From 1048f7a5340b4e8c88153a5d38083a5a8835b45b Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Mon, 1 Feb 2021 23:12:58 +0100 Subject: [PATCH 01/38] Assigning Joystick to BBC Keyboard - Assigning host joystick axes and buttons to BBC keyboard (up to 2 joysticks or gamepads separately). - New human readable file format for joystick mapping (see Documents/JoystickMap.txt) - Option to automatically load image-specific joystick mapping file (same as disk name, but .jmap extension) - Fixed displaying keys assigned to Shift in User Mapping dialog - Added "Unassign" to keyboard and joystick mapping dialog - Joystick mapping table is separate from key mapping table - Mapping joysticks is done separately from keyboard (but using the same dialog code) - Highlight assigned keys in joystick mapping dialog --- Documents/JoystickMap.txt | 70 ++++++ Help/keyboard.html | 90 +++++++- Help/menus.html | 45 ++++ Src/BeebEm.rc | 15 +- Src/SelectKeyDialog.cpp | 126 ++++++++++- Src/SelectKeyDialog.h | 7 +- Src/beebemrc.h | 13 +- Src/beebwin.cpp | 387 +++++++++++++++++++++++++++++--- Src/beebwin.h | 72 +++++- Src/beebwinio.cpp | 433 ++++++++++++++++++++++++++++++----- Src/beebwinprefs.cpp | 30 ++- Src/filedialog.cpp | 3 +- Src/filedialog.h | 5 + Src/userkybd.cpp | 458 ++++++++++++++++++++++++++++---------- Src/userkybd.h | 14 +- 15 files changed, 1550 insertions(+), 218 deletions(-) create mode 100644 Documents/JoystickMap.txt diff --git a/Documents/JoystickMap.txt b/Documents/JoystickMap.txt new file mode 100644 index 00000000..2958b6a0 --- /dev/null +++ b/Documents/JoystickMap.txt @@ -0,0 +1,70 @@ +*** BeebEm Joystick Map *** + +# Joystick map file begins with special marker line as above. +# All empty lines and lines starting with '#' are ignored. + +# Remaining lines must consist of two or three tokens separated with +# whitespaces. +# All names are case insensitive except for the marker in the first line. + +# Two-token lines are: +# +# Three-token lines are: +# + +# These two lines define the same mapping: +# Joy1Up A +# Joy1Up A SH+A + +# BBC Key names prefixed with 'SH+' indicate shifted key press. +# In the two-token lines, the 'SH+' prefix is allowed, but ignored. + +# Special BBC key names are: +# LEFT, RIGHT, UP, DOWN, BREAK, COPY, DELETE, CAPS-LOCK, TAB, CTRL +# SPACE, RETURN, ESCACE, SHIFT, SHIFT-LOCK +# Allowed aliases: +# DEL, CAPS, CAPSLOCK, CONTROL, ESC, SHIFTLOCK + +# All other BBC keys are named by their unshifted character (i.e. 'SH+[' not '{') +# Function keys are named, as one would expect, 'F0' to 'F9' + +# Joystick input have form Joy[X][Act] where [X] is joystick number and +# [Act] is action name. Axis and button assignments differ between +# joystick or gamepad models. + +# Xbox 360 controller has following assignments +# (with JOYINFOEX fields in the middle column): +# Up, Down, Left, Right - dwY, dwX - Left Analog Up, Down, Left, Right +# Axis3+,Axis3- - dwZ - Left Trigger, Right Trigger +# Axis4-,Axis4+,Axis5-,Axis5+ - dwR, dwU - Right Analog Up, Down, Left, Right +# Axis7-,Axis7+,Axis8-,Axis8+ - dwPOV - Hat Up, Down, Left, Right +# Btn1-4 - A, B, X, Y +# Btn5,6 - Left Shoulder, Right Shoulder +# Btn7,8 - Select, Start +# Btn9,10 - Left Thumbstick, Right Thumbstick + +# Other common layout: +# Up, Down, Left, Right - dwY, dwX - Left Analog Up, Down, Left, Right +# Axis4-,Axis4+,Axis3-,Axis3+ - dwR, dwZ - Right Analog Up, Down, Left, Right +# Axis7-,Axis7+,Axis8-,Axis8+ - dwPOV - Hat Up, Down, Left, Right +# Btn1-4 - Y, B, A, X +# Btn5,6 - Left Shoulder, Right Shoulder +# Btn7,8 - Left Trigger, Right Trigger +# Btn9,10 - Select, Start +# Btn11,12 - Left Thumbstick, Right Thumbstick +# Btn12 - Home + +# Sample mapping for Chuckie Egg + +Joy1Up A +Joy1Down Z +Joy1Left , +Joy1Right . +Joy1Axis7- A +Joy1Axis7+ Z +Joy1Axis8- , +Joy1Axis8+ . +Joy1Btn1 SPACE +Joy1Btn6 RETURN +Joy1Btn7 1 +Joy1Btn8 S diff --git a/Help/keyboard.html b/Help/keyboard.html index 3112d690..d27f57ec 100644 --- a/Help/keyboard.html +++ b/Help/keyboard.html @@ -237,7 +237,95 @@

Custom Key Mappings

key mapping file that gets loaded when BeebEm starts up. - +

Mapping PC Joystick to BBC Keys

+ +

To enable translating PC joystick actions to BBC keys, use menu item + "Options -> Joystick To Keyboard -> Enable Joystick To Keyboard". + BeemEm supports mapping up to two joysticks or gamepads to BBC keys, + with each joystick actions (button presses or stick movements) + mapped separately.

+ +

Creating mapping of PC joystick movements and buttons to BBC keys is + similar to creating custom key mappings:

+ +
    +
  1. If you are in full screen mode then switch back to Windowed mode.
  2. + +
  3. Select menu item "Options -> Joystick To Keybord -> Define Joystick Mapping". + A graphic showing the BBC keyboard layout will appear within the BeebEm + interface. BBC keys which already have assigned joystick actions will be + highlighted with light blue colour.
  4. + +
  5. Use your mouse pointer to click once on the BBC key that you are + attempting to map to your joystick or gamepad.
  6. + +
  7. Press the joystick button or move the joystick in the direction that you want + to map to the unshifted BBC key press, or click "OK" to skip that part. + If you want to map shifted joystick action to unshifted BBC key press, enable + the "Shift" checkbox before moving the joystick.
  8. + +
  9. Press the joystick button or move the joystick in the direction that you want + to map to the shifted BBC key press, or click "OK" to skip that part. + If you want to map unshifted joystick action to shifted BBC key press, disable + the "Shift" checkbox before moving the joystick.
  10. + +
  11. Repeat from step 3 for other keys you want to map.
  12. + +
  13. Save your mapping using menu item + "Options -> Joystick To Keybord -> Save Joystick Mapping...". + You can save your mapping as the default user joystick mapping file + (DefaultUser.jmap) or save to a different file. If you are creating + a game-specific joystick mapping, you can save the mapping in the same + directory and with the same name as your game disk image, with extension + changed to ".jmap". This will make BeebEm automatically use the joystick + mapping for that disk image, if that is enabled with menu item + "Options -> Joystick To Keybord -> Autoload Joystick Mapping".
  14. +
+ +

As with PC keys, you can map joystick actions to BBC keys in shifted and + unshifted state separately. The most common scenario is to assign a + joystick action to the same BBC key in both shifted and unshifted state. + To do that, just click on the BBC key that you want to map, and move + joystick or press button twice - once for unshifted and second time for + shifted state.

+ +

Additionally, you can assign one or more joystick buttons to the SHIFT key. + This can be used to assign one joystick button as a modifier for + other joystick actions, which can be useful for games that have a lot of + keyboard contols.

+ +

PC joystick to BBC keyboard mapping is independent from enabling PC joystick + acting as BBC joystick. If you enable both, primary PC joystick axes + (primary stick up, down, left and right) and first two buttons are mapped + to BBC joystick. You can map other axes and buttons to BBC keys. You can + map those axes and buttons which are acting as BBC joystick to BBC + keys as well. In that case, the PC joystick action will be seen as both BBC + joystick action and BBC key press.

+ +

To remove mapping from previously mapped joystick action, click on the + 'Unassign' button. It will display small window and wait for the + joystick action. Press the joystick button or move the joystick in the + direction that you want to unmap. The same window will be displayed again, + giving you the opportunity to unmap the joystick action in both unshifted + and shifted state at one go. You can press the "OK" button to skip the + second unassignment.

+ +

At start up BeebEm loads DefaultUser.jmap from the User Data Folder, if such + file was created by user. If this file is not present, all joystick actions + start unassigned.

+ +

Menu item "Options -> Joystick To Keybord -> Autoload Joystick Mapping" + toggles Autoload Joystick Mapping option. If this option is enabled, BeebEm + will automatically look for joystick mapping file based on name of image + file started from "File -> Run Disc.." or "File -> Load Tape..." menu item, + or from command-line. The sought mapping file has the same name and + directory as loaded image, but extension changed to ".jmap". If no such + file is found, joystick mapping is reset to default - either empty, or that + from "DefaultUser.jmap" file. If the Autoload Joystick Mapping option is + enabled, the "Save Joystick Mapping..." dialog will automatically suggest + correct file name for currently loaded image file.

+ + diff --git a/Help/menus.html b/Help/menus.html index 1561536e..9211b270 100644 --- a/Help/menus.html +++ b/Help/menus.html @@ -732,6 +732,51 @@

Options Menu

+ + Enable Joystick To Keyboard + Switch on or off mapping PC joystick to BBC keyboard. + See the Keyboard Mappings section. + + + + + Autoload Joystick Mapping + Switch on or off automatically loading joystick mapping + files for disk or tape images. + + + + + Define Joystick Mapping + Allows you to configure joystick to keyboard mapping. + See the Keyboard Mappings section. + + + + + Reset Joystick Mapping + Clears joystick to keyboard mapping table. + + + + Load Joystick Mapping + Loads joystick to keyboard mapping from a file. + + + + + Save Joystick Mapping + Saves joystick to keyboard mapping to a file. + + + + + Reinitialize Joystick + Retries joystick initialization. You can use + this if you connected your USB gamepad after starting BeebEm. + + + Freeze when inactive When selected BeebEm will freeze when you switch diff --git a/Src/BeebEm.rc b/Src/BeebEm.rc index 71871e84..5866e6ef 100644 --- a/Src/BeebEm.rc +++ b/Src/BeebEm.rc @@ -108,6 +108,7 @@ BEGIN CONTROL "_ £",IDK_UNDERSCORE,"Button",BS_OWNERDRAW | WS_TABSTOP,269,33,17,14 CONTROL "^ ~",IDK_CARET,"Button",BS_OWNERDRAW | WS_TABSTOP,243,19,17,14 CONTROL "SFT LK",IDK_SHIFT_LOCK,"Button",BS_OWNERDRAW | WS_TABSTOP,9,62,29,14 + CONTROL "Unassign",IDK_UNASSIGN,"Button",BS_OWNERDRAW | WS_TABSTOP,9,101,36,14 END IDD_KEYBOARD_LINKS DIALOG 0, 0, 160, 66 @@ -165,7 +166,7 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPT CAPTION "Press key for unshifted press..." FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - LTEXT "Assigned to PC key(s):",IDC_STATIC,7,7,74,8 + LTEXT "Assigned to PC key(s):",IDC_ASSIGNED_KEYS_LBL,7,7,74,8 LTEXT "Static",IDC_ASSIGNED_KEYS,15,21,127,8 CONTROL "Shift",IDC_SHIFT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,40,31,10 PUSHBUTTON "OK",IDOK,92,39,50,14 @@ -711,6 +712,18 @@ BEGIN MENUITEM "&Joystick", IDM_JOYSTICK MENUITEM "&Analogue Mousestick", IDM_ANALOGUE_MOUSESTICK MENUITEM "&Digital Mousestick", IDM_DIGITAL_MOUSESTICK + POPUP "Joystick To Keyboard" + BEGIN + MENUITEM "Enable Joystick To Keyboard", IDM_JOYSTICK_TO_KEYS + MENUITEM "Autoload Joystick Mapping", IDM_AUTOLOADJOYMAP + MENUITEM SEPARATOR + MENUITEM "Define Joystick Mapping", IDM_DEFINEJOYMAP + MENUITEM "Reset Joystick Mapping", IDM_RESETJOYMAP + MENUITEM "Load Joystick Mapping...", IDM_LOADJOYMAP + MENUITEM "Save Joystick Mapping...", IDM_SAVEJOYMAP + END + MENUITEM "Reinitialize Joystick", IDM_INIT_JOYSTICK + MENUITEM SEPARATOR MENUITEM "&Freeze when inactive", IDM_FREEZEINACTIVE MENUITEM "&Hide Cursor", IDM_HIDECURSOR MENUITEM SEPARATOR diff --git a/Src/SelectKeyDialog.cpp b/Src/SelectKeyDialog.cpp index a6cd02a4..c43d0642 100644 --- a/Src/SelectKeyDialog.cpp +++ b/Src/SelectKeyDialog.cpp @@ -31,6 +31,7 @@ Boston, MA 02110-1301, USA. /****************************************************************************/ static bool IsDlgItemChecked(HWND hDlg, int nIDDlgItem); +static void DlgItemCheck(HWND hDlg, int nIDDlgItem, bool checked); SelectKeyDialog* selectKeyDialog; @@ -40,14 +41,17 @@ SelectKeyDialog::SelectKeyDialog( HINSTANCE hInstance, HWND hwndParent, const std::string& Title, - const std::string& SelectedKey) : + const std::string& SelectedKey, + bool doingShifted, + bool doingJoystick) : m_hInstance(hInstance), m_hwnd(nullptr), m_hwndParent(hwndParent), m_Title(Title), m_SelectedKey(SelectedKey), m_Key(-1), - m_Shift(false) + m_Shift(doingShifted), + m_Joystick(doingJoystick) { } @@ -101,16 +105,30 @@ INT_PTR SelectKeyDialog::DlgProc( SetWindowText(m_hwnd, m_Title.c_str()); SetDlgItemText(m_hwnd, IDC_ASSIGNED_KEYS, m_SelectedKey.c_str()); + + // If the selected keys is empty (as opposed to "Not assigned"), we are currently unassigning. + // Hide the "Assigned to:" label + if (m_SelectedKey.empty()) + SetDlgItemText(m_hwnd, IDC_ASSIGNED_KEYS_LBL, ""); + else if (m_Joystick) + SetDlgItemText(m_hwnd, IDC_ASSIGNED_KEYS_LBL, "Assigned to:"); + + // If doing shifted key, start with the Shift checkbox checked because that's most likely + // what the user wants + DlgItemCheck(m_hwnd, IDC_SHIFT, m_Shift); + return TRUE; case WM_ACTIVATE: if (LOWORD(wParam) == WA_INACTIVE) { hCurrentDialog = nullptr; + mainWin->m_JoystickTarget = nullptr; } else { hCurrentDialog = m_hwnd; + mainWin->m_JoystickTarget = m_hwnd; hCurrentAccelTable = nullptr; } break; @@ -167,11 +185,16 @@ bool SelectKeyDialog::HandleMessage(const MSG& msg) { if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) { - m_Key = (int)msg.wParam; - m_Shift = IsDlgItemChecked(m_hwnd, IDC_SHIFT); + int key = (int)msg.wParam; + if (!m_Joystick && key < 256 + || m_Joystick && key >= BEEB_VKEY_JOY_START && key < BEEB_VKEY_JOY_END) + { + m_Key = key; + m_Shift = IsDlgItemChecked(m_hwnd, IDC_SHIFT); + Close(IDOK); + return true; + } - Close(IDOK); - return true; } return false; @@ -186,6 +209,13 @@ int SelectKeyDialog::Key() const /****************************************************************************/ +bool SelectKeyDialog::Shift() const +{ + return m_Shift; +} + +/****************************************************************************/ + static bool IsDlgItemChecked(HWND hDlg, int nIDDlgItem) { return SendDlgItemMessage(hDlg, nIDDlgItem, BM_GETCHECK, 0, 0) == BST_CHECKED; @@ -193,15 +223,68 @@ static bool IsDlgItemChecked(HWND hDlg, int nIDDlgItem) /****************************************************************************/ +static void DlgItemCheck(HWND hDlg, int nIDDlgItem, bool checked) +{ + SendDlgItemMessage(hDlg, nIDDlgItem, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0); +} + +/****************************************************************************/ + LPCSTR SelectKeyDialog::KeyName(int Key) { static CHAR Character[2]; // Used to return single characters. + if (Key >= BEEB_VKEY_JOY_START && Key < BEEB_VKEY_JOY_END) + { + static CHAR Name[16]; // Buffer for joystick button or axis name + Key -= BEEB_VKEY_JOY_START; + if (Key >= BEEB_VKEY_JOY2_AXES - BEEB_VKEY_JOY1_AXES) + { + strcpy(Name, "Joy2"); + Key -= BEEB_VKEY_JOY2_AXES - BEEB_VKEY_JOY1_AXES; + } + else + { + strcpy(Name, "Joy1"); + } + + if (Key < BEEB_VKEY_JOY1_BTN1 - BEEB_VKEY_JOY1_AXES) + { + if (Key == JOYSTICK_AXIS_UP) + { + strcat(Name, "Up"); + } + else if (Key == JOYSTICK_AXIS_DOWN) + { + strcat(Name, "Down"); + } + else if (Key == JOYSTICK_AXIS_LEFT) + { + strcat(Name, "Left"); + } + else if (Key == JOYSTICK_AXIS_RIGHT) + { + strcat(Name, "Right"); + } + else + { + sprintf(Name + strlen(Name), "Axis%d", (Key / 2) + 1); + strcat(Name, (Key & 1) ? "+" : "-"); + } + } + else + { + sprintf(Name + strlen(Name), "Btn%d", Key - (BEEB_VKEY_JOY1_BTN1 - BEEB_VKEY_JOY1_AXES) + 1); + } + return Name; + } + switch (Key) { case 8: return "Backspace"; case 9: return "Tab"; case 13: return "Enter"; + case 16: return "Shift"; case 17: return "Ctrl"; case 18: return "Alt"; case 19: return "Break"; @@ -267,3 +350,34 @@ LPCSTR SelectKeyDialog::KeyName(int Key) return Character; } } + +static +std::string toupper(const std::string& src) +{ + std::string result = src; + for (auto& c : result) + c = toupper(c); + return result; +} + +int SelectKeyDialog::JoyVKeyByName(const char* Name) +{ + + using vkeyMapType = std::map; + + // Construct map on first use by lambda + static vkeyMapType nameToVKeyMap = []() { + vkeyMapType keyMap{}; + + for (int vkey = BEEB_VKEY_JOY_START; vkey < BEEB_VKEY_JOY_END; ++vkey) + keyMap[toupper(KeyName(vkey))] = vkey; + + return keyMap; + }(); + + auto iter = nameToVKeyMap.find(toupper(Name)); + if (iter == nameToVKeyMap.end()) + return -1; + + return iter->second; +} diff --git a/Src/SelectKeyDialog.h b/Src/SelectKeyDialog.h index a6fe7abb..faae4f5e 100644 --- a/Src/SelectKeyDialog.h +++ b/Src/SelectKeyDialog.h @@ -30,7 +30,9 @@ class SelectKeyDialog HINSTANCE hInstance, HWND hwndParent, const std::string& Title, - const std::string& SelectedKey + const std::string& SelectedKey, + bool doingShifted = false, + bool doingJoystick = false ); bool Open(); @@ -39,8 +41,10 @@ class SelectKeyDialog bool HandleMessage(const MSG& msg); int Key() const; + bool Shift() const; static LPCSTR KeyName(int Key); + static int JoyVKeyByName(const char* Name); private: static INT_PTR CALLBACK sDlgProc( @@ -65,6 +69,7 @@ class SelectKeyDialog std::string m_SelectedKey; int m_Key; bool m_Shift; + bool m_Joystick; }; extern SelectKeyDialog* selectKeyDialog; diff --git a/Src/beebemrc.h b/Src/beebemrc.h index ca0c45b6..f076c0be 100644 --- a/Src/beebemrc.h +++ b/Src/beebemrc.h @@ -97,6 +97,7 @@ Boston, MA 02110-1301, USA. #define IDK_BACKSLASH 81 #define IDK_UNDERSCORE 82 #define IDK_CARET 83 +#define IDK_UNASSIGN 84 #define IDR_MENU 101 #define IDI_BEEBEM 102 #define IDD_USERKYBRD 103 @@ -186,6 +187,7 @@ Boston, MA 02110-1301, USA. #define IDC_SHIFT 1088 #define IDC_DEBUGTELETEXTBRK 1089 #define IDC_ASSIGNED_KEYS 1090 +#define IDC_ASSIGNED_KEYS_LBL 1091 #define IDM_ABOUT 40001 #define IDM_DISC 40002 #define IDM_LOADDISC0 40002 @@ -433,17 +435,24 @@ Boston, MA 02110-1301, USA. #define ID_VIEW_DD_2560X1440 40294 #define ID_VIEW_DD_3840X2160 40295 #define IDM_EMUPAUSED 40296 +#define IDM_JOYSTICK_TO_KEYS 40297 +#define IDM_DEFINEJOYMAP 40298 +#define IDM_LOADJOYMAP 40299 +#define IDM_SAVEJOYMAP 40300 +#define IDM_AUTOLOADJOYMAP 40301 +#define IDM_RESETJOYMAP 40302 +#define IDM_INIT_JOYSTICK 40303 #define IDM_CAPTUREMOUSE 40318 #define IDC_STATIC -1 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 118 #define _APS_NEXT_COMMAND_VALUE 40319 -#define _APS_NEXT_CONTROL_VALUE 1090 +#define _APS_NEXT_CONTROL_VALUE 1092 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index be81f3f2..42f4f64d 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -131,6 +131,7 @@ static KeyMap logicalMapping; /* Currently selected translation table */ static KeyMap *transTable = &defaultMapping; +static JoyMap *joystickTable = &JoystickMap; /****************************************************************************/ BeebWin::BeebWin() @@ -159,6 +160,8 @@ BeebWin::BeebWin() memset(&logicalMapping, 0, sizeof(KeyMap)); memset(&UserKeymap, 0, sizeof(KeyMap)); memset(m_UserKeyMapPath, 0, sizeof(m_UserKeyMapPath)); + memset(&JoystickMap, 0, sizeof(JoyMap)); + memset(m_JoystickMapPath, 0, sizeof(m_JoystickMapPath)); m_hBitmap = m_hOldObj = m_hDCBitmap = NULL; m_screen = m_screen_blur = NULL; m_ScreenRefreshCount = 0; @@ -203,6 +206,12 @@ BeebWin::BeebWin() m_DXResetPending = false; m_JoystickCaptured = false; + m_Joystick2Captured = false; + m_JoystickTarget = NULL; + m_Joystick1PrevAxes = 0; + m_Joystick1PrevBtns = 0; + m_Joystick2PrevAxes = 0; + m_Joystick2PrevBtns = 0; m_customip[0] = 0; m_customport = 0; m_isFullScreen = false; @@ -317,6 +326,9 @@ bool BeebWin::Initialise() ReadROMFile(RomFile, RomConfig); ApplyPrefs(); + if (m_AutoloadJoystickMap) + CheckForJoystickMap(m_CommandLineFileName1); + if (m_DebugScript[0] != '\0') { DebugOpenDialog(hInst, m_hWnd); @@ -382,6 +394,9 @@ void BeebWin::ApplyPrefs() GetDataPath(m_UserDataPath, keymap); ReadKeyMap(keymap, &defaultMapping); + ResetJoyMapToDefaultUser(); + + InitMenu(); ShowMenu(true); @@ -399,8 +414,8 @@ void BeebWin::ApplyPrefs() PrinterDisable(); /* Joysticks can only be initialised after the window is created (needs hwnd) */ - if (m_MenuIdSticks == IDM_JOYSTICK) - InitJoystick(); + InitJoystick(); + MaybeEnableInitJoystick(); LoadFDC(NULL, true); RTCInit(); @@ -1088,6 +1103,8 @@ void BeebWin::InitMenu(void) CheckMenuItem(IDM_DIGITAL_MOUSESTICK, false); if (m_MenuIdSticks != 0) CheckMenuItem(m_MenuIdSticks, true); + CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); + CheckMenuItem(IDM_AUTOLOADJOYMAP, m_AutoloadJoystickMap); CheckMenuItem(IDM_FREEZEINACTIVE, m_FreezeWhenInactive); CheckMenuItem(IDM_HIDECURSOR, m_HideCursor); CheckMenuItem(IDM_DEFAULTKYBDMAPPING, false); @@ -1224,36 +1241,65 @@ void BeebWin::SetRomMenu(void) } /****************************************************************************/ + +void BeebWin::MaybeEnableInitJoystick(void) +{ + bool enableIt = ((m_MenuIdSticks == IDM_JOYSTICK) && !m_JoystickCaptured + || m_JoystickToKeys && (!m_JoystickCaptured || !m_Joystick2Captured)); + EnableMenuItem(IDM_INIT_JOYSTICK, enableIt); +} + +/****************************************************************************/ + void BeebWin::InitJoystick(void) { MMRESULT mmresult = JOYERR_NOERROR; + MMRESULT mmresult2; - if (!m_JoystickCaptured) + if ((m_MenuIdSticks == IDM_JOYSTICK || m_JoystickToKeys) && !m_JoystickCaptured) { - /* Get joystick updates 10 times a second */ - mmresult = joySetCapture(m_hWnd, JOYSTICKID1, 100, FALSE); + /* Get joystick updates 20 times a second */ + mmresult = joySetCapture(m_hWnd, JOYSTICKID1, 50, FALSE); if (mmresult == JOYERR_NOERROR) mmresult = joyGetDevCaps(JOYSTICKID1, &m_JoystickCaps, sizeof(JOYCAPS)); if (mmresult == JOYERR_NOERROR) m_JoystickCaptured = true; } + if (m_JoystickToKeys && !m_Joystick2Captured) + { + /* Get joystick updates 20 times a second */ + mmresult2 = joySetCapture(m_hWnd, JOYSTICKID2, 50, FALSE); + if (mmresult2 == JOYERR_NOERROR) + mmresult2 = joyGetDevCaps(JOYSTICKID2, &m_Joystick2Caps, sizeof(JOYCAPS)); + if (mmresult2 == JOYERR_NOERROR) + m_Joystick2Captured = true; + } + if (mmresult == JOYERR_NOERROR) { - AtoDEnable(); + if (m_MenuIdSticks == IDM_JOYSTICK) + AtoDEnable(); } else if (mmresult == JOYERR_UNPLUGGED) { MessageBox(m_hWnd, "Joystick is not plugged in", - WindowTitle, MB_OK|MB_ICONERROR); + WindowTitle, MB_OK|MB_ICONWARNING); } else { MessageBox(m_hWnd, "Failed to initialise the joystick", - WindowTitle, MB_OK|MB_ICONERROR); + WindowTitle, MB_OK|MB_ICONWARNING); } } +/****************************************************************************/ +void BeebWin::SetJoystickButton(bool button) +{ + if (m_MenuIdSticks == IDM_JOYSTICK) + JoystickButton[0] = button; +} + /****************************************************************************/ void BeebWin::ScaleJoystick(unsigned int x, unsigned int y) { @@ -1267,13 +1313,179 @@ void BeebWin::ScaleJoystick(unsigned int x, unsigned int y) } } +/****************************************************************************/ +unsigned int BeebWin::GetJoystickAxes(JOYCAPS& caps, unsigned int deadband, JOYINFOEX& joyInfoEx) +{ + unsigned int axes = 0; + + int tx = (int)((double)(joyInfoEx.dwXpos - caps.wXmin) * 65535.0 / + (double)(caps.wXmax - caps.wXmin)); + int ty = (int)((double)(joyInfoEx.dwYpos - caps.wYmin) * 65535.0 / + (double)(caps.wYmax - caps.wYmin)); + int tz = (int)((double)(joyInfoEx.dwZpos - caps.wZmin) * 65535.0 / + (double)(caps.wZmax - caps.wZmin)); + int tr = (int)((double)(joyInfoEx.dwRpos - caps.wRmin) * 65535.0 / + (double)(caps.wRmax - caps.wRmin)); + int tu = (int)((double)(joyInfoEx.dwUpos - caps.wUmin) * 65535.0 / + (double)(caps.wUmax - caps.wUmin)); + int tv = (int)((double)(joyInfoEx.dwVpos - caps.wVmin) * 65535.0 / + (double)(caps.wVmax - caps.wVmin)); + + if (ty < 32768 - deadband) + axes |= (1 << JOYSTICK_AXIS_UP); + else if (ty > 32768 + deadband) + axes |= (1 << JOYSTICK_AXIS_DOWN); + + if (tx < 32768 - deadband) + axes |= (1 << JOYSTICK_AXIS_LEFT); + else if (tx > 32768 + deadband) + axes |= (1 << JOYSTICK_AXIS_RIGHT); + + if (tz < 32768 - deadband) + axes |= (1 << JOYSTICK_AXIS_Z_N); + else if (tz > 32768 + deadband) + axes |= (1 << JOYSTICK_AXIS_Z_P); + + if (tr < 32768 - deadband) + axes |= (1 << JOYSTICK_AXIS_R_N); + else if (tr > 32768 + deadband) + axes |= (1 << JOYSTICK_AXIS_R_P); + + if (tu < 32768 - deadband) + axes |= (1 << JOYSTICK_AXIS_U_N); + else if (tu > 32768 + deadband) + axes |= (1 << JOYSTICK_AXIS_U_P); + + if (tv < 32768 - deadband) + axes |= (1 << JOYSTICK_AXIS_V_N); + else if (tv > 32768 + deadband) + axes |= (1 << JOYSTICK_AXIS_V_P); + + if (joyInfoEx.dwPOV != JOY_POVCENTERED) + { + if (joyInfoEx.dwPOV >= 29250 || joyInfoEx.dwPOV < 6750) + axes |= (1 << JOYSTICK_AXIS_HAT_UP); + if (joyInfoEx.dwPOV >= 2250 && joyInfoEx.dwPOV < 15750) + axes |= (1 << JOYSTICK_AXIS_HAT_RIGHT); + if (joyInfoEx.dwPOV >= 11250 && joyInfoEx.dwPOV < 24750) + axes |= (1 << JOYSTICK_AXIS_HAT_DOWN); + if (joyInfoEx.dwPOV >= 20250 && joyInfoEx.dwPOV < 33750) + axes |= (1 << JOYSTICK_AXIS_HAT_LEFT); + } + + return axes; +} + +/****************************************************************************/ +void BeebWin::TranslateOrSendKey(int vkey, bool keyUp) +{ + int row, col; + if (m_JoystickTarget == NULL) + { + TranslateKey(vkey, keyUp, row, col); + } + else if (!keyUp) + { + // Keyboard input dialog is visible - translate button down to key down message and send to dialog + PostMessage(m_JoystickTarget, WM_KEYDOWN, vkey, 0); + } +} + +/****************************************************************************/ +void BeebWin::TranslateJoystickMove(int joyId, JOYINFOEX& joyInfoEx) +{ + const int axNum = JOYSTICK_AXES_COUNT; + + JOYCAPS& caps = (joyId == 1) ? m_Joystick2Caps : m_JoystickCaps; + unsigned int deadband = (joyId == 1) ? m_Joystick2Deadband : m_Joystick1Deadband; + int& prevAxes = (joyId == 1) ? m_Joystick2PrevAxes : m_Joystick1PrevAxes; + int vkeys = (joyId == 1) ? BEEB_VKEY_JOY2_AXES : BEEB_VKEY_JOY1_AXES; + + unsigned int axes = mainWin->GetJoystickAxes(caps, deadband, joyInfoEx); + + if (axes != prevAxes) + { + for (int axId = 0; axId < axNum; ++axId) + { + if ((axes & ~prevAxes) & (1 << axId)) + { + TranslateOrSendKey(vkeys + axId, false); + } + else if ((~axes & prevAxes) & (1 << axId)) + { + TranslateOrSendKey(vkeys + axId, true); + } + } + prevAxes = axes; + } +} + +/****************************************************************************/ +void BeebWin::TranslateJoystickButtons(int joyId, unsigned int buttons) +{ + const int btnNum = 32; + + int& prevBtns = (joyId == 1) ? m_Joystick2PrevBtns : m_Joystick1PrevBtns; + int vkeys = (joyId == 1) ? BEEB_VKEY_JOY2_BTN1 : BEEB_VKEY_JOY1_BTN1; + + if (buttons != prevBtns) + { + for (int btnId = 0; btnId < btnNum; ++btnId) + { + if ((buttons & ~prevBtns) & (1 << btnId)) + { + TranslateOrSendKey(vkeys + btnId, false); + } + else if ((~buttons & prevBtns) & (1 << btnId)) + { + TranslateOrSendKey(vkeys + btnId, true); + } + } + prevBtns = buttons; + } +} + +/****************************************************************************/ +void BeebWin::TranslateJoystick(int joyId) +{ + if (mainWin->m_JoystickToKeys) + { + JOYINFOEX joyInfoEx; + joyInfoEx.dwSize = sizeof(joyInfoEx); + joyInfoEx.dwFlags = JOY_RETURNALL | JOY_RETURNPOVCTS; + if (!joyGetPosEx(joyId, &joyInfoEx)) + { + mainWin->TranslateJoystickMove(joyId, joyInfoEx); + mainWin->TranslateJoystickButtons(joyId, joyInfoEx.dwButtons); + } + } +} + /****************************************************************************/ void BeebWin::ResetJoystick(void) { // joySetCapture() fails after a joyReleaseCapture() call (not sure why) // so leave joystick captured. // joyReleaseCapture(JOYSTICKID1); - AtoDDisable(); + + // It still doesn't work + // Don't really release joystick if mapping is enabled + //if (!m_JoystickToKeyboard) + //{ + // if (m_JoystickCaptured) + // { + // if (joyReleaseCapture(JOYSTICKID1) == JOYERR_NOERROR) + // m_JoystickCaptured = false; + // } + // if (m_Joystick2Captured) + // { + // if (joyReleaseCapture(JOYSTICKID2) == JOYERR_NOERROR) + // m_Joystick2Captured = false; + // } + //} + + // Don't disable AtoD here, we may want to reset joystick, but keep + // mousestick active } /****************************************************************************/ @@ -1662,11 +1874,16 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle case MM_JOY1MOVE: mainWin->ScaleJoystick(LOWORD(lParam), HIWORD(lParam)); + mainWin->TranslateJoystick(0); break; case MM_JOY1BUTTONDOWN: case MM_JOY1BUTTONUP: - JoystickButton[0] = (uParam & (JOY_BUTTON1 | JOY_BUTTON2)) != 0; + mainWin->SetJoystickButton(((UINT)uParam & (JOY_BUTTON1 | JOY_BUTTON2)) != 0); + break; + + case MM_JOY2MOVE: + mainWin->TranslateJoystick(1); break; case WM_INPUT: @@ -1824,11 +2041,20 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle /****************************************************************************/ int BeebWin::TranslateKey(int vkey, bool keyUp, int &row, int &col) { - if (vkey < 0 || vkey > 255) + KeyPair* table = *transTable; + int tableIdx = vkey; + + if (vkey < 0 || vkey >= BEEB_VKEY_COUNT) return -9; + if (vkey >= BEEB_VKEY_JOY_START) + { + table = *joystickTable; + tableIdx -= BEEB_VKEY_JOY_START; + } + // Key track of shift state - if ((*transTable)[vkey][0].row == 0 && (*transTable)[vkey][0].col == 0) + if (table[tableIdx][0].row == 0 && table[tableIdx][0].col == 0) { m_ShiftPressed = !keyUp; } @@ -1854,9 +2080,9 @@ int BeebWin::TranslateKey(int vkey, bool keyUp, int &row, int &col) } else // New key press - convert to beeb row + col { - row = (*transTable)[vkey][static_cast(m_ShiftPressed)].row; - col = (*transTable)[vkey][static_cast(m_ShiftPressed)].col; - bool needShift = (*transTable)[vkey][static_cast(m_ShiftPressed)].shift; + row = table[tableIdx][static_cast(m_ShiftPressed)].row; + col = table[tableIdx][static_cast(m_ShiftPressed)].col; + bool needShift = table[tableIdx][static_cast(m_ShiftPressed)].shift; if (m_KeyMapAS) { @@ -1878,8 +2104,8 @@ int BeebWin::TranslateKey(int vkey, bool keyUp, int &row, int &col) // Map F1-F10 to f0-f9 if (vkey >= 113 && vkey <= 121) { - row = (*transTable)[vkey - 1][0].row; - col = (*transTable)[vkey - 1][0].col; + row = table[tableIdx - 1][0].row; + col = table[tableIdx - 1][0].col; } else if (vkey == 112) { @@ -3355,10 +3581,7 @@ void BeebWin::HandleCommand(int MenuId) { ResetJoystick(); } - else /* mousestick */ - { - AtoDDisable(); - } + AtoDDisable(); } if (MenuId == m_MenuIdSticks) @@ -3385,6 +3608,31 @@ void BeebWin::HandleCommand(int MenuId) else m_MenuIdSticks = 0; } + MaybeEnableInitJoystick(); + break; + + case IDM_INIT_JOYSTICK: + InitJoystick(); + MaybeEnableInitJoystick(); + break; + + case IDM_JOYSTICK_TO_KEYS: + m_JoystickToKeys = !m_JoystickToKeys; + if (m_JoystickToKeys) + { + InitJoystick(); + } + else if (m_MenuIdSticks != IDM_JOYSTICK) + { + ResetJoystick(); + } + CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); + MaybeEnableInitJoystick(); + break; + + case IDM_AUTOLOADJOYMAP: + m_AutoloadJoystickMap = !m_AutoloadJoystickMap; + CheckMenuItem(IDM_AUTOLOADJOYMAP, m_AutoloadJoystickMap); break; case IDM_FREEZEINACTIVE: @@ -3397,8 +3645,8 @@ void BeebWin::HandleCommand(int MenuId) CheckMenuItem(IDM_HIDECURSOR, m_HideCursor); break; - case IDM_DEFINEKEYMAP: - OpenUserKeyboardDialog(); + case IDM_DEFINEJOYMAP: + OpenJoystickMapDialog(); StayMuted = true; break; @@ -3410,6 +3658,23 @@ void BeebWin::HandleCommand(int MenuId) SaveUserKeyMap(); break; + case IDM_LOADJOYMAP: + LoadJoystickMap(); + break; + + case IDM_SAVEJOYMAP: + SaveJoystickMap(); + break; + + case IDM_RESETJOYMAP: + ResetJoystickMap(); + break; + + case IDM_DEFINEKEYMAP: + OpenUserKeyboardDialog(); + StayMuted = true; + break; + case IDM_USERKYBDMAPPING: case IDM_DEFAULTKYBDMAPPING: case IDM_LOGICALKYBDMAPPING: @@ -4195,12 +4460,37 @@ void BeebWin::OpenUserKeyboardDialog() m_WasPaused = mainWin->IsPaused(); + m_J2KWasEnabled = m_JoystickToKeys; + + if (!m_WasPaused) + { + TogglePause(); + } + + UserKeyboardDialog(m_hWnd, false); +} + +void BeebWin::OpenJoystickMapDialog() +{ + // Pause the emulator if not already paused. + + m_WasPaused = mainWin->IsPaused(); + if (!m_WasPaused) { TogglePause(); } - UserKeyboardDialog(m_hWnd); + // Enable mapping to capture keys in dialog + m_J2KWasEnabled = m_JoystickToKeys; + + if (!m_J2KWasEnabled) + { + m_JoystickToKeys = true; + InitJoystick(); + } + + UserKeyboardDialog(m_hWnd, true); } void BeebWin::UserKeyboardDialogClosed() @@ -4212,6 +4502,16 @@ void BeebWin::UserKeyboardDialogClosed() { mainWin->TogglePause(); } + + // Restore joystick state + if (m_JoystickToKeys && !m_J2KWasEnabled) + { + m_JoystickToKeys = m_J2KWasEnabled; + if (m_MenuIdSticks != IDM_JOYSTICK) + { + ResetJoystick(); + } + } } /*****************************************************************************/ @@ -4354,6 +4654,37 @@ void BeebWin::ParseCommandLine() } } +/*****************************************************************************/ +// Look for file specific joystick map +void BeebWin::CheckForJoystickMap(const char *path) +{ + char file[_MAX_PATH]; + char drive[_MAX_DRIVE]; + char dir[_MAX_DIR]; + char filename[_MAX_FNAME]; + + if (!path || !path[0] || !m_AutoloadJoystickMap) + return; + + _splitpath(path, drive, dir, filename, NULL); + + // Look for prefs file with the same name with ".jmap" extension + _makepath(file, drive, dir, filename, "jmap"); + + if (GetFileAttributes(file) != INVALID_FILE_ATTRIBUTES) + { + ReadJoyMap(file, &JoystickMap); + } + else + { + // Mapping file not found - reset to default + ResetJoyMapToDefaultUser(); + + // Set jmap path even if the file doesn't exist + strcpy(m_JoystickMapPath, file); + } +} + /*****************************************************************************/ // Check for preference files in the same directory as the file specified void BeebWin::CheckForLocalPrefs(const char *path, bool bLoadPrefs) @@ -4386,8 +4717,7 @@ void BeebWin::CheckForLocalPrefs(const char *path, bool bLoadPrefs) HandleCommand(m_DisplayRenderer); InitMenu(); SetWindowText(m_hWnd, WindowTitle); - if (m_MenuIdSticks == IDM_JOYSTICK) - InitJoystick(); + InitJoystick(); } } @@ -4405,6 +4735,11 @@ void BeebWin::CheckForLocalPrefs(const char *path, bool bLoadPrefs) BeebReadRoms(); } } + + if (bLoadPrefs && m_AutoloadJoystickMap) + { + CheckForJoystickMap(path); + } } /*****************************************************************************/ diff --git a/Src/beebwin.h b/Src/beebwin.h index e9f1c946..bc093dde 100644 --- a/Src/beebwin.h +++ b/Src/beebwin.h @@ -51,13 +51,48 @@ Boston, MA 02110-1301, USA. #define CFG_KEYBOARD_LAYOUT "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout" #define CFG_SCANCODE_MAP "Scancode Map" +#define JOYSTICK_MAX_AXES 16 +#define JOYSTICK_MAX_BTNS 16 + +#define JOYSTICK_AXIS_UP 0 +#define JOYSTICK_AXIS_DOWN 1 +#define JOYSTICK_AXIS_LEFT 2 +#define JOYSTICK_AXIS_RIGHT 3 +#define JOYSTICK_AXIS_Z_N 4 +#define JOYSTICK_AXIS_Z_P 5 +#define JOYSTICK_AXIS_R_N 6 +#define JOYSTICK_AXIS_R_P 7 +#define JOYSTICK_AXIS_U_N 8 +#define JOYSTICK_AXIS_U_P 9 +#define JOYSTICK_AXIS_V_N 10 +#define JOYSTICK_AXIS_V_P 11 +#define JOYSTICK_AXIS_HAT_UP 12 +#define JOYSTICK_AXIS_HAT_DOWN 13 +#define JOYSTICK_AXIS_HAT_LEFT 14 +#define JOYSTICK_AXIS_HAT_RIGHT 15 +#define JOYSTICK_AXES_COUNT 16 + +#define BEEB_VKEY_JOY_START 256 +#define BEEB_VKEY_JOY1_AXES BEEB_VKEY_JOY_START +#define BEEB_VKEY_JOY1_BTN1 (BEEB_VKEY_JOY1_AXES + JOYSTICK_MAX_AXES) // 256+16 = 272 +#define BEEB_VKEY_JOY2_AXES (BEEB_VKEY_JOY1_BTN1 + JOYSTICK_MAX_BTNS) // 272+16 = 288 +#define BEEB_VKEY_JOY2_BTN1 (BEEB_VKEY_JOY2_AXES + JOYSTICK_MAX_AXES) // 288+16 = 304 +#define BEEB_VKEY_JOY_END (BEEB_VKEY_JOY2_BTN1 + JOYSTICK_MAX_BTNS) // 304+16 = 320 + +#define BEEB_VKEY_JOY_COUNT (BEEB_VKEY_JOY_END - BEEB_VKEY_JOY_START) +#define BEEB_VKEY_COUNT BEEB_VKEY_JOY_END + +#define UNASSIGNED_ROW -9 + typedef struct KeyMapping { int row; // Beeb row int col; // Beeb col bool shift; // Beeb shift state } KeyMapping; -typedef KeyMapping KeyMap[256][2]; // Indices are: [Virt key][shift state] +typedef KeyMapping KeyPair[2]; +typedef KeyPair KeyMap[256]; // Indices are: [Virt key][shift state] +typedef KeyPair JoyMap[BEEB_VKEY_JOY_COUNT]; extern const char *WindowTitle; @@ -213,7 +248,13 @@ class BeebWin { bool IsWindowMinimized() const; void DisplayClientAreaText(HDC hdc); void DisplayFDCBoardInfo(HDC hDC, int x, int y); + void SetJoystickButton(bool button); void ScaleJoystick(unsigned int x, unsigned int y); + unsigned int GetJoystickAxes(JOYCAPS& caps, unsigned int deadband, JOYINFOEX& joyInfoEx); + void TranslateOrSendKey(int vkey, bool keyUp); + void TranslateJoystickMove(int joyId, JOYINFOEX& joyInfoEx); + void TranslateJoystickButtons(int joyId, unsigned int buttons); + void TranslateJoystick(int joyId); void SetMousestickButton(int index, bool button); void ScaleMousestick(unsigned int x, unsigned int y); void HandleCommand(int MenuId); @@ -229,6 +270,7 @@ class BeebWin { void TogglePause(); bool IsPaused(); void OpenUserKeyboardDialog(); + void OpenJoystickMapDialog(); void UserKeyboardDialogClosed(); void ShowMenu(bool on); void HideMenu(bool hide); @@ -237,6 +279,7 @@ class BeebWin { void ResetTiming(void); int TranslateKey(int vkey, bool keyUp, int &row, int &col); void ParseCommandLine(void); + void CheckForJoystickMap(const char *path); void CheckForLocalPrefs(const char *path, bool bLoadPrefs); void FindCommandLineFile(char *CmdLineFile); void HandleCommandLineFile(int drive, const char *CmdLineFile); @@ -313,7 +356,18 @@ class BeebWin { int m_FPSTarget; bool m_JoystickCaptured; JOYCAPS m_JoystickCaps; + unsigned int m_Joystick1Deadband; + int m_Joystick1PrevAxes; + int m_Joystick1PrevBtns; + bool m_Joystick2Captured; + JOYCAPS m_Joystick2Caps; + unsigned int m_Joystick2Deadband; + int m_Joystick2PrevAxes; + int m_Joystick2PrevBtns; int m_MenuIdSticks; + bool m_JoystickToKeys; + bool m_AutoloadJoystickMap; + HWND m_JoystickTarget; bool m_HideCursor; bool m_CaptureMouse; bool m_MouseCaptured; @@ -323,8 +377,9 @@ class BeebWin { bool m_KeyMapAS; bool m_KeyMapFunc; char m_UserKeyMapPath[_MAX_PATH]; + char m_JoystickMapPath[_MAX_PATH]; bool m_ShiftPressed; - int m_vkeyPressed[256][2][2]; + int m_vkeyPressed[BEEB_VKEY_COUNT][2][2]; char m_AppPath[_MAX_PATH]; char m_UserDataPath[_MAX_PATH]; bool m_CustomData; @@ -406,6 +461,7 @@ class BeebWin { bool m_EmuPaused; bool m_StartPaused; bool m_WasPaused; + bool m_J2KWasEnabled; bool m_AutoBootDisc; bool m_KeyboardTimerElapsed; bool m_BootDiscTimerElapsed; @@ -555,9 +611,21 @@ class BeebWin { bool RebootSystem(); void LoadUserKeyMap(void); void SaveUserKeyMap(void); + void ResetJoystickMap(void); + void LoadJoystickMap(void); + void SaveJoystickMap(void); + FILE* OpenReadFile(const char *filename, const char *typeDescr, + const char *token); + FILE* OpenWriteFile(const char *filename, const char *typeDescr); bool ReadKeyMap(const char *filename, KeyMap *keymap); bool WriteKeyMap(const char *filename, KeyMap *keymap); + void ResetJoyMapToDefaultUser(void); + void ResetJoyMap(JoyMap* joymap); + bool ReadJoyMap(const char *filename, JoyMap *joymap); + bool WriteJoyMap(const char *filename, JoyMap *joymap); + void MaybeEnableInitJoystick(void); + void Report(MessageType type, const char *format, ...); bool RegCreateKey(HKEY hKeyRoot, LPCSTR lpSubKey); diff --git a/Src/beebwinio.cpp b/Src/beebwinio.cpp index 0c9a2c3d..6f5bf5b2 100644 --- a/Src/beebwinio.cpp +++ b/Src/beebwinio.cpp @@ -32,6 +32,7 @@ Boston, MA 02110-1301, USA. #include "beebmem.h" #include "beebemrc.h" #include "filedialog.h" +#include "SelectKeyDialog.h" #include "uservia.h" #include "beebsound.h" #include "disc8271.h" @@ -53,6 +54,8 @@ using namespace Gdiplus; // Token written to start of map file #define KEYMAP_TOKEN "*** BeebEm Keymap ***" +#define JOYMAP_TOKEN "*** BeebEm Joystick Map ***" + extern EDCB ExtBoard; extern bool DiscLoaded[2]; // Set to true when a disc image has been loaded. extern char CDiscName[2][256]; // Filename of disc current in drive 0 and 1; @@ -301,6 +304,8 @@ void BeebWin::LoadTape(void) else if (hasFileExt(FileName, ".csw")) { LoadCSWTape(FileName); } + + CheckForJoystickMap(FileName); } } @@ -1161,7 +1166,7 @@ void BeebWin::LoadUserKeyMap() /****************************************************************************/ void BeebWin::SaveUserKeyMap() { - char FileName[_MAX_PATH]; + char FileName[_MAX_PATH + 5]; const char* filter = "Key Map File (*.kmap)\0*.kmap\0"; FileDialog fileDialog(m_hWnd, FileName, sizeof(FileName), m_UserDataPath, filter); @@ -1176,111 +1181,423 @@ void BeebWin::SaveUserKeyMap() } } +/****************************************************************************/ +void BeebWin::ResetJoystickMap() +{ + if (MessageBox(GETHWND, "Clear joystick to keyboard mapping table?", WindowTitle, MB_YESNO | MB_ICONQUESTION) != IDYES) + return; + + ResetJoyMap(&JoystickMap); +} + +/****************************************************************************/ +void BeebWin::LoadJoystickMap() +{ + char DefaultPath[_MAX_PATH]; + char FileName[_MAX_PATH]; + const char* filter = "Joystick Mapping File (*.jmap)\0*.jmap\0"; + + // If Autoload is enabled, look for joystick mapping near disks, + // otherwise start in user data path. + if (m_AutoloadJoystickMap) + { + m_Preferences.GetStringValue("DiscsPath", DefaultPath); + GetDataPath(m_UserDataPath, DefaultPath); + } + else + { + strcpy(DefaultPath, m_UserDataPath); + } + + FileDialog fileDialog(m_hWnd, FileName, sizeof(FileName), DefaultPath, filter); + + if (m_JoystickMapPath[0] == '\0' && !m_AutoloadJoystickMap) + { + fileDialog.SetInitial("DefaultUser.jmap"); + } + else + { + fileDialog.SetInitial(m_JoystickMapPath); + } + + if (fileDialog.Open()) + { + if (ReadJoyMap(FileName, &JoystickMap)) + strcpy(m_JoystickMapPath, FileName); + } +} + +/****************************************************************************/ +void BeebWin::SaveJoystickMap() +{ + char DefaultPath[_MAX_PATH]; + // Add space for .jmap exstension + char FileName[_MAX_PATH + 5]; + const char* filter = "Key Map File (*.jmap)\0*.jmap\0"; + + // If Autoload is enabled, store joystick mapping near disks, + // otherwise start in user data path. + if (m_AutoloadJoystickMap) + { + m_Preferences.GetStringValue("DiscsPath", DefaultPath); + GetDataPath(m_UserDataPath, DefaultPath); + } + else + { + strcpy(DefaultPath, m_UserDataPath); + } + + FileDialog fileDialog(m_hWnd, FileName, sizeof(FileName), DefaultPath, filter); + + if (m_JoystickMapPath[0] == '\0' && !m_AutoloadJoystickMap) + { + fileDialog.SetInitial("DefaultUser.jmap"); + } + else + { + fileDialog.SetInitial(m_JoystickMapPath); + } + + if (fileDialog.Save()) + { + if (!hasFileExt(FileName, ".jmap")) + strcat(FileName, ".jmap"); + + if (WriteJoyMap(FileName, &JoystickMap)) + strcpy(m_JoystickMapPath, FileName); + } +} + +FILE* BeebWin::OpenReadFile(const char *filename, const char *typeDescr, + const char *token) +{ + FILE *infile = fopen(filename, "r"); + + if (infile == NULL) + { + char errstr[500]; + sprintf(errstr, "Failed to read %s file:\n %s", typeDescr, filename); + MessageBox(GETHWND, errstr, WindowTitle, MB_OK | MB_ICONERROR); + } + else + { + char buf[256]; + + if (fgets(buf, 255, infile) == NULL || + strcmp(buf, token) != 0) + { + char errstr[500]; + sprintf(errstr, "Invalid %s file:\n %s\n", typeDescr, filename); + MessageBox(GETHWND, errstr, WindowTitle, MB_OK | MB_ICONERROR); + fclose(infile); + infile = NULL; + } + } + + return infile; +} + /****************************************************************************/ bool BeebWin::ReadKeyMap(const char *filename, KeyMap *keymap) { bool success = true; + FILE *infile = OpenReadFile(filename, "key map", KEYMAP_TOKEN "\n"); char buf[256]; - FILE *infile = fopen(filename,"r"); - if (infile == NULL) + return false; + + fgets(buf, 255, infile); + + for (int i = 0; i < 256; ++i) { - char errstr[500]; - sprintf(errstr, "Failed to read key map file:\n %s", filename); - MessageBox(GETHWND, errstr, WindowTitle, MB_OK|MB_ICONERROR); - success = false; + if (fgets(buf, 255, infile) == NULL) + { + char errstr[500]; + sprintf(errstr, "Data missing from key map file:\n %s\n", filename); + MessageBox(GETHWND, errstr, WindowTitle, MB_OK | MB_ICONERROR); + success = false; + break; + } + + int shift0 = 0, shift1 = 0; + + sscanf(buf, "%d %d %d %d %d %d", + &(*keymap)[i][0].row, + &(*keymap)[i][0].col, + &shift0, + &(*keymap)[i][1].row, + &(*keymap)[i][1].col, + &shift1); + + (*keymap)[i][0].shift = shift0 != 0; + (*keymap)[i][1].shift = shift1 != 0; } - else + + fclose(infile); + + return success; +} + +/****************************************************************************/ +void BeebWin::ResetJoyMapToDefaultUser(void) +{ + char keymap[_MAX_PATH]; + strcpy(keymap, "DefaultUser.jmap"); + GetDataPath(m_UserDataPath, keymap); + ResetJoyMap(&JoystickMap); + if (GetFileAttributes(keymap) != INVALID_FILE_ATTRIBUTES) + ReadJoyMap(keymap, &JoystickMap); +} + +/****************************************************************************/ +void BeebWin::ResetJoyMap(JoyMap* joymap) +{ + // Initialize all input to unassigned + for (auto& mapping : *joymap) + { + mapping[0].row = mapping[1].row = UNASSIGNED_ROW; + mapping[0].col = mapping[1].col = 0; + mapping[0].shift = false; + mapping[1].shift = true; + } +} + +/****************************************************************************/ +static +void makeupper(char* str) +{ + if (str == NULL) + return; + while (*str) { - if (fgets(buf, 255, infile) == NULL || - strcmp(buf, KEYMAP_TOKEN "\n") != 0) + *str = toupper(*str); + ++str; + } +} + +/****************************************************************************/ +bool BeebWin::ReadJoyMap(const char *filename, JoyMap *joymap) +{ + bool success = true; + FILE *infile = OpenReadFile(filename, "key map", JOYMAP_TOKEN "\n"); + char buf[256]; + JoyMap newJoyMap; + int line = 1; + + if (infile == NULL) + return false; + + ResetJoyMap(&newJoyMap); + + while (fgets(buf, 255, infile) != NULL) + { + char *ptr, *inputName, *keyName1, *keyName2; + + ++line; + + // Read at most three tokens from line + inputName = strtok_s(buf, " \t\n", &ptr); + keyName1 = strtok_s(NULL, " \t\n", &ptr); + keyName2 = strtok_s(NULL, " \t\n", &ptr); + + // Ignore empty lines or lines starting with '#' + if (inputName == NULL || inputName[0] == '#') + continue; + + if (keyName1 == NULL) { char errstr[500]; - sprintf(errstr, "Invalid key map file:\n %s\n", filename); - MessageBox(GETHWND, errstr, WindowTitle, MB_OK|MB_ICONERROR); + sprintf(errstr, "Invalid line in joystick mapping file:\n %s\n line %d\n", + filename, line); + MessageBox(GETHWND, errstr, WindowTitle, MB_OK | MB_ICONERROR); success = false; + break; } - else + + // Get vkey number and mapping entry from input name + int vkey = SelectKeyDialog::JoyVKeyByName(inputName); + if (vkey < BEEB_VKEY_JOY_START || vkey >= BEEB_VKEY_JOY_END) { - fgets(buf, 255, infile); + char errstr[500]; + sprintf(errstr, "Invalid input name in joystick mapping file:\n %s\n line %d\n", + filename, line); + MessageBox(GETHWND, errstr, WindowTitle, MB_OK | MB_ICONERROR); + success = false; + break; + } + KeyMapping* mapping = newJoyMap[vkey - BEEB_VKEY_JOY_START]; - for (int i = 0; i < 256; ++i) - { - if (fgets(buf, 255, infile) == NULL) - { - char errstr[500]; - sprintf(errstr, "Data missing from key map file:\n %s\n", filename); - MessageBox(GETHWND, errstr, WindowTitle, MB_OK|MB_ICONERROR); - success = false; - break; - } + // Get shift state and BBC key from key name + bool shift1 = false; + makeupper(keyName1); + + if (strncmp(keyName1, "SH+", 3) == 0) + { + shift1 = true; + keyName1 += 3; + } + + const BBCKey& key1 = GetBBCKeyByName(keyName1); + if (key1.row == UNASSIGNED_ROW && strcmp(keyName1, "NONE") != 0) + { + char errstr[500]; + sprintf(errstr, "Invalid key name in joystick mapping file:\n %s\n line %d\n", + filename, line); + MessageBox(GETHWND, errstr, WindowTitle, MB_OK | MB_ICONERROR); + success = false; + break; + } - int shift0 = 0, shift1 = 0; + if (keyName2 == NULL) + { + // Shifted and unshifted input map to the same key + mapping[0].row = mapping[1].row = key1.row; + mapping[0].col = mapping[1].col = key1.column; + mapping[0].shift = false; + mapping[1].shift = true; + } + else + { + bool shift2 = false; + makeupper(keyName2); - sscanf(buf, "%d %d %d %d %d %d", - &(*keymap)[i][0].row, - &(*keymap)[i][0].col, - &shift0, - &(*keymap)[i][1].row, - &(*keymap)[i][1].col, - &shift1); + if (strncmp(keyName2, "SH+", 3) == 0) + { + shift2 = true; + keyName2 += 3; + } - (*keymap)[i][0].shift = shift0 != 0; - (*keymap)[i][1].shift = shift1 != 0; + const BBCKey& key2 = GetBBCKeyByName(keyName2); + if (key2.row == UNASSIGNED_ROW && strcmp(keyName2, "NONE") != 0) + { + char errstr[500]; + sprintf(errstr, "Invalid key name in joystick mapping file:\n %s\n line %d\n", + filename, line); + MessageBox(GETHWND, errstr, WindowTitle, MB_OK | MB_ICONERROR); + success = false; + break; } + + mapping[0].row = key1.row; + mapping[0].col = key1.column; + mapping[0].shift = shift1; + mapping[1].row = key2.row; + mapping[1].col = key2.column; + mapping[1].shift = shift2; } + } - fclose(infile); + if (success) + { + memcpy(joymap, &newJoyMap, sizeof(newJoyMap)); } return success; } /****************************************************************************/ -bool BeebWin::WriteKeyMap(const char *filename, KeyMap *keymap) +FILE* BeebWin::OpenWriteFile(const char *filename, const char *typeDescr) { - bool success = true; - /* First check if file already exists */ - FILE *outfile = fopen(filename,"r"); + FILE *outfile = fopen(filename, "r"); if (outfile != NULL) { fclose(outfile); char errstr[200]; sprintf(errstr, "File already exists:\n %s\n\nOverwrite file?", filename); - if (MessageBox(GETHWND,errstr,WindowTitle,MB_YESNO|MB_ICONQUESTION) != IDYES) - return false; + if (MessageBox(GETHWND, errstr, WindowTitle, MB_YESNO | MB_ICONQUESTION) != IDYES) + return NULL; } - outfile=fopen(filename,"w"); + outfile = fopen(filename, "w"); if (outfile == NULL) { char errstr[500]; - sprintf(errstr, "Failed to write key map file:\n %s", filename); - MessageBox(GETHWND, errstr, WindowTitle, MB_OK|MB_ICONERROR); - success = false; + sprintf(errstr, "Failed to write %s file:\n %s", typeDescr, filename); + MessageBox(GETHWND, errstr, WindowTitle, MB_OK | MB_ICONERROR); } - else + + return outfile; +} + +/****************************************************************************/ +bool BeebWin::WriteKeyMap(const char *filename, KeyMap *keymap) +{ + FILE* outfile = OpenWriteFile(filename, "key map"); + + if (outfile == NULL) + return false; + + fprintf(outfile, KEYMAP_TOKEN "\n\n"); + + for (int i = 0; i < 256; ++i) { - fprintf(outfile, KEYMAP_TOKEN "\n\n"); + fprintf(outfile, "%d %d %d %d %d %d\n", + (*keymap)[i][0].row, + (*keymap)[i][0].col, + (*keymap)[i][0].shift, + (*keymap)[i][1].row, + (*keymap)[i][1].col, + (*keymap)[i][1].shift); + } + + fclose(outfile); + + return true; +} + +/****************************************************************************/ +bool BeebWin::WriteJoyMap(const char *filename, JoyMap *joymap) +{ + FILE* outfile = OpenWriteFile(filename, "joystick map"); + + if (outfile == NULL) + return false; - for (int i = 0; i < 256; ++i) + fprintf(outfile, JOYMAP_TOKEN "\n\n"); + + for (int i = 0; i < BEEB_VKEY_JOY_COUNT; ++i) + { + KeyMapping* mapping = (*joymap)[i]; + if (mapping[0].row != UNASSIGNED_ROW + || mapping[1].row != UNASSIGNED_ROW) { - fprintf(outfile, "%d %d %d %d %d %d\n", - (*keymap)[i][0].row, - (*keymap)[i][0].col, - (*keymap)[i][0].shift, - (*keymap)[i][1].row, - (*keymap)[i][1].col, - (*keymap)[i][1].shift); - } + auto inputName = SelectKeyDialog::KeyName(i + BEEB_VKEY_JOY_START); - fclose(outfile); + if (mapping[0].row == mapping[1].row + && mapping[0].col == mapping[1].col + && !mapping[0].shift && mapping[1].shift) + { + // Shifted and unshifted input map to the same key - write one name + const auto& key = GetBBCKeyByRowAndCol(mapping[0].row, mapping[0].col); + fprintf(outfile, "%-13s %s\n", + inputName, + key.name); + } + else + { + // Separate mapping for shifted and unshifted + const auto& key1 = GetBBCKeyByRowAndCol(mapping[0].row, mapping[0].col); + auto key1shift = mapping[0].shift && mapping[0].row != UNASSIGNED_ROW; + const auto& key2 = GetBBCKeyByRowAndCol(mapping[1].row, mapping[1].col); + auto key2shift = mapping[1].shift && mapping[1].row != UNASSIGNED_ROW; + fprintf(outfile, "%-13s %s%-*s %s%s\n", + inputName, + key1shift ? "SH+" : "", + // Align for longest possible: "SHIFT-LOCK+SH" + key1shift ? 10 : 13, + key1.name, + key2shift ? "SH+" : "", + key2.name); + } + } } - return success; + fclose(outfile); + + return true; } /****************************************************************************/ diff --git a/Src/beebwinprefs.cpp b/Src/beebwinprefs.cpp index 86ec96c7..9abbd5fb 100644 --- a/Src/beebwinprefs.cpp +++ b/Src/beebwinprefs.cpp @@ -75,11 +75,17 @@ static const char *CFG_PRINTER_PORT = "PrinterPort"; static const char *CFG_PRINTER_FILE = "PrinterFile"; static const char *CFG_MACHINE_TYPE = "MachineType"; static const char *CFG_TUBE_TYPE = "TubeType"; +static const char *CFG_OPTIONS_STICKS_TO_KEYS = "SticksToKeys"; +static const char *CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP = "AutoloadJoystickMap"; +static const char *CFG_OPTIONS_STICK1_DEADBAND = "Stick1ToKeysDeadBand"; +static const char *CFG_OPTIONS_STICK2_DEADBAND = "Stick2ToKeysDeadBand"; #define LED_COLOUR_TYPE (LEDByte&4)>>2 #define LED_SHOW_KB (LEDByte&1) #define LED_SHOW_DISC (LEDByte&2)>>1 +#define DEFAULT_JOY_DEADBAND 4096 + extern unsigned char CMOSDefault[64]; void BeebWin::LoadPreferences() @@ -258,6 +264,23 @@ void BeebWin::LoadPreferences() else m_MenuIdSticks = 0; + if (!m_Preferences.GetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys)) + m_JoystickToKeys = false; + + if (!m_Preferences.GetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, + m_AutoloadJoystickMap)) + m_AutoloadJoystickMap = false; + + if (m_Preferences.GetDWORDValue(CFG_OPTIONS_STICK1_DEADBAND, dword)) + m_Joystick1Deadband = dword; + else + m_Joystick1Deadband = DEFAULT_JOY_DEADBAND; + + if (m_Preferences.GetDWORDValue(CFG_OPTIONS_STICK2_DEADBAND, dword)) + m_Joystick2Deadband = dword; + else + m_Joystick2Deadband = DEFAULT_JOY_DEADBAND; + if (!m_Preferences.GetBoolValue(CFG_OPTIONS_FREEZEINACTIVE, m_FreezeWhenInactive)) m_FreezeWhenInactive = true; @@ -642,7 +665,12 @@ void BeebWin::SavePreferences(bool saveAll) m_Preferences.SetBoolValue("TextToSpeechEnabled", m_TextToSpeechEnabled); m_Preferences.SetBoolValue("Music5000Enabled", Music5000Enabled); - m_Preferences.SetDWORDValue( CFG_OPTIONS_STICKS, m_MenuIdSticks); + m_Preferences.SetDWORDValue(CFG_OPTIONS_STICKS, m_MenuIdSticks); + m_Preferences.SetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys); + m_Preferences.SetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, m_AutoloadJoystickMap); + m_Preferences.SetDWORDValue(CFG_OPTIONS_STICK1_DEADBAND, m_Joystick1Deadband); + m_Preferences.SetDWORDValue(CFG_OPTIONS_STICK2_DEADBAND, m_Joystick2Deadband); + m_Preferences.SetBoolValue(CFG_OPTIONS_FREEZEINACTIVE, m_FreezeWhenInactive); m_Preferences.SetBoolValue(CFG_OPTIONS_HIDE_CURSOR, m_HideCursor); m_Preferences.SetBoolValue(CFG_OPTIONS_CAPTURE_MOUSE, m_CaptureMouse); diff --git a/Src/filedialog.cpp b/Src/filedialog.cpp index c60d9f95..65f2c761 100644 --- a/Src/filedialog.cpp +++ b/Src/filedialog.cpp @@ -36,12 +36,11 @@ FileDialog::FileDialog(HWND hwndOwner, LPTSTR result, DWORD resultLength, m_ofn.nMaxFile = resultLength; m_ofn.lpstrInitialDir = initialFolder; m_ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY; + m_ofn.lpstrFile[0] = 0; } bool FileDialog::ShowDialog(bool open) { - m_ofn.lpstrFile[0] = 0; - if (open) return GetOpenFileName(&m_ofn) != 0; else diff --git a/Src/filedialog.h b/Src/filedialog.h index 6e54d79b..5124f256 100644 --- a/Src/filedialog.h +++ b/Src/filedialog.h @@ -51,6 +51,11 @@ class FileDialog m_ofn.lpstrTitle = title; } + void SetInitial(LPCTSTR initial) + { + strcpy(m_ofn.lpstrFile, initial); + } + // Show dialog bool Open() { diff --git a/Src/userkybd.cpp b/Src/userkybd.cpp index 6e61eca1..8e8f1cd0 100644 --- a/Src/userkybd.cpp +++ b/Src/userkybd.cpp @@ -24,13 +24,14 @@ Boston, MA 02110-1301, USA. #include #include #include +#include #include "main.h" #include "beebemrc.h" #include "userkybd.h" #include "SelectKeyDialog.h" #include "Messages.h" -static void SetKeyColour(COLORREF aColour); +static void SetKeyColour(COLORREF aColour, HWND keyCtrl = NULL); static void SelectKeyMapping(HWND hwnd, UINT ctrlID, HWND hwndCtrl); static void SetBBCKeyForVKEY(int Key, bool shift); static void SetRowCol(UINT ctrlID); @@ -44,37 +45,244 @@ static void DrawBorder(HDC hDC, RECT rect, BOOL Depressed); static void DrawText(HDC hDC, RECT rect, HWND hwndCtrl, COLORREF colour, bool Depressed); static COLORREF GetKeyColour(UINT ctrlID); static std::string GetKeysUsed(); +static void FillAssignedKeysCount(); +static void UpdateAssignedKeysCount(int row, int col, int change, bool redrawColour = false); +static int GetBBCKeyIndex(const BBCKey& key); // Colour used to highlight the selected key. static const COLORREF HighlightColour = 0x00FF0080; // Purple static const COLORREF FunctionKeyColour = 0x000000FF; // Red static const COLORREF NormalKeyColour = 0x00000000; // Black -static COLORREF oldKeyColour; +static const COLORREF JoyAssignedKeyColour = 0x00802020; // Blueish static HWND hwndBBCKey; // Holds the BBCKey control handle which is now selected. static UINT selectedCtrlID; // Holds ctrlId of selected key (or 0 if none selected). static HWND hwndMain; // Holds the BeebWin window handle. static HWND hwndUserKeyboard; +static bool doingJoystick; // Doing joystick vs user keyboard mapping static int BBCRow; // Used to store the Row and Col values while we wait static int BBCCol; // for a key press from the User. static bool doingShifted; // Selecting shifted or unshifted key press +static bool doingUnassign; // Removing key assignment // Initialised to defaultMapping. KeyMap UserKeymap; -static const char* szSelectKeyDialogTitle[2] = { - "Press key for unshifted press...", - "Press key for shifted press..." +JoyMap JoystickMap; + +static const char* szSelectKeyDialogTitle[2][3] = { + { + "Press key for unshifted press...", + "Press key for shifted press...", + "Press key to unassign..." + }, + { + // This can't be too long so 'unshifted' part is visible + "Move stick for unshifted press...", + "Move stick for shifted press...", + "Move stick to unassign..." + } +}; + +// Table of BBC keys +const BBCKey BBCKeys[] = { + // Unassigned must go first + { IDK_UNASSIGN, "NONE", -9, 0 }, + + // Letter keys + { IDK_A, "A", 4, 1 }, + { IDK_B, "B", 6, 4 }, + { IDK_C, "C", 5, 2 }, + { IDK_D, "D", 3, 2 }, + { IDK_E, "E", 2, 2 }, + { IDK_F, "F", 4, 3 }, + { IDK_G, "G", 5, 3 }, + { IDK_H, "H", 5, 4 }, + { IDK_I, "I", 2, 5 }, + { IDK_J, "J", 4, 5 }, + { IDK_K, "K", 4, 6 }, + { IDK_L, "L", 5, 6 }, + { IDK_M, "M", 6, 5 }, + { IDK_N, "N", 5, 5 }, + { IDK_O, "O", 3, 6 }, + { IDK_P, "P", 3, 7 }, + { IDK_Q, "Q", 1, 0 }, + { IDK_R, "R", 3, 3 }, + { IDK_S, "S", 5, 1 }, + { IDK_T, "T", 2, 3 }, + { IDK_U, "U", 3, 5 }, + { IDK_V, "V", 6, 3 }, + { IDK_W, "W", 2, 1 }, + { IDK_X, "X", 4, 2 }, + { IDK_Y, "Y", 4, 4 }, + { IDK_Z, "Z", 6, 1 }, + + // Number keys + { IDK_0, "0", 2, 7 }, + { IDK_1, "1", 3, 0 }, + { IDK_2, "2", 3, 1 }, + { IDK_3, "3", 1, 1 }, + { IDK_4, "4", 1, 2 }, + { IDK_5, "5", 1, 3 }, + { IDK_6, "6", 3, 4 }, + { IDK_7, "7", 2, 4 }, + { IDK_8, "8", 1, 5 }, + { IDK_9, "9", 2, 6 }, + + // Function keys. + { IDK_F0, "F0", 2, 0 }, + { IDK_F1, "F1", 7, 1 }, + { IDK_F2, "F2", 7, 2 }, + { IDK_F3, "F3", 7, 3 }, + { IDK_F4, "F4", 1, 4 }, + { IDK_F5, "F5", 7, 4 }, + { IDK_F6, "F6", 7, 5 }, + { IDK_F7, "F7", 1, 6 }, + { IDK_F8, "F8", 7, 6 }, + { IDK_F9, "F9", 7, 7 }, + + // Special keys. + { IDK_LEFT, "LEFT", 1, 9 }, + { IDK_RIGHT, "RIGHT", 7, 9 }, + { IDK_UP, "UP", 3, 9 }, + { IDK_DOWN, "DOWN", 2, 9 }, + { IDK_BREAK, "BREAK", -2, -2 }, + { IDK_COPY, "COPY", 6, 9 }, + { IDK_DEL, "DELETE", 5, 9 }, + { IDK_CAPS, "CAPS-LOCK", 4, 0 }, + { IDK_TAB, "TAB", 6, 0 }, + { IDK_CTRL, "CTRL", 0, 1 }, + { IDK_SPACE, "SPACE", 6, 2 }, + { IDK_RETURN, "RETURN", 4, 9 }, + { IDK_ESC, "ESCAPE", 7, 0 }, + { IDK_SHIFT_L, "SHIFT", 0, 0 }, + { IDK_SHIFT_R, "SHIFT", 0, 0 }, + { IDK_SHIFT_LOCK, "SHIFT-LOCK", 5, 0 }, + + // Special Character keys. + { IDK_SEMI_COLON, ";", 5, 7 }, + { IDK_EQUALS, "=", 1, 7 }, + { IDK_COMMA, ",", 6, 6 }, + { IDK_CARET, "^", 1, 8 }, + { IDK_DOT, ".", 6, 7 }, + { IDK_FWDSLASH, "/", 6, 8 }, + { IDK_STAR, "*", 4, 8 }, + { IDK_OPEN_SQUARE, "[", 3, 8 }, + { IDK_BACKSLASH, "\\", 7, 8 }, + { IDK_CLOSE_SQUARE, "]", 5, 8 }, + { IDK_AT, "@", 4, 7 }, + { IDK_UNDERSCORE, "_", 2, 8 }, +}; + +const std::pair BBCKeyAliases[] = { + { "DEL", "DELETE" }, + { "CAPS", "CAPS-LOCK" }, + { "CAPSLOCK", "CAPS-LOCK" }, + { "CONTROL", "CTRL" }, + { "ESC", "ESCAPE" }, + { "SHIFTLOCK", "SHIFT-LOCK" } }; +// Number of inputs assigned to each key (for joystick dialog highlight) +static int assignedKeysCount[_countof(BBCKeys)] = {}; + +/****************************************************************************/ + +const BBCKey& GetBBCKeyByResId(int ctrlId) +{ + using resIdToKeyMapType = std::map; + + // Construct map on first use by lambda + static resIdToKeyMapType resIdToKeyMap = []() + { + resIdToKeyMapType keyMap{}; + + for (auto& theKey : BBCKeys) + keyMap[theKey.ctrlId] = &theKey; + return keyMap; + } (); + + auto iter = resIdToKeyMap.find(ctrlId); + if (iter == resIdToKeyMap.end()) + return BBCKeys[0]; + + return *iter->second; +} + +/****************************************************************************/ + +const BBCKey& GetBBCKeyByName(const std::string& name) +{ + using nameToKeyMapType = std::map; + + // Construct map on first use by lambda + static nameToKeyMapType nameToKeyMap = []() + { + nameToKeyMapType keyMap{}; + + for (auto& theKey : BBCKeys) + keyMap[theKey.name] = &theKey; + + for (auto& alias : BBCKeyAliases) + keyMap[alias.first] = keyMap[alias.second]; + + return keyMap; + } (); + + auto iter = nameToKeyMap.find(name); + if (iter == nameToKeyMap.end()) + return BBCKeys[0]; + + return *iter->second; +} + /****************************************************************************/ -bool UserKeyboardDialog(HWND hwndParent) +const BBCKey& GetBBCKeyByRowAndCol(int row, int col) +{ + using posPair = std::pair; + using posToKeyMapType = std::map; + + // Construct map on first use by lambda + static posToKeyMapType posToKeyMap = []() + { + posToKeyMapType keyMap{}; + + for (auto& theKey : BBCKeys) + keyMap[posPair{ theKey.row, theKey.column }] = &theKey; + return keyMap; + } (); + + auto iter = posToKeyMap.find(posPair{ row, col }); + if (iter == posToKeyMap.end()) + return BBCKeys[0]; + + return *iter->second; +} + +/****************************************************************************/ +// Get index for assignedKeysCount. The 'key' parameter must be reference to +// item in BBCKeys table, not a copy of it. +int GetBBCKeyIndex(const BBCKey& key) +{ + int index = &key - BBCKeys; + if (index >= 0 && index < _countof(BBCKeys)) + return index; + return 0; +} + +/****************************************************************************/ + +bool UserKeyboardDialog(HWND hwndParent, bool joystick) { // Initialise locals used during this window's life. hwndMain = hwndParent; selectedCtrlID = 0; + doingJoystick = joystick; + + if (doingJoystick) + FillAssignedKeysCount(); // Open the dialog box. This is created as a modeless dialog so that // the "select key" dialog box can handle key-press messages. @@ -97,13 +305,16 @@ bool UserKeyboardDialog(HWND hwndParent) /****************************************************************************/ -static void SetKeyColour(COLORREF aColour) +static void SetKeyColour(COLORREF aColour, HWND keyCtrl) { - HDC hdc = GetDC(hwndBBCKey); + if (keyCtrl == NULL) + keyCtrl = hwndBBCKey; + + HDC hdc = GetDC(keyCtrl); SetBkColor(hdc, aColour); - ReleaseDC(hwndBBCKey, hdc); - InvalidateRect(hwndBBCKey, nullptr, TRUE); - UpdateWindow(hwndBBCKey); + ReleaseDC(keyCtrl, hdc); + InvalidateRect(keyCtrl, nullptr, TRUE); + UpdateWindow(keyCtrl); } /****************************************************************************/ @@ -113,21 +324,22 @@ static void SelectKeyMapping(HWND hwnd, UINT ctrlID, HWND hwndCtrl) // Set the placeholders. SetRowCol(ctrlID); - oldKeyColour = GetKeyColour(ctrlID); - hwndBBCKey = hwndCtrl; selectedCtrlID = ctrlID; doingShifted = false; + doingUnassign = (BBCRow == -9); - std::string UsedKeys = GetKeysUsed(); + std::string UsedKeys = doingUnassign ? "" : GetKeysUsed(); // Now ask the user to input the PC key to assign to the BBC key. selectKeyDialog = new SelectKeyDialog( hInst, hwnd, - szSelectKeyDialogTitle[doingShifted ? 1 : 0], - UsedKeys + szSelectKeyDialogTitle[(int)doingJoystick][doingUnassign ? 2 : doingShifted ? 1 : 0], + UsedKeys, + doingShifted, + doingJoystick ); selectKeyDialog->Open(); @@ -137,7 +349,7 @@ static void SelectKeyMapping(HWND hwnd, UINT ctrlID, HWND hwndCtrl) static void SetBBCKeyForVKEY(int Key, bool Shift) { - if (Key >= 0 && Key < 256) + if (!doingJoystick && Key >= 0 && Key < BEEB_VKEY_JOY_START) { UserKeymap[Key][static_cast(Shift)].row = BBCRow; UserKeymap[Key][static_cast(Shift)].col = BBCCol; @@ -146,101 +358,24 @@ static void SetBBCKeyForVKEY(int Key, bool Shift) // DebugTrace("SetBBCKey: key=%d, shift=%d, row=%d, col=%d, bbcshift=%d\n", // Key, shift, BBCRow, BBCCol, doingShifted); } + else if (doingJoystick && Key >= BEEB_VKEY_JOY_START && Key < BEEB_VKEY_JOY_END) + { + KeyMapping& entry = JoystickMap[Key - BEEB_VKEY_JOY_START][static_cast(Shift)]; + UpdateAssignedKeysCount(entry.row, entry.col, -1, true); + entry.row = BBCRow; + entry.col = BBCCol; + entry.shift = doingShifted; + UpdateAssignedKeysCount(entry.row, entry.col, +1); + } } /****************************************************************************/ static void SetRowCol(UINT ctrlID) { - switch (ctrlID) - { - // Character keys. - case IDK_A: BBCRow = 4; BBCCol = 1; break; - case IDK_B: BBCRow = 6; BBCCol = 4; break; - case IDK_C: BBCRow = 5; BBCCol = 2; break; - case IDK_D: BBCRow = 3; BBCCol = 2; break; - case IDK_E: BBCRow = 2; BBCCol = 2; break; - case IDK_F: BBCRow = 4; BBCCol = 3; break; - case IDK_G: BBCRow = 5; BBCCol = 3; break; - case IDK_H: BBCRow = 5; BBCCol = 4; break; - case IDK_I: BBCRow = 2; BBCCol = 5; break; - case IDK_J: BBCRow = 4; BBCCol = 5; break; - case IDK_K: BBCRow = 4; BBCCol = 6; break; - case IDK_L: BBCRow = 5; BBCCol = 6; break; - case IDK_M: BBCRow = 6; BBCCol = 5; break; - case IDK_N: BBCRow = 5; BBCCol = 5; break; - case IDK_O: BBCRow = 3; BBCCol = 6; break; - case IDK_P: BBCRow = 3; BBCCol = 7; break; - case IDK_Q: BBCRow = 1; BBCCol = 0; break; - case IDK_R: BBCRow = 3; BBCCol = 3; break; - case IDK_S: BBCRow = 5; BBCCol = 1; break; - case IDK_T: BBCRow = 2; BBCCol = 3; break; - case IDK_U: BBCRow = 3; BBCCol = 5; break; - case IDK_V: BBCRow = 6; BBCCol = 3; break; - case IDK_W: BBCRow = 2; BBCCol = 1; break; - case IDK_X: BBCRow = 4; BBCCol = 2; break; - case IDK_Y: BBCRow = 4; BBCCol = 4; break; - case IDK_Z: BBCRow = 6; BBCCol = 1; break; - - // Number keys. - case IDK_0: BBCRow = 2; BBCCol = 7; break; - case IDK_1: BBCRow = 3; BBCCol = 0; break; - case IDK_2: BBCRow = 3; BBCCol = 1; break; - case IDK_3: BBCRow = 1; BBCCol = 1; break; - case IDK_4: BBCRow = 1; BBCCol = 2; break; - case IDK_5: BBCRow = 1; BBCCol = 3; break; - case IDK_6: BBCRow = 3; BBCCol = 4; break; - case IDK_7: BBCRow = 2; BBCCol = 4; break; - case IDK_8: BBCRow = 1; BBCCol = 5; break; - case IDK_9: BBCRow = 2; BBCCol = 6; break; - - // Function keys. - case IDK_F0: BBCRow = 2; BBCCol = 0; break; - case IDK_F1: BBCRow = 7; BBCCol = 1; break; - case IDK_F2: BBCRow = 7; BBCCol = 2; break; - case IDK_F3: BBCRow = 7; BBCCol = 3; break; - case IDK_F4: BBCRow = 1; BBCCol = 4; break; - case IDK_F5: BBCRow = 7; BBCCol = 4; break; - case IDK_F6: BBCRow = 7; BBCCol = 5; break; - case IDK_F7: BBCRow = 1; BBCCol = 6; break; - case IDK_F8: BBCRow = 7; BBCCol = 6; break; - case IDK_F9: BBCRow = 7; BBCCol = 7; break; - - // Special keys. - case IDK_LEFT: BBCRow = 1; BBCCol = 9; break; - case IDK_RIGHT: BBCRow = 7; BBCCol = 9; break; - case IDK_UP: BBCRow = 3; BBCCol = 9; break; - case IDK_DOWN: BBCRow = 2; BBCCol = 9; break; - case IDK_BREAK: BBCRow = -2; BBCCol = -2; break; - case IDK_COPY: BBCRow = 6; BBCCol = 9; break; - case IDK_DEL: BBCRow = 5; BBCCol = 9; break; - case IDK_CAPS: BBCRow = 4; BBCCol = 0; break; - case IDK_TAB: BBCRow = 6; BBCCol = 0; break; - case IDK_CTRL: BBCRow = 0; BBCCol = 1; break; - case IDK_SPACE: BBCRow = 6; BBCCol = 2; break; - case IDK_RETURN: BBCRow = 4; BBCCol = 9; break; - case IDK_ESC: BBCRow = 7; BBCCol = 0; break; - case IDK_SHIFT_L: BBCRow = 0; BBCCol = 0; break; - case IDK_SHIFT_R: BBCRow = 0; BBCCol = 0; break; - case IDK_SHIFT_LOCK: BBCRow = 5; BBCCol = 0; break; - - // Special Character keys. - case IDK_SEMI_COLON: BBCRow = 5; BBCCol = 7; break; - case IDK_EQUALS: BBCRow = 1; BBCCol = 7; break; - case IDK_COMMA: BBCRow = 6; BBCCol = 6; break; - case IDK_CARET: BBCRow = 1; BBCCol = 8; break; - case IDK_DOT: BBCRow = 6; BBCCol = 7; break; - case IDK_FWDSLASH: BBCRow = 6; BBCCol = 8; break; - case IDK_STAR: BBCRow = 4; BBCCol = 8; break; - case IDK_OPEN_SQUARE: BBCRow = 3; BBCCol = 8; break; - case IDK_BACKSLASH: BBCRow = 7; BBCCol = 8; break; - case IDK_CLOSE_SQUARE: BBCRow = 5; BBCCol = 8; break; - case IDK_AT: BBCRow = 4; BBCCol = 7; break; - case IDK_UNDERSCORE: BBCRow = 2; BBCCol = 8; break; - - default: - BBCRow = 0; BBCCol = 0; - } + const BBCKey& key = GetBBCKeyByResId(ctrlID); + BBCRow = key.row; + BBCCol = key.column; } /****************************************************************************/ @@ -252,6 +387,13 @@ static INT_PTR CALLBACK UserKeyboardDlgProc(HWND hwnd, { switch (nMessage) { + case WM_INITDIALOG: + if (doingJoystick) + { + SetWindowText(hwnd, "Joystick To Keyboard Mapping"); + } + return FALSE; + case WM_COMMAND: switch (wParam) { @@ -281,7 +423,7 @@ static INT_PTR CALLBACK UserKeyboardDlgProc(HWND hwnd, // Assign the BBC key to the PC key. SetBBCKeyForVKEY( selectKeyDialog->Key(), - doingShifted + selectKeyDialog->Shift() ); } @@ -290,25 +432,28 @@ static INT_PTR CALLBACK UserKeyboardDlgProc(HWND hwnd, if ((wParam == IDOK || wParam == IDCONTINUE) && !doingShifted) { - doingShifted = true; + doingShifted = !doingShifted; - std::string UsedKeys = GetKeysUsed(); + std::string UsedKeys = doingUnassign ? "" : GetKeysUsed(); selectKeyDialog = new SelectKeyDialog( hInst, hwndUserKeyboard, - szSelectKeyDialogTitle[doingShifted ? 1 : 0], - UsedKeys + szSelectKeyDialogTitle[(int)doingJoystick][doingUnassign ? 2 : doingShifted ? 1 : 0], + UsedKeys, + doingShifted, + doingJoystick ); selectKeyDialog->Open(); } else { + int ctrlID = selectedCtrlID; selectedCtrlID = 0; // Show the key as not depressed, i.e., normal. - SetKeyColour(oldKeyColour); + SetKeyColour(GetKeyColour(ctrlID)); } return TRUE; @@ -436,6 +581,13 @@ static COLORREF GetKeyColour(UINT ctrlID) return HighlightColour; } + if (doingJoystick) + { + int index = GetBBCKeyIndex(GetBBCKeyByResId(ctrlID)); + if (index != 0 && assignedKeysCount[index] > 0) + return JoyAssignedKeyColour; + } + switch (ctrlID) { case IDK_F0: @@ -461,16 +613,36 @@ static std::string GetKeysUsed() { std::string Keys; + KeyPair* table; + int start, end; + int offset; // First Vkey code in table + + if (!doingJoystick) + { + table = UserKeymap; + offset = 0; + start = 1; + end = 256; + } + else + { + table = JoystickMap; + offset = BEEB_VKEY_JOY_START; + start = BEEB_VKEY_JOY_START; + end = BEEB_VKEY_JOY_END; + } + // First see if this key is defined. - if (BBCRow != 0 || BBCCol != 0) + // Row 0 is Shift key, row -2 is Break, row -9 is unassigned + if (BBCRow != -9) { - for (int i = 1; i < 256; i++) + for (int i = start; i < end; i++) { for (int s = 0; s < 2; ++s) { - if (UserKeymap[i][s].row == BBCRow && - UserKeymap[i][s].col == BBCCol && - UserKeymap[i][s].shift == doingShifted) + if (table[i - offset][s].row == BBCRow && + table[i - offset][s].col == BBCCol && + table[i - offset][s].shift == doingShifted) { // We have found a key that matches. if (!Keys.empty()) @@ -496,3 +668,55 @@ static std::string GetKeysUsed() return Keys; } + +/****************************************************************************/ + +void FillAssignedKeysCount() +{ + std::fill(std::begin(assignedKeysCount), std::end(assignedKeysCount), 0); + + KeyPair* table; + int start, end; + int offset; // First Vkey code in table + + if (!doingJoystick) + { + table = UserKeymap; + offset = 0; + start = 1; + end = 256; + } + else + { + table = JoystickMap; + offset = BEEB_VKEY_JOY_START; + start = BEEB_VKEY_JOY_START; + end = BEEB_VKEY_JOY_END; + } + + for (int i = start; i < end; i++) + { + KeyPair& pair = table[i - offset]; + for (int s = 0; s < 2; s++) + { + UpdateAssignedKeysCount(pair[s].row, pair[s].col, +1); + } + } +} + +/****************************************************************************/ + +static void UpdateAssignedKeysCount(int row, int col, int change, bool redrawColour) +{ + const BBCKey& key = GetBBCKeyByRowAndCol(row, col); + int index = GetBBCKeyIndex(key); + if (index < _countof(BBCKeys)) + { + assignedKeysCount[index] += change; + if (redrawColour && key.ctrlId != selectedCtrlID) + { + HWND keyCtrl = GetDlgItem(hwndUserKeyboard, key.ctrlId); + SetKeyColour(GetKeyColour(key.ctrlId), keyCtrl); + } + } +} diff --git a/Src/userkybd.h b/Src/userkybd.h index fcc47152..cadc6262 100644 --- a/Src/userkybd.h +++ b/Src/userkybd.h @@ -25,8 +25,20 @@ Boston, MA 02110-1301, USA. // Public declarations. -bool UserKeyboardDialog(HWND hwndParent); +struct BBCKey +{ + int ctrlId; + const char* name; + int row; + int column; +}; + +const BBCKey& GetBBCKeyByName(const std::string& name); +const BBCKey& GetBBCKeyByRowAndCol(int row, int col); + +bool UserKeyboardDialog(HWND hwndParent, bool joystick); extern KeyMap UserKeymap; +extern JoyMap JoystickMap; #endif From 9e82e9dc7c89b397fbdec8d633401eef7c10f3c0 Mon Sep 17 00:00:00 2001 From: Tadeusz Kijkowski Date: Tue, 2 Feb 2021 00:25:01 +0100 Subject: [PATCH 02/38] Separate fire buttons on BBC joystick Map first two PC joystick or gamepad fire buttons to two BBC joystick buttons separately. Fixes #74 --- Src/beebwin.cpp | 7 ++++--- Src/beebwin.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index 42f4f64d..5e3ac21f 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -1294,10 +1294,10 @@ void BeebWin::InitJoystick(void) } /****************************************************************************/ -void BeebWin::SetJoystickButton(bool button) +void BeebWin::SetJoystickButton(int index, bool button) { if (m_MenuIdSticks == IDM_JOYSTICK) - JoystickButton[0] = button; + JoystickButton[index] = button; } /****************************************************************************/ @@ -1879,7 +1879,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle case MM_JOY1BUTTONDOWN: case MM_JOY1BUTTONUP: - mainWin->SetJoystickButton(((UINT)uParam & (JOY_BUTTON1 | JOY_BUTTON2)) != 0); + mainWin->SetJoystickButton(0, ((UINT)uParam & JOY_BUTTON1) != 0); + mainWin->SetJoystickButton(1, ((UINT)uParam & JOY_BUTTON2) != 0); break; case MM_JOY2MOVE: diff --git a/Src/beebwin.h b/Src/beebwin.h index bc093dde..7f946f18 100644 --- a/Src/beebwin.h +++ b/Src/beebwin.h @@ -248,7 +248,7 @@ class BeebWin { bool IsWindowMinimized() const; void DisplayClientAreaText(HDC hdc); void DisplayFDCBoardInfo(HDC hDC, int x, int y); - void SetJoystickButton(bool button); + void SetJoystickButton(int index, bool button); void ScaleJoystick(unsigned int x, unsigned int y); unsigned int GetJoystickAxes(JOYCAPS& caps, unsigned int deadband, JOYINFOEX& joyInfoEx); void TranslateOrSendKey(int vkey, bool keyUp); From abbbe535f47e39a5d4af6c368d43f4b6bec5a92c Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Tue, 2 Feb 2021 12:48:17 +0100 Subject: [PATCH 03/38] Fixed typo in JoystickMap.txt --- Documents/JoystickMap.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documents/JoystickMap.txt b/Documents/JoystickMap.txt index 2958b6a0..d52c51c3 100644 --- a/Documents/JoystickMap.txt +++ b/Documents/JoystickMap.txt @@ -21,7 +21,7 @@ # Special BBC key names are: # LEFT, RIGHT, UP, DOWN, BREAK, COPY, DELETE, CAPS-LOCK, TAB, CTRL -# SPACE, RETURN, ESCACE, SHIFT, SHIFT-LOCK +# SPACE, RETURN, ESCAPE, SHIFT, SHIFT-LOCK # Allowed aliases: # DEL, CAPS, CAPSLOCK, CONTROL, ESC, SHIFTLOCK From c34f5b774d801609abe7ce1f96cea6bb90bc391d Mon Sep 17 00:00:00 2001 From: Chris Needham Date: Tue, 2 Feb 2021 21:45:09 +0000 Subject: [PATCH 04/38] Fixed joystick map file dialog filter string --- Src/beebwinio.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/beebwinio.cpp b/Src/beebwinio.cpp index 6f5bf5b2..11b344f6 100644 --- a/Src/beebwinio.cpp +++ b/Src/beebwinio.cpp @@ -1195,7 +1195,7 @@ void BeebWin::LoadJoystickMap() { char DefaultPath[_MAX_PATH]; char FileName[_MAX_PATH]; - const char* filter = "Joystick Mapping File (*.jmap)\0*.jmap\0"; + const char* filter = "Joystick Map File (*.jmap)\0*.jmap\0"; // If Autoload is enabled, look for joystick mapping near disks, // otherwise start in user data path. @@ -1233,7 +1233,7 @@ void BeebWin::SaveJoystickMap() char DefaultPath[_MAX_PATH]; // Add space for .jmap exstension char FileName[_MAX_PATH + 5]; - const char* filter = "Key Map File (*.jmap)\0*.jmap\0"; + const char* filter = "Joystick Map File (*.jmap)\0*.jmap\0"; // If Autoload is enabled, store joystick mapping near disks, // otherwise start in user data path. From dce19b4889b8e9ee635129e7347c859fa1277ad2 Mon Sep 17 00:00:00 2001 From: Chris Needham Date: Tue, 2 Feb 2021 22:08:43 +0000 Subject: [PATCH 05/38] Fix typo in Help file Also replace -> with right-arrow symbol --- Help/keyboard.html | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Help/keyboard.html b/Help/keyboard.html index d27f57ec..4f6b432c 100644 --- a/Help/keyboard.html +++ b/Help/keyboard.html @@ -10,7 +10,7 @@ @@ -204,7 +204,7 @@

Custom Key Mappings

  1. If you are in full screen mode then switch back to Windowed mode.
  2. -
  3. Select menu item "Options -> Define User Key Mapping". A graphic +
  4. Select menu item "Options → Define User Key Mapping". A graphic showing the BBC keyboard layout will appear within the BeebEm interface.
  5. @@ -228,11 +228,11 @@

    Custom Key Mappings

  6. Repeat from step 3 for other keys you want to map.
  7. -
  8. Save your mapping using menu item "Options -> Save User Key Mapping". +
  9. Save your mapping using menu item "Options → Save User Key Mapping". You can write over the default user key mapping file (DefaultUser.kmap) or save a new file.
  10. -
  11. Select your mapping using menu item "Options -> User Defined Mapping". +
  12. Select your mapping using menu item "Options → User Defined Mapping". You can also use the "Save Preferences" option to save the default user key mapping file that gets loaded when BeebEm starts up.
@@ -240,7 +240,7 @@

Custom Key Mappings

Mapping PC Joystick to BBC Keys

To enable translating PC joystick actions to BBC keys, use menu item - "Options -> Joystick To Keyboard -> Enable Joystick To Keyboard". + "Options → Joystick To Keyboard → Enable Joystick To Keyboard". BeemEm supports mapping up to two joysticks or gamepads to BBC keys, with each joystick actions (button presses or stick movements) mapped separately.

@@ -251,7 +251,7 @@

Mapping PC Joystick to BBC Keys

  1. If you are in full screen mode then switch back to Windowed mode.
  2. -
  3. Select menu item "Options -> Joystick To Keybord -> Define Joystick Mapping". +
  4. Select menu item "Options → Joystick To Keyboard → Define Joystick Mapping". A graphic showing the BBC keyboard layout will appear within the BeebEm interface. BBC keys which already have assigned joystick actions will be highlighted with light blue colour.
  5. @@ -272,18 +272,18 @@

    Mapping PC Joystick to BBC Keys

  6. Repeat from step 3 for other keys you want to map.
  7. Save your mapping using menu item - "Options -> Joystick To Keybord -> Save Joystick Mapping...". + "Options → Joystick To Keyboard → Save Joystick Mapping...". You can save your mapping as the default user joystick mapping file (DefaultUser.jmap) or save to a different file. If you are creating a game-specific joystick mapping, you can save the mapping in the same directory and with the same name as your game disk image, with extension changed to ".jmap". This will make BeebEm automatically use the joystick mapping for that disk image, if that is enabled with menu item - "Options -> Joystick To Keybord -> Autoload Joystick Mapping".
  8. + "Options → Joystick To Keyboard → Autoload Joystick Mapping".

As with PC keys, you can map joystick actions to BBC keys in shifted and - unshifted state separately. The most common scenario is to assign a + unshifted state separately. The most common scenario is to assign a joystick action to the same BBC key in both shifted and unshifted state. To do that, just click on the BBC key that you want to map, and move joystick or press button twice - once for unshifted and second time for @@ -314,10 +314,10 @@

Mapping PC Joystick to BBC Keys

file was created by user. If this file is not present, all joystick actions start unassigned.

-

Menu item "Options -> Joystick To Keybord -> Autoload Joystick Mapping" +

Menu item "Options → Joystick To Keyboard → Autoload Joystick Mapping" toggles Autoload Joystick Mapping option. If this option is enabled, BeebEm will automatically look for joystick mapping file based on name of image - file started from "File -> Run Disc.." or "File -> Load Tape..." menu item, + file started from "File → Run Disc.." or "File → Load Tape..." menu item, or from command-line. The sought mapping file has the same name and directory as loaded image, but extension changed to ".jmap". If no such file is found, joystick mapping is reset to default - either empty, or that From 050f8606d8bc5a235eab77b2d59818722ba7f54f Mon Sep 17 00:00:00 2001 From: Chris Needham Date: Thu, 4 Feb 2021 20:27:41 +0000 Subject: [PATCH 06/38] Refactored to remove code duplication Moved SetDlgItemChecked() and IsDlgItemChecked() to new files, Dialog.cpp and Dialog.h --- Src/BeebEm.vcxproj | 2 ++ Src/BeebEm.vcxproj.filters | 6 ++++++ Src/Dialog.cpp | 33 ++++++++++++++++++++++++++++ Src/Dialog.h | 27 +++++++++++++++++++++++ Src/SelectKeyDialog.cpp | 43 ++++++++++++++----------------------- Src/UserPortBreakoutBox.cpp | 5 +++-- Src/debug.cpp | 11 +--------- 7 files changed, 88 insertions(+), 39 deletions(-) create mode 100644 Src/Dialog.cpp create mode 100644 Src/Dialog.h diff --git a/Src/BeebEm.vcxproj b/Src/BeebEm.vcxproj index 18b45f89..ed032c12 100644 --- a/Src/BeebEm.vcxproj +++ b/Src/BeebEm.vcxproj @@ -187,6 +187,7 @@ + @@ -331,6 +332,7 @@ + diff --git a/Src/BeebEm.vcxproj.filters b/Src/BeebEm.vcxproj.filters index 55fc2c5f..86d4a7b5 100644 --- a/Src/BeebEm.vcxproj.filters +++ b/Src/BeebEm.vcxproj.filters @@ -243,6 +243,9 @@ Source Files + + Source Files + @@ -488,6 +491,9 @@ Header Files + + Header Files + diff --git a/Src/Dialog.cpp b/Src/Dialog.cpp new file mode 100644 index 00000000..93e0265b --- /dev/null +++ b/Src/Dialog.cpp @@ -0,0 +1,33 @@ +/**************************************************************** +BeebEm - BBC Micro and Master 128 Emulator +Copyright (C) 2021 Chris Needham + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +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. + +You should have received a copy of the GNU General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. +****************************************************************/ + +#include + +#include "Dialog.h" + +bool IsDlgItemChecked(HWND hDlg, int nIDDlgItem) +{ + return SendDlgItemMessage(hDlg, nIDDlgItem, BM_GETCHECK, 0, 0) == BST_CHECKED; +} + +void SetDlgItemChecked(HWND hDlg, int nIDDlgItem, bool checked) +{ + SendDlgItemMessage(hDlg, nIDDlgItem, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0); +} diff --git a/Src/Dialog.h b/Src/Dialog.h new file mode 100644 index 00000000..f16d0e8c --- /dev/null +++ b/Src/Dialog.h @@ -0,0 +1,27 @@ +/**************************************************************** +BeebEm - BBC Micro and Master 128 Emulator +Copyright (C) 2021 Chris Needham + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +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. + +You should have received a copy of the GNU General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. +****************************************************************/ + +#ifndef DIALOG_HEADER +#define DIALOG_HEADER + +bool IsDlgItemChecked(HWND hDlg, int nIDDlgItem); +void SetDlgItemChecked(HWND hDlg, int nIDDlgItem, bool checked); + +#endif diff --git a/Src/SelectKeyDialog.cpp b/Src/SelectKeyDialog.cpp index c43d0642..08da5dc2 100644 --- a/Src/SelectKeyDialog.cpp +++ b/Src/SelectKeyDialog.cpp @@ -26,13 +26,11 @@ Boston, MA 02110-1301, USA. #include "main.h" #include "beebemrc.h" #include "SelectKeyDialog.h" +#include "Dialog.h" #include "Messages.h" /****************************************************************************/ -static bool IsDlgItemChecked(HWND hDlg, int nIDDlgItem); -static void DlgItemCheck(HWND hDlg, int nIDDlgItem, bool checked); - SelectKeyDialog* selectKeyDialog; /****************************************************************************/ @@ -109,13 +107,13 @@ INT_PTR SelectKeyDialog::DlgProc( // If the selected keys is empty (as opposed to "Not assigned"), we are currently unassigning. // Hide the "Assigned to:" label if (m_SelectedKey.empty()) - SetDlgItemText(m_hwnd, IDC_ASSIGNED_KEYS_LBL, ""); + SetDlgItemText(m_hwnd, IDC_ASSIGNED_KEYS_LBL, ""); else if (m_Joystick) - SetDlgItemText(m_hwnd, IDC_ASSIGNED_KEYS_LBL, "Assigned to:"); + SetDlgItemText(m_hwnd, IDC_ASSIGNED_KEYS_LBL, "Assigned to:"); // If doing shifted key, start with the Shift checkbox checked because that's most likely // what the user wants - DlgItemCheck(m_hwnd, IDC_SHIFT, m_Shift); + SetDlgItemChecked(m_hwnd, IDC_SHIFT, m_Shift); return TRUE; @@ -186,15 +184,15 @@ bool SelectKeyDialog::HandleMessage(const MSG& msg) if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) { int key = (int)msg.wParam; - if (!m_Joystick && key < 256 - || m_Joystick && key >= BEEB_VKEY_JOY_START && key < BEEB_VKEY_JOY_END) + + if (!m_Joystick && key < 256 || + m_Joystick && key >= BEEB_VKEY_JOY_START && key < BEEB_VKEY_JOY_END) { m_Key = key; m_Shift = IsDlgItemChecked(m_hwnd, IDC_SHIFT); Close(IDOK); return true; } - } return false; @@ -216,20 +214,6 @@ bool SelectKeyDialog::Shift() const /****************************************************************************/ -static bool IsDlgItemChecked(HWND hDlg, int nIDDlgItem) -{ - return SendDlgItemMessage(hDlg, nIDDlgItem, BM_GETCHECK, 0, 0) == BST_CHECKED; -} - -/****************************************************************************/ - -static void DlgItemCheck(HWND hDlg, int nIDDlgItem, bool checked) -{ - SendDlgItemMessage(hDlg, nIDDlgItem, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0); -} - -/****************************************************************************/ - LPCSTR SelectKeyDialog::KeyName(int Key) { static CHAR Character[2]; // Used to return single characters. @@ -237,7 +221,9 @@ LPCSTR SelectKeyDialog::KeyName(int Key) if (Key >= BEEB_VKEY_JOY_START && Key < BEEB_VKEY_JOY_END) { static CHAR Name[16]; // Buffer for joystick button or axis name + Key -= BEEB_VKEY_JOY_START; + if (Key >= BEEB_VKEY_JOY2_AXES - BEEB_VKEY_JOY1_AXES) { strcpy(Name, "Joy2"); @@ -276,6 +262,7 @@ LPCSTR SelectKeyDialog::KeyName(int Key) { sprintf(Name + strlen(Name), "Btn%d", Key - (BEEB_VKEY_JOY1_BTN1 - BEEB_VKEY_JOY1_AXES) + 1); } + return Name; } @@ -351,18 +338,20 @@ LPCSTR SelectKeyDialog::KeyName(int Key) } } -static -std::string toupper(const std::string& src) +static std::string toupper(const std::string& src) { std::string result = src; + for (auto& c : result) - c = toupper(c); + { + c = static_cast(toupper(c)); + } + return result; } int SelectKeyDialog::JoyVKeyByName(const char* Name) { - using vkeyMapType = std::map; // Construct map on first use by lambda diff --git a/Src/UserPortBreakoutBox.cpp b/Src/UserPortBreakoutBox.cpp index 4b842ad8..78a8153e 100644 --- a/Src/UserPortBreakoutBox.cpp +++ b/Src/UserPortBreakoutBox.cpp @@ -26,6 +26,7 @@ Boston, MA 02110-1301, USA. #include "main.h" #include "Messages.h" #include "SelectKeyDialog.h" +#include "Dialog.h" #include "uservia.h" /****************************************************************************/ @@ -325,14 +326,14 @@ INT_PTR UserPortBreakoutDialog::DlgProc( bool UserPortBreakoutDialog::GetValue(int ctrlID) { - return SendDlgItemMessage(m_hwnd, ctrlID, BM_GETCHECK, 0, 0) == BST_CHECKED; + return IsDlgItemChecked(m_hwnd, ctrlID); } /****************************************************************************/ void UserPortBreakoutDialog::SetValue(int ctrlID, bool State) { - SendDlgItemMessage(m_hwnd, ctrlID, BM_SETCHECK, State ? 1 : 0, 0); + SetDlgItemChecked(m_hwnd, ctrlID, State); } /****************************************************************************/ diff --git a/Src/debug.cpp b/Src/debug.cpp index 6f2114d4..28c63069 100644 --- a/Src/debug.cpp +++ b/Src/debug.cpp @@ -45,6 +45,7 @@ Boston, MA 02110-1301, USA. #include "debug.h" #include "z80mem.h" #include "z80.h" +#include "Dialog.h" #include "FileDialog.h" #include "StringUtils.h" #include "DebugTrace.h" @@ -988,16 +989,6 @@ static const InstInfo* GetOpcodeTable(bool host) } } -static bool IsDlgItemChecked(HWND hDlg, int nIDDlgItem) -{ - return SendDlgItemMessage(hDlg, nIDDlgItem, BM_GETCHECK, 0, 0) == BST_CHECKED; -} - -static void SetDlgItemChecked(HWND hDlg, int nIDDlgItem, bool checked) -{ - SendDlgItemMessage(hDlg, nIDDlgItem, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0); -} - void DebugOpenDialog(HINSTANCE hinst, HWND /* hwndMain */) { if (hwndInvisibleOwner == 0) From 6b05829c511724c2864adf520f406e635d3cf0eb Mon Sep 17 00:00:00 2001 From: Chris Needham Date: Thu, 4 Feb 2021 22:17:58 +0000 Subject: [PATCH 07/38] Renamed variables and simplified code --- Src/beebwin.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index 5e3ac21f..543e312c 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -1379,14 +1379,15 @@ unsigned int BeebWin::GetJoystickAxes(JOYCAPS& caps, unsigned int deadband, JOYI /****************************************************************************/ void BeebWin::TranslateOrSendKey(int vkey, bool keyUp) { - int row, col; - if (m_JoystickTarget == NULL) + if (m_JoystickTarget == nullptr) { + int row, col; TranslateKey(vkey, keyUp, row, col); } else if (!keyUp) { - // Keyboard input dialog is visible - translate button down to key down message and send to dialog + // Keyboard input dialog is visible - translate button down to key down + // message and send to dialog PostMessage(m_JoystickTarget, WM_KEYDOWN, vkey, 0); } } @@ -1394,18 +1395,16 @@ void BeebWin::TranslateOrSendKey(int vkey, bool keyUp) /****************************************************************************/ void BeebWin::TranslateJoystickMove(int joyId, JOYINFOEX& joyInfoEx) { - const int axNum = JOYSTICK_AXES_COUNT; - JOYCAPS& caps = (joyId == 1) ? m_Joystick2Caps : m_JoystickCaps; unsigned int deadband = (joyId == 1) ? m_Joystick2Deadband : m_Joystick1Deadband; int& prevAxes = (joyId == 1) ? m_Joystick2PrevAxes : m_Joystick1PrevAxes; int vkeys = (joyId == 1) ? BEEB_VKEY_JOY2_AXES : BEEB_VKEY_JOY1_AXES; - unsigned int axes = mainWin->GetJoystickAxes(caps, deadband, joyInfoEx); + unsigned int axes = GetJoystickAxes(caps, deadband, joyInfoEx); if (axes != prevAxes) { - for (int axId = 0; axId < axNum; ++axId) + for (int axId = 0; axId < JOYSTICK_AXES_COUNT; ++axId) { if ((axes & ~prevAxes) & (1 << axId)) { @@ -1416,6 +1415,7 @@ void BeebWin::TranslateJoystickMove(int joyId, JOYINFOEX& joyInfoEx) TranslateOrSendKey(vkeys + axId, true); } } + prevAxes = axes; } } @@ -1423,14 +1423,14 @@ void BeebWin::TranslateJoystickMove(int joyId, JOYINFOEX& joyInfoEx) /****************************************************************************/ void BeebWin::TranslateJoystickButtons(int joyId, unsigned int buttons) { - const int btnNum = 32; + const int BUTTON_COUNT = 32; int& prevBtns = (joyId == 1) ? m_Joystick2PrevBtns : m_Joystick1PrevBtns; int vkeys = (joyId == 1) ? BEEB_VKEY_JOY2_BTN1 : BEEB_VKEY_JOY1_BTN1; if (buttons != prevBtns) { - for (int btnId = 0; btnId < btnNum; ++btnId) + for (int btnId = 0; btnId < BUTTON_COUNT; ++btnId) { if ((buttons & ~prevBtns) & (1 << btnId)) { @@ -1441,6 +1441,7 @@ void BeebWin::TranslateJoystickButtons(int joyId, unsigned int buttons) TranslateOrSendKey(vkeys + btnId, true); } } + prevBtns = buttons; } } @@ -1448,15 +1449,16 @@ void BeebWin::TranslateJoystickButtons(int joyId, unsigned int buttons) /****************************************************************************/ void BeebWin::TranslateJoystick(int joyId) { - if (mainWin->m_JoystickToKeys) + if (m_JoystickToKeys) { JOYINFOEX joyInfoEx; joyInfoEx.dwSize = sizeof(joyInfoEx); joyInfoEx.dwFlags = JOY_RETURNALL | JOY_RETURNPOVCTS; + if (!joyGetPosEx(joyId, &joyInfoEx)) { - mainWin->TranslateJoystickMove(joyId, joyInfoEx); - mainWin->TranslateJoystickButtons(joyId, joyInfoEx.dwButtons); + TranslateJoystickMove(joyId, joyInfoEx); + TranslateJoystickButtons(joyId, joyInfoEx.dwButtons); } } } From 5511881712e3c754e2e71cce03cb224ca006e779 Mon Sep 17 00:00:00 2001 From: Chris Needham Date: Thu, 4 Feb 2021 22:23:45 +0000 Subject: [PATCH 08/38] Removed unnecessary struct typedef --- Src/beebwin.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/beebwin.h b/Src/beebwin.h index 7f946f18..777312f9 100644 --- a/Src/beebwin.h +++ b/Src/beebwin.h @@ -84,11 +84,11 @@ Boston, MA 02110-1301, USA. #define UNASSIGNED_ROW -9 -typedef struct KeyMapping { +struct KeyMapping { int row; // Beeb row int col; // Beeb col bool shift; // Beeb shift state -} KeyMapping; +}; typedef KeyMapping KeyPair[2]; typedef KeyPair KeyMap[256]; // Indices are: [Virt key][shift state] From 7620530a98d7550fc59be5895a72fa0fbd4852d9 Mon Sep 17 00:00:00 2001 From: Tadeusz Kijkowski Date: Fri, 5 Feb 2021 03:07:01 +0100 Subject: [PATCH 09/38] Code cleanup after review - Moved joystick state to JoystickState struct - Removed ResetJoystick - Fixed some signedness warnings - Unmute when keyboard mapping dialog is closed --- Src/beebwin.cpp | 89 ++++++++++++++------------------------------ Src/beebwin.h | 22 +++++------ Src/beebwinio.cpp | 5 +-- Src/beebwinprefs.cpp | 12 +++--- Src/userkybd.h | 2 +- 5 files changed, 46 insertions(+), 84 deletions(-) diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index 543e312c..eddad4fc 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -205,13 +205,13 @@ BeebWin::BeebWin() m_DXSmoothMode7Only = false; m_DXResetPending = false; - m_JoystickCaptured = false; - m_Joystick2Captured = false; m_JoystickTarget = NULL; - m_Joystick1PrevAxes = 0; - m_Joystick1PrevBtns = 0; - m_Joystick2PrevAxes = 0; - m_Joystick2PrevBtns = 0; + for (int i = 0; i < 2; ++i) + { + m_JoystickState[i].Captured = false; + m_JoystickState[i].PrevAxes = 0; + m_JoystickState[i].PrevBtns = 0; + } m_customip[0] = 0; m_customport = 0; m_isFullScreen = false; @@ -1244,8 +1244,8 @@ void BeebWin::SetRomMenu(void) void BeebWin::MaybeEnableInitJoystick(void) { - bool enableIt = ((m_MenuIdSticks == IDM_JOYSTICK) && !m_JoystickCaptured - || m_JoystickToKeys && (!m_JoystickCaptured || !m_Joystick2Captured)); + bool enableIt = ((m_MenuIdSticks == IDM_JOYSTICK) && !m_JoystickState[0].Captured + || m_JoystickToKeys && (!m_JoystickState[0].Captured || !m_JoystickState[1].Captured)); EnableMenuItem(IDM_INIT_JOYSTICK, enableIt); } @@ -1256,24 +1256,24 @@ void BeebWin::InitJoystick(void) MMRESULT mmresult = JOYERR_NOERROR; MMRESULT mmresult2; - if ((m_MenuIdSticks == IDM_JOYSTICK || m_JoystickToKeys) && !m_JoystickCaptured) + if ((m_MenuIdSticks == IDM_JOYSTICK || m_JoystickToKeys) && !m_JoystickState[0].Captured) { /* Get joystick updates 20 times a second */ mmresult = joySetCapture(m_hWnd, JOYSTICKID1, 50, FALSE); if (mmresult == JOYERR_NOERROR) - mmresult = joyGetDevCaps(JOYSTICKID1, &m_JoystickCaps, sizeof(JOYCAPS)); + mmresult = joyGetDevCaps(JOYSTICKID1, &m_JoystickState[0].Caps, sizeof(JOYCAPS)); if (mmresult == JOYERR_NOERROR) - m_JoystickCaptured = true; + m_JoystickState[0].Captured = true; } - if (m_JoystickToKeys && !m_Joystick2Captured) + if (m_JoystickToKeys && !m_JoystickState[1].Captured) { /* Get joystick updates 20 times a second */ mmresult2 = joySetCapture(m_hWnd, JOYSTICKID2, 50, FALSE); if (mmresult2 == JOYERR_NOERROR) - mmresult2 = joyGetDevCaps(JOYSTICKID2, &m_Joystick2Caps, sizeof(JOYCAPS)); + mmresult2 = joyGetDevCaps(JOYSTICKID2, &m_JoystickState[1].Caps, sizeof(JOYCAPS)); if (mmresult2 == JOYERR_NOERROR) - m_Joystick2Captured = true; + m_JoystickState[1].Captured = true; } if (mmresult == JOYERR_NOERROR) @@ -1303,18 +1303,19 @@ void BeebWin::SetJoystickButton(int index, bool button) /****************************************************************************/ void BeebWin::ScaleJoystick(unsigned int x, unsigned int y) { + JOYCAPS& caps = m_JoystickState[0].Caps; if (m_MenuIdSticks == IDM_JOYSTICK) { /* Scale and reverse the readings */ - JoystickX = (int)((double)(m_JoystickCaps.wXmax - x) * 65535.0 / - (double)(m_JoystickCaps.wXmax - m_JoystickCaps.wXmin)); - JoystickY = (int)((double)(m_JoystickCaps.wYmax - y) * 65535.0 / - (double)(m_JoystickCaps.wYmax - m_JoystickCaps.wYmin)); + JoystickX = (int)((double)(caps.wXmax - x) * 65535.0 / + (double)(caps.wXmax - caps.wXmin)); + JoystickY = (int)((double)(caps.wYmax - y) * 65535.0 / + (double)(caps.wYmax - caps.wYmin)); } } /****************************************************************************/ -unsigned int BeebWin::GetJoystickAxes(JOYCAPS& caps, unsigned int deadband, JOYINFOEX& joyInfoEx) +unsigned int BeebWin::GetJoystickAxes(JOYCAPS& caps, int deadband, JOYINFOEX& joyInfoEx) { unsigned int axes = 0; @@ -1395,9 +1396,9 @@ void BeebWin::TranslateOrSendKey(int vkey, bool keyUp) /****************************************************************************/ void BeebWin::TranslateJoystickMove(int joyId, JOYINFOEX& joyInfoEx) { - JOYCAPS& caps = (joyId == 1) ? m_Joystick2Caps : m_JoystickCaps; - unsigned int deadband = (joyId == 1) ? m_Joystick2Deadband : m_Joystick1Deadband; - int& prevAxes = (joyId == 1) ? m_Joystick2PrevAxes : m_Joystick1PrevAxes; + JOYCAPS& caps = m_JoystickState[joyId].Caps; + unsigned int deadband = m_JoystickState[joyId].Deadband; + unsigned int& prevAxes = m_JoystickState[joyId].PrevAxes; int vkeys = (joyId == 1) ? BEEB_VKEY_JOY2_AXES : BEEB_VKEY_JOY1_AXES; unsigned int axes = GetJoystickAxes(caps, deadband, joyInfoEx); @@ -1425,7 +1426,7 @@ void BeebWin::TranslateJoystickButtons(int joyId, unsigned int buttons) { const int BUTTON_COUNT = 32; - int& prevBtns = (joyId == 1) ? m_Joystick2PrevBtns : m_Joystick1PrevBtns; + unsigned int& prevBtns = m_JoystickState[joyId].PrevBtns; int vkeys = (joyId == 1) ? BEEB_VKEY_JOY2_BTN1 : BEEB_VKEY_JOY1_BTN1; if (buttons != prevBtns) @@ -1463,33 +1464,6 @@ void BeebWin::TranslateJoystick(int joyId) } } -/****************************************************************************/ -void BeebWin::ResetJoystick(void) -{ - // joySetCapture() fails after a joyReleaseCapture() call (not sure why) - // so leave joystick captured. - // joyReleaseCapture(JOYSTICKID1); - - // It still doesn't work - // Don't really release joystick if mapping is enabled - //if (!m_JoystickToKeyboard) - //{ - // if (m_JoystickCaptured) - // { - // if (joyReleaseCapture(JOYSTICKID1) == JOYERR_NOERROR) - // m_JoystickCaptured = false; - // } - // if (m_Joystick2Captured) - // { - // if (joyReleaseCapture(JOYSTICKID2) == JOYERR_NOERROR) - // m_Joystick2Captured = false; - // } - //} - - // Don't disable AtoD here, we may want to reset joystick, but keep - // mousestick active -} - /****************************************************************************/ void BeebWin::SetMousestickButton(int index, bool button) { @@ -3580,10 +3554,6 @@ void BeebWin::HandleCommand(int MenuId) { CheckMenuItem(m_MenuIdSticks, false); - if (m_MenuIdSticks == IDM_JOYSTICK) - { - ResetJoystick(); - } AtoDDisable(); } @@ -3625,10 +3595,6 @@ void BeebWin::HandleCommand(int MenuId) { InitJoystick(); } - else if (m_MenuIdSticks != IDM_JOYSTICK) - { - ResetJoystick(); - } CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); MaybeEnableInitJoystick(); break; @@ -4510,11 +4476,10 @@ void BeebWin::UserKeyboardDialogClosed() if (m_JoystickToKeys && !m_J2KWasEnabled) { m_JoystickToKeys = m_J2KWasEnabled; - if (m_MenuIdSticks != IDM_JOYSTICK) - { - ResetJoystick(); - } } + + // Unmute + SetSound(SoundState::Unmuted); } /*****************************************************************************/ diff --git a/Src/beebwin.h b/Src/beebwin.h index 777312f9..163f6561 100644 --- a/Src/beebwin.h +++ b/Src/beebwin.h @@ -162,6 +162,14 @@ enum class MessageType { Info }; +struct JoystickState { + JOYCAPS Caps; + unsigned int Deadband; + bool Captured; + unsigned int PrevAxes; + unsigned int PrevBtns; +}; + class BeebWin { public: @@ -250,7 +258,7 @@ class BeebWin { void DisplayFDCBoardInfo(HDC hDC, int x, int y); void SetJoystickButton(int index, bool button); void ScaleJoystick(unsigned int x, unsigned int y); - unsigned int GetJoystickAxes(JOYCAPS& caps, unsigned int deadband, JOYINFOEX& joyInfoEx); + unsigned int GetJoystickAxes(JOYCAPS& caps, int deadband, JOYINFOEX& joyInfoEx); void TranslateOrSendKey(int vkey, bool keyUp); void TranslateJoystickMove(int joyId, JOYINFOEX& joyInfoEx); void TranslateJoystickButtons(int joyId, unsigned int buttons); @@ -354,16 +362,7 @@ class BeebWin { int m_MenuIdVolume; int m_MenuIdTiming; int m_FPSTarget; - bool m_JoystickCaptured; - JOYCAPS m_JoystickCaps; - unsigned int m_Joystick1Deadband; - int m_Joystick1PrevAxes; - int m_Joystick1PrevBtns; - bool m_Joystick2Captured; - JOYCAPS m_Joystick2Caps; - unsigned int m_Joystick2Deadband; - int m_Joystick2PrevAxes; - int m_Joystick2PrevBtns; + JoystickState m_JoystickState[2]; int m_MenuIdSticks; bool m_JoystickToKeys; bool m_AutoloadJoystickMap; @@ -574,7 +573,6 @@ class BeebWin { void Load1770DiscImage(const char *FileName, int Drive, DiscType Type); void LoadTape(void); void InitJoystick(void); - void ResetJoystick(void); void RestoreState(void); void SaveState(void); void NewDiscImage(int Drive); diff --git a/Src/beebwinio.cpp b/Src/beebwinio.cpp index 11b344f6..9da6695a 100644 --- a/Src/beebwinio.cpp +++ b/Src/beebwinio.cpp @@ -1364,14 +1364,13 @@ void BeebWin::ResetJoyMap(JoyMap* joymap) } /****************************************************************************/ -static -void makeupper(char* str) +static void makeupper(char* str) { if (str == NULL) return; while (*str) { - *str = toupper(*str); + *str = static_cast(toupper(*str)); ++str; } } diff --git a/Src/beebwinprefs.cpp b/Src/beebwinprefs.cpp index 9abbd5fb..cd64c2ef 100644 --- a/Src/beebwinprefs.cpp +++ b/Src/beebwinprefs.cpp @@ -272,14 +272,14 @@ void BeebWin::LoadPreferences() m_AutoloadJoystickMap = false; if (m_Preferences.GetDWORDValue(CFG_OPTIONS_STICK1_DEADBAND, dword)) - m_Joystick1Deadband = dword; + m_JoystickState[0].Deadband = dword; else - m_Joystick1Deadband = DEFAULT_JOY_DEADBAND; + m_JoystickState[0].Deadband = DEFAULT_JOY_DEADBAND; if (m_Preferences.GetDWORDValue(CFG_OPTIONS_STICK2_DEADBAND, dword)) - m_Joystick2Deadband = dword; + m_JoystickState[1].Deadband = dword; else - m_Joystick2Deadband = DEFAULT_JOY_DEADBAND; + m_JoystickState[1].Deadband = DEFAULT_JOY_DEADBAND; if (!m_Preferences.GetBoolValue(CFG_OPTIONS_FREEZEINACTIVE, m_FreezeWhenInactive)) m_FreezeWhenInactive = true; @@ -668,8 +668,8 @@ void BeebWin::SavePreferences(bool saveAll) m_Preferences.SetDWORDValue(CFG_OPTIONS_STICKS, m_MenuIdSticks); m_Preferences.SetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys); m_Preferences.SetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, m_AutoloadJoystickMap); - m_Preferences.SetDWORDValue(CFG_OPTIONS_STICK1_DEADBAND, m_Joystick1Deadband); - m_Preferences.SetDWORDValue(CFG_OPTIONS_STICK2_DEADBAND, m_Joystick2Deadband); + m_Preferences.SetDWORDValue(CFG_OPTIONS_STICK1_DEADBAND, m_JoystickState[0].Deadband); + m_Preferences.SetDWORDValue(CFG_OPTIONS_STICK2_DEADBAND, m_JoystickState[1].Deadband); m_Preferences.SetBoolValue(CFG_OPTIONS_FREEZEINACTIVE, m_FreezeWhenInactive); m_Preferences.SetBoolValue(CFG_OPTIONS_HIDE_CURSOR, m_HideCursor); diff --git a/Src/userkybd.h b/Src/userkybd.h index cadc6262..3c559604 100644 --- a/Src/userkybd.h +++ b/Src/userkybd.h @@ -27,7 +27,7 @@ Boston, MA 02110-1301, USA. struct BBCKey { - int ctrlId; + unsigned int ctrlId; const char* name; int row; int column; From 8b2642c1fe98fc4973561892eb6320df9e75e030 Mon Sep 17 00:00:00 2001 From: Chris Needham Date: Fri, 5 Feb 2021 22:22:45 +0000 Subject: [PATCH 10/38] Some more updates - Simplfied code a bit - Added CaptureJoystick() function to remove duplication - Renamed MaybeEnableInitJoystick() function for consistency --- Src/beebwin.cpp | 109 +++++++++++++++++++++++++++++++----------------- Src/beebwin.h | 5 ++- 2 files changed, 73 insertions(+), 41 deletions(-) diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index eddad4fc..99468237 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -205,13 +205,17 @@ BeebWin::BeebWin() m_DXSmoothMode7Only = false; m_DXResetPending = false; - m_JoystickTarget = NULL; + m_JoystickTarget = nullptr; + for (int i = 0; i < 2; ++i) { + memset(&m_JoystickState[i].Caps, 0, sizeof(m_JoystickState[i])); + m_JoystickState[i].Deadband = false; m_JoystickState[i].Captured = false; m_JoystickState[i].PrevAxes = 0; m_JoystickState[i].PrevBtns = 0; } + m_customip[0] = 0; m_customport = 0; m_isFullScreen = false; @@ -415,7 +419,7 @@ void BeebWin::ApplyPrefs() /* Joysticks can only be initialised after the window is created (needs hwnd) */ InitJoystick(); - MaybeEnableInitJoystick(); + UpdateInitJoystickMenu(); LoadFDC(NULL, true); RTCInit(); @@ -1242,57 +1246,78 @@ void BeebWin::SetRomMenu(void) /****************************************************************************/ -void BeebWin::MaybeEnableInitJoystick(void) +void BeebWin::UpdateInitJoystickMenu() { - bool enableIt = ((m_MenuIdSticks == IDM_JOYSTICK) && !m_JoystickState[0].Captured - || m_JoystickToKeys && (!m_JoystickState[0].Captured || !m_JoystickState[1].Captured)); - EnableMenuItem(IDM_INIT_JOYSTICK, enableIt); + bool Enable = false; + + if (m_MenuIdSticks == IDM_JOYSTICK && !m_JoystickState[0].Captured) + { + Enable = true; + } + else if (m_JoystickToKeys && (!m_JoystickState[0].Captured || !m_JoystickState[1].Captured)) + { + Enable = true; + } + + EnableMenuItem(IDM_INIT_JOYSTICK, Enable); } /****************************************************************************/ -void BeebWin::InitJoystick(void) +bool BeebWin::InitJoystick() { - MMRESULT mmresult = JOYERR_NOERROR; - MMRESULT mmresult2; + bool Success = false; if ((m_MenuIdSticks == IDM_JOYSTICK || m_JoystickToKeys) && !m_JoystickState[0].Captured) { - /* Get joystick updates 20 times a second */ - mmresult = joySetCapture(m_hWnd, JOYSTICKID1, 50, FALSE); - if (mmresult == JOYERR_NOERROR) - mmresult = joyGetDevCaps(JOYSTICKID1, &m_JoystickState[0].Caps, sizeof(JOYCAPS)); - if (mmresult == JOYERR_NOERROR) - m_JoystickState[0].Captured = true; + Success = CaptureJoystick(0); } - if (m_JoystickToKeys && !m_JoystickState[1].Captured) + if (Success && m_JoystickToKeys && !m_JoystickState[1].Captured) { - /* Get joystick updates 20 times a second */ - mmresult2 = joySetCapture(m_hWnd, JOYSTICKID2, 50, FALSE); - if (mmresult2 == JOYERR_NOERROR) - mmresult2 = joyGetDevCaps(JOYSTICKID2, &m_JoystickState[1].Caps, sizeof(JOYCAPS)); - if (mmresult2 == JOYERR_NOERROR) - m_JoystickState[1].Captured = true; + Success = CaptureJoystick(1); } - if (mmresult == JOYERR_NOERROR) + return Success; +} + +bool BeebWin::CaptureJoystick(int Index) +{ + static const int Joystick[] = { JOYSTICKID1, JOYSTICKID2 }; + + const int JoystickId = Joystick[Index]; + + // Get joystick updates 20 times a second + MMRESULT Result = joySetCapture(m_hWnd, JoystickId, 50, FALSE); + + if (Result == JOYERR_NOERROR) { - if (m_MenuIdSticks == IDM_JOYSTICK) - AtoDEnable(); + Result = joyGetDevCaps(JoystickId, &m_JoystickState[Index].Caps, sizeof(JOYCAPS)); } - else if (mmresult == JOYERR_UNPLUGGED) + + if (Result == JOYERR_NOERROR) + { + m_JoystickState[Index].Captured = true; + } + else if (Result == JOYERR_UNPLUGGED) { - MessageBox(m_hWnd, "Joystick is not plugged in", - WindowTitle, MB_OK|MB_ICONWARNING); + char str[100]; + sprintf(str, "Joystick %d is not plugged in", Index + 1); + + MessageBox(m_hWnd, str, WindowTitle, MB_OK | MB_ICONWARNING); } else { - MessageBox(m_hWnd, "Failed to initialise the joystick", - WindowTitle, MB_OK|MB_ICONWARNING); + char str[100]; + sprintf(str, "Failed to initialise joystick %d", Index + 1); + + MessageBox(m_hWnd, str, WindowTitle, MB_OK | MB_ICONWARNING); } + + return Result == JOYERR_NOERROR; } + /****************************************************************************/ void BeebWin::SetJoystickButton(int index, bool button) { @@ -1303,14 +1328,15 @@ void BeebWin::SetJoystickButton(int index, bool button) /****************************************************************************/ void BeebWin::ScaleJoystick(unsigned int x, unsigned int y) { - JOYCAPS& caps = m_JoystickState[0].Caps; if (m_MenuIdSticks == IDM_JOYSTICK) { - /* Scale and reverse the readings */ + JOYCAPS& caps = m_JoystickState[0].Caps; + + // Scale and reverse the readings JoystickX = (int)((double)(caps.wXmax - x) * 65535.0 / - (double)(caps.wXmax - caps.wXmin)); + (double)(caps.wXmax - caps.wXmin)); JoystickY = (int)((double)(caps.wYmax - y) * 65535.0 / - (double)(caps.wYmax - caps.wYmin)); + (double)(caps.wYmax - caps.wYmin)); } } @@ -3581,22 +3607,27 @@ void BeebWin::HandleCommand(int MenuId) else m_MenuIdSticks = 0; } - MaybeEnableInitJoystick(); + UpdateInitJoystickMenu(); break; case IDM_INIT_JOYSTICK: InitJoystick(); - MaybeEnableInitJoystick(); + UpdateInitJoystickMenu(); break; case IDM_JOYSTICK_TO_KEYS: m_JoystickToKeys = !m_JoystickToKeys; if (m_JoystickToKeys) { - InitJoystick(); + bool Success = InitJoystick(); + + if (Success) + { + CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); + } } - CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); - MaybeEnableInitJoystick(); + + UpdateInitJoystickMenu(); break; case IDM_AUTOLOADJOYMAP: diff --git a/Src/beebwin.h b/Src/beebwin.h index 163f6561..d816da89 100644 --- a/Src/beebwin.h +++ b/Src/beebwin.h @@ -572,7 +572,8 @@ class BeebWin { int ReadDisc(int Drive, bool bCheckForPrefs); void Load1770DiscImage(const char *FileName, int Drive, DiscType Type); void LoadTape(void); - void InitJoystick(void); + bool InitJoystick(); + bool CaptureJoystick(int Index); void RestoreState(void); void SaveState(void); void NewDiscImage(int Drive); @@ -622,7 +623,7 @@ class BeebWin { void ResetJoyMap(JoyMap* joymap); bool ReadJoyMap(const char *filename, JoyMap *joymap); bool WriteJoyMap(const char *filename, JoyMap *joymap); - void MaybeEnableInitJoystick(void); + void UpdateInitJoystickMenu(); void Report(MessageType type, const char *format, ...); From d663bf7a936b3d7a4d66cb5cdd156d1c6c66dd72 Mon Sep 17 00:00:00 2001 From: Tadeusz Kijkowski Date: Sat, 6 Feb 2021 02:27:26 +0100 Subject: [PATCH 11/38] Fixed enabling and disabling Joystick and Joystick to Keyboard --- Src/beebwin.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index 99468237..2e5763d8 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -209,8 +209,8 @@ BeebWin::BeebWin() for (int i = 0; i < 2; ++i) { - memset(&m_JoystickState[i].Caps, 0, sizeof(m_JoystickState[i])); - m_JoystickState[i].Deadband = false; + memset(&m_JoystickState[i].Caps, 0, sizeof(m_JoystickState[i].Caps)); + m_JoystickState[i].Deadband = 0; m_JoystickState[i].Captured = false; m_JoystickState[i].PrevAxes = 0; m_JoystickState[i].PrevBtns = 0; @@ -1266,13 +1266,18 @@ void BeebWin::UpdateInitJoystickMenu() bool BeebWin::InitJoystick() { - bool Success = false; + bool Success = true; if ((m_MenuIdSticks == IDM_JOYSTICK || m_JoystickToKeys) && !m_JoystickState[0].Captured) { Success = CaptureJoystick(0); } + if (Success && m_MenuIdSticks == IDM_JOYSTICK) + { + AtoDEnable(); + } + if (Success && m_JoystickToKeys && !m_JoystickState[1].Captured) { Success = CaptureJoystick(1); @@ -3626,6 +3631,10 @@ void BeebWin::HandleCommand(int MenuId) CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); } } + else + { + CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); + } UpdateInitJoystickMenu(); break; From d280d5929cd13a9da6e24adb7c0bb5b3a1b3a592 Mon Sep 17 00:00:00 2001 From: Chris Needham Date: Sun, 7 Feb 2021 16:01:01 +0000 Subject: [PATCH 12/38] Changed wording in JoystickMap.txt --- Documents/JoystickMap.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documents/JoystickMap.txt b/Documents/JoystickMap.txt index d52c51c3..9cc12e78 100644 --- a/Documents/JoystickMap.txt +++ b/Documents/JoystickMap.txt @@ -4,7 +4,7 @@ # All empty lines and lines starting with '#' are ignored. # Remaining lines must consist of two or three tokens separated with -# whitespaces. +# whitespace. # All names are case insensitive except for the marker in the first line. # Two-token lines are: @@ -28,8 +28,8 @@ # All other BBC keys are named by their unshifted character (i.e. 'SH+[' not '{') # Function keys are named, as one would expect, 'F0' to 'F9' -# Joystick input have form Joy[X][Act] where [X] is joystick number and -# [Act] is action name. Axis and button assignments differ between +# Joystick inputs have the form Joy[X][Act] where [X] is the joystick number +# and [Act] is the action name. Axis and button assignments differ between # joystick or gamepad models. # Xbox 360 controller has following assignments @@ -38,7 +38,7 @@ # Axis3+,Axis3- - dwZ - Left Trigger, Right Trigger # Axis4-,Axis4+,Axis5-,Axis5+ - dwR, dwU - Right Analog Up, Down, Left, Right # Axis7-,Axis7+,Axis8-,Axis8+ - dwPOV - Hat Up, Down, Left, Right -# Btn1-4 - A, B, X, Y +# Btn1-4 - A, B, X, Y # Btn5,6 - Left Shoulder, Right Shoulder # Btn7,8 - Select, Start # Btn9,10 - Left Thumbstick, Right Thumbstick From 9e979def453978e4fab59900a979f3575d1874f1 Mon Sep 17 00:00:00 2001 From: Chris Needham Date: Sun, 7 Feb 2021 16:13:07 +0000 Subject: [PATCH 13/38] Renamed variable Long names are OK, and this makes it easier to search the code for related variables --- Src/beebwin.cpp | 15 +++++++++------ Src/beebwin.h | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index 2e5763d8..3451e393 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -145,13 +145,15 @@ BeebWin::BeebWin() m_KeyMapFunc = false; m_ShiftPressed = 0; m_ShiftBooted = false; - for (int k = 0; k < 256; ++k) + + for (int k = 0; k < BEEB_VKEY_COUNT; ++k) { m_vkeyPressed[k][0][0] = -1; m_vkeyPressed[k][1][0] = -1; m_vkeyPressed[k][0][1] = -1; m_vkeyPressed[k][1][1] = -1; } + m_DisableKeysWindows = false; m_DisableKeysBreak = false; m_DisableKeysEscape = false; @@ -193,6 +195,7 @@ BeebWin::BeebWin() m_EmuPaused = false; m_StartPaused = false; m_WasPaused = false; + m_JoystickToKeysWasEnabled = false; m_KeyboardTimerElapsed = false; m_BootDiscTimerElapsed = false; memset(m_ClipboardBuffer, 0, sizeof(m_ClipboardBuffer)); @@ -4469,7 +4472,7 @@ void BeebWin::OpenUserKeyboardDialog() m_WasPaused = mainWin->IsPaused(); - m_J2KWasEnabled = m_JoystickToKeys; + m_JoystickToKeysWasEnabled = m_JoystickToKeys; if (!m_WasPaused) { @@ -4491,9 +4494,9 @@ void BeebWin::OpenJoystickMapDialog() } // Enable mapping to capture keys in dialog - m_J2KWasEnabled = m_JoystickToKeys; + m_JoystickToKeysWasEnabled = m_JoystickToKeys; - if (!m_J2KWasEnabled) + if (!m_JoystickToKeysWasEnabled) { m_JoystickToKeys = true; InitJoystick(); @@ -4513,9 +4516,9 @@ void BeebWin::UserKeyboardDialogClosed() } // Restore joystick state - if (m_JoystickToKeys && !m_J2KWasEnabled) + if (m_JoystickToKeys && !m_JoystickToKeysWasEnabled) { - m_JoystickToKeys = m_J2KWasEnabled; + m_JoystickToKeys = m_JoystickToKeysWasEnabled; } // Unmute diff --git a/Src/beebwin.h b/Src/beebwin.h index d816da89..ce3d3720 100644 --- a/Src/beebwin.h +++ b/Src/beebwin.h @@ -460,7 +460,7 @@ class BeebWin { bool m_EmuPaused; bool m_StartPaused; bool m_WasPaused; - bool m_J2KWasEnabled; + bool m_JoystickToKeysWasEnabled; bool m_AutoBootDisc; bool m_KeyboardTimerElapsed; bool m_BootDiscTimerElapsed; From 3ede0c7a8d8363889fab5ed1ba2a153f05ba1579 Mon Sep 17 00:00:00 2001 From: Chris Needham Date: Sun, 7 Feb 2021 16:15:40 +0000 Subject: [PATCH 14/38] Added const --- Src/SelectKeyDialog.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Src/SelectKeyDialog.cpp b/Src/SelectKeyDialog.cpp index 08da5dc2..2529b88a 100644 --- a/Src/SelectKeyDialog.cpp +++ b/Src/SelectKeyDialog.cpp @@ -352,14 +352,16 @@ static std::string toupper(const std::string& src) int SelectKeyDialog::JoyVKeyByName(const char* Name) { - using vkeyMapType = std::map; + using VKeyMapType = std::map; // Construct map on first use by lambda - static vkeyMapType nameToVKeyMap = []() { - vkeyMapType keyMap{}; + static const VKeyMapType nameToVKeyMap = []() { + VKeyMapType keyMap{}; for (int vkey = BEEB_VKEY_JOY_START; vkey < BEEB_VKEY_JOY_END; ++vkey) + { keyMap[toupper(KeyName(vkey))] = vkey; + } return keyMap; }(); From 146ef1783572bbb9727ee030e1e0e92015a71241 Mon Sep 17 00:00:00 2001 From: Tadeusz Kijkowski Date: Sun, 7 Feb 2021 19:50:32 +0100 Subject: [PATCH 15/38] More consts - Maps from ... to BBCKey are now const - Functions GetBBCKeyBy... return pointer instead of reference --- Src/beebwinio.cpp | 38 +++++++++++++++++++------------------- Src/userkybd.cpp | 46 +++++++++++++++++++++++----------------------- Src/userkybd.h | 4 ++-- 3 files changed, 44 insertions(+), 44 deletions(-) diff --git a/Src/beebwinio.cpp b/Src/beebwinio.cpp index 9da6695a..73008353 100644 --- a/Src/beebwinio.cpp +++ b/Src/beebwinio.cpp @@ -1437,8 +1437,8 @@ bool BeebWin::ReadJoyMap(const char *filename, JoyMap *joymap) keyName1 += 3; } - const BBCKey& key1 = GetBBCKeyByName(keyName1); - if (key1.row == UNASSIGNED_ROW && strcmp(keyName1, "NONE") != 0) + const BBCKey* key1 = GetBBCKeyByName(keyName1); + if (key1->row == UNASSIGNED_ROW && strcmp(keyName1, "NONE") != 0) { char errstr[500]; sprintf(errstr, "Invalid key name in joystick mapping file:\n %s\n line %d\n", @@ -1451,8 +1451,8 @@ bool BeebWin::ReadJoyMap(const char *filename, JoyMap *joymap) if (keyName2 == NULL) { // Shifted and unshifted input map to the same key - mapping[0].row = mapping[1].row = key1.row; - mapping[0].col = mapping[1].col = key1.column; + mapping[0].row = mapping[1].row = key1->row; + mapping[0].col = mapping[1].col = key1->column; mapping[0].shift = false; mapping[1].shift = true; } @@ -1467,8 +1467,8 @@ bool BeebWin::ReadJoyMap(const char *filename, JoyMap *joymap) keyName2 += 3; } - const BBCKey& key2 = GetBBCKeyByName(keyName2); - if (key2.row == UNASSIGNED_ROW && strcmp(keyName2, "NONE") != 0) + const BBCKey* key2 = GetBBCKeyByName(keyName2); + if (key2->row == UNASSIGNED_ROW && strcmp(keyName2, "NONE") != 0) { char errstr[500]; sprintf(errstr, "Invalid key name in joystick mapping file:\n %s\n line %d\n", @@ -1478,11 +1478,11 @@ bool BeebWin::ReadJoyMap(const char *filename, JoyMap *joymap) break; } - mapping[0].row = key1.row; - mapping[0].col = key1.column; + mapping[0].row = key1->row; + mapping[0].col = key1->column; mapping[0].shift = shift1; - mapping[1].row = key2.row; - mapping[1].col = key2.column; + mapping[1].row = key2->row; + mapping[1].col = key2->column; mapping[1].shift = shift2; } } @@ -1563,33 +1563,33 @@ bool BeebWin::WriteJoyMap(const char *filename, JoyMap *joymap) if (mapping[0].row != UNASSIGNED_ROW || mapping[1].row != UNASSIGNED_ROW) { - auto inputName = SelectKeyDialog::KeyName(i + BEEB_VKEY_JOY_START); + const char* inputName = SelectKeyDialog::KeyName(i + BEEB_VKEY_JOY_START); if (mapping[0].row == mapping[1].row && mapping[0].col == mapping[1].col && !mapping[0].shift && mapping[1].shift) { // Shifted and unshifted input map to the same key - write one name - const auto& key = GetBBCKeyByRowAndCol(mapping[0].row, mapping[0].col); + const BBCKey* key = GetBBCKeyByRowAndCol(mapping[0].row, mapping[0].col); fprintf(outfile, "%-13s %s\n", inputName, - key.name); + key->name); } else { // Separate mapping for shifted and unshifted - const auto& key1 = GetBBCKeyByRowAndCol(mapping[0].row, mapping[0].col); - auto key1shift = mapping[0].shift && mapping[0].row != UNASSIGNED_ROW; - const auto& key2 = GetBBCKeyByRowAndCol(mapping[1].row, mapping[1].col); - auto key2shift = mapping[1].shift && mapping[1].row != UNASSIGNED_ROW; + const BBCKey* key1 = GetBBCKeyByRowAndCol(mapping[0].row, mapping[0].col); + bool key1shift = mapping[0].shift && mapping[0].row != UNASSIGNED_ROW; + const BBCKey* key2 = GetBBCKeyByRowAndCol(mapping[1].row, mapping[1].col); + bool key2shift = mapping[1].shift && mapping[1].row != UNASSIGNED_ROW; fprintf(outfile, "%-13s %s%-*s %s%s\n", inputName, key1shift ? "SH+" : "", // Align for longest possible: "SHIFT-LOCK+SH" key1shift ? 10 : 13, - key1.name, + key1->name, key2shift ? "SH+" : "", - key2.name); + key2->name); } } } diff --git a/Src/userkybd.cpp b/Src/userkybd.cpp index 8e8f1cd0..89dfa6ff 100644 --- a/Src/userkybd.cpp +++ b/Src/userkybd.cpp @@ -47,7 +47,7 @@ static COLORREF GetKeyColour(UINT ctrlID); static std::string GetKeysUsed(); static void FillAssignedKeysCount(); static void UpdateAssignedKeysCount(int row, int col, int change, bool redrawColour = false); -static int GetBBCKeyIndex(const BBCKey& key); +static int GetBBCKeyIndex(const BBCKey* key); // Colour used to highlight the selected key. static const COLORREF HighlightColour = 0x00FF0080; // Purple @@ -189,12 +189,12 @@ static int assignedKeysCount[_countof(BBCKeys)] = {}; /****************************************************************************/ -const BBCKey& GetBBCKeyByResId(int ctrlId) +const BBCKey* GetBBCKeyByResId(int ctrlId) { using resIdToKeyMapType = std::map; // Construct map on first use by lambda - static resIdToKeyMapType resIdToKeyMap = []() + static const resIdToKeyMapType resIdToKeyMap = []() { resIdToKeyMapType keyMap{}; @@ -205,19 +205,19 @@ const BBCKey& GetBBCKeyByResId(int ctrlId) auto iter = resIdToKeyMap.find(ctrlId); if (iter == resIdToKeyMap.end()) - return BBCKeys[0]; + return &BBCKeys[0]; - return *iter->second; + return iter->second; } /****************************************************************************/ -const BBCKey& GetBBCKeyByName(const std::string& name) +const BBCKey* GetBBCKeyByName(const std::string& name) { using nameToKeyMapType = std::map; // Construct map on first use by lambda - static nameToKeyMapType nameToKeyMap = []() + static const nameToKeyMapType nameToKeyMap = []() { nameToKeyMapType keyMap{}; @@ -232,20 +232,20 @@ const BBCKey& GetBBCKeyByName(const std::string& name) auto iter = nameToKeyMap.find(name); if (iter == nameToKeyMap.end()) - return BBCKeys[0]; + return &BBCKeys[0]; - return *iter->second; + return iter->second; } /****************************************************************************/ -const BBCKey& GetBBCKeyByRowAndCol(int row, int col) +const BBCKey* GetBBCKeyByRowAndCol(int row, int col) { using posPair = std::pair; using posToKeyMapType = std::map; // Construct map on first use by lambda - static posToKeyMapType posToKeyMap = []() + static const posToKeyMapType posToKeyMap = []() { posToKeyMapType keyMap{}; @@ -256,17 +256,17 @@ const BBCKey& GetBBCKeyByRowAndCol(int row, int col) auto iter = posToKeyMap.find(posPair{ row, col }); if (iter == posToKeyMap.end()) - return BBCKeys[0]; + return &BBCKeys[0]; - return *iter->second; + return iter->second; } /****************************************************************************/ -// Get index for assignedKeysCount. The 'key' parameter must be reference to +// Get index for assignedKeysCount. The 'key' parameter must be a pointer to // item in BBCKeys table, not a copy of it. -int GetBBCKeyIndex(const BBCKey& key) +int GetBBCKeyIndex(const BBCKey* key) { - int index = &key - BBCKeys; + int index = key - BBCKeys; if (index >= 0 && index < _countof(BBCKeys)) return index; return 0; @@ -373,9 +373,9 @@ static void SetBBCKeyForVKEY(int Key, bool Shift) static void SetRowCol(UINT ctrlID) { - const BBCKey& key = GetBBCKeyByResId(ctrlID); - BBCRow = key.row; - BBCCol = key.column; + const BBCKey* key = GetBBCKeyByResId(ctrlID); + BBCRow = key->row; + BBCCol = key->column; } /****************************************************************************/ @@ -708,15 +708,15 @@ void FillAssignedKeysCount() static void UpdateAssignedKeysCount(int row, int col, int change, bool redrawColour) { - const BBCKey& key = GetBBCKeyByRowAndCol(row, col); + const BBCKey* key = GetBBCKeyByRowAndCol(row, col); int index = GetBBCKeyIndex(key); if (index < _countof(BBCKeys)) { assignedKeysCount[index] += change; - if (redrawColour && key.ctrlId != selectedCtrlID) + if (redrawColour && key->ctrlId != selectedCtrlID) { - HWND keyCtrl = GetDlgItem(hwndUserKeyboard, key.ctrlId); - SetKeyColour(GetKeyColour(key.ctrlId), keyCtrl); + HWND keyCtrl = GetDlgItem(hwndUserKeyboard, key->ctrlId); + SetKeyColour(GetKeyColour(key->ctrlId), keyCtrl); } } } diff --git a/Src/userkybd.h b/Src/userkybd.h index 3c559604..617b9cbb 100644 --- a/Src/userkybd.h +++ b/Src/userkybd.h @@ -33,8 +33,8 @@ struct BBCKey int column; }; -const BBCKey& GetBBCKeyByName(const std::string& name); -const BBCKey& GetBBCKeyByRowAndCol(int row, int col); +const BBCKey* GetBBCKeyByName(const std::string& name); +const BBCKey* GetBBCKeyByRowAndCol(int row, int col); bool UserKeyboardDialog(HWND hwndParent, bool joystick); From 5cf12f91191e93810c4ce3013b318c559df6288a Mon Sep 17 00:00:00 2001 From: Tadeusz Kijkowski Date: Sun, 7 Feb 2021 20:05:35 +0100 Subject: [PATCH 16/38] Update checkmark in menu even if joystick initialization failed --- Src/beebwin.cpp | 57 ++++++++++++++++++------------------------------- 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index 3451e393..e636f085 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -1276,11 +1276,6 @@ bool BeebWin::InitJoystick() Success = CaptureJoystick(0); } - if (Success && m_MenuIdSticks == IDM_JOYSTICK) - { - AtoDEnable(); - } - if (Success && m_JoystickToKeys && !m_JoystickState[1].Captured) { Success = CaptureJoystick(1); @@ -1291,35 +1286,35 @@ bool BeebWin::InitJoystick() bool BeebWin::CaptureJoystick(int Index) { - static const int Joystick[] = { JOYSTICKID1, JOYSTICKID2 }; - - const int JoystickId = Joystick[Index]; - - // Get joystick updates 20 times a second - MMRESULT Result = joySetCapture(m_hWnd, JoystickId, 50, FALSE); + // Get joystick updates 20 times a second. + // There's no need to use JOYSTICKID constants. + MMRESULT Result = joySetCapture(m_hWnd, Index, 50, FALSE); if (Result == JOYERR_NOERROR) { - Result = joyGetDevCaps(JoystickId, &m_JoystickState[Index].Caps, sizeof(JOYCAPS)); + Result = joyGetDevCaps(Index, &m_JoystickState[Index].Caps, sizeof(JOYCAPS)); } if (Result == JOYERR_NOERROR) { m_JoystickState[Index].Captured = true; } - else if (Result == JOYERR_UNPLUGGED) + else if (Index == 0) { - char str[100]; - sprintf(str, "Joystick %d is not plugged in", Index + 1); + if (Result == JOYERR_UNPLUGGED) + { + char str[100]; + sprintf(str, "Joystick %d is not plugged in", Index + 1); - MessageBox(m_hWnd, str, WindowTitle, MB_OK | MB_ICONWARNING); - } - else - { - char str[100]; - sprintf(str, "Failed to initialise joystick %d", Index + 1); + MessageBox(m_hWnd, str, WindowTitle, MB_OK | MB_ICONWARNING); + } + else + { + char str[100]; + sprintf(str, "Failed to initialise joystick %d", Index + 1); - MessageBox(m_hWnd, str, WindowTitle, MB_OK | MB_ICONWARNING); + MessageBox(m_hWnd, str, WindowTitle, MB_OK | MB_ICONWARNING); + } } return Result == JOYERR_NOERROR; @@ -3605,10 +3600,7 @@ void BeebWin::HandleCommand(int MenuId) { InitJoystick(); } - else /* mousestick */ - { - AtoDEnable(); - } + AtoDEnable(); if (JoystickEnabled) CheckMenuItem(m_MenuIdSticks, true); @@ -3627,18 +3619,11 @@ void BeebWin::HandleCommand(int MenuId) m_JoystickToKeys = !m_JoystickToKeys; if (m_JoystickToKeys) { - bool Success = InitJoystick(); - - if (Success) - { - CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); - } - } - else - { - CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); + InitJoystick(); } + CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); + UpdateInitJoystickMenu(); break; From 7b30aef78751a28013d00627d7e7103bd3e7f6ea Mon Sep 17 00:00:00 2001 From: Tadeusz Kijkowski Date: Sun, 7 Feb 2021 20:36:35 +0100 Subject: [PATCH 17/38] Checkmark next to 'Initialise Joystick' menu item - Display checkmark next to 'Initialise Joystick' menu item if first joystick initialised OK. - Display warning message only if explicit initialisation failed. --- Src/BeebEm.rc | 2 +- Src/beebwin.cpp | 32 ++++++++++++++++++++------------ Src/beebwin.h | 4 ++-- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/Src/BeebEm.rc b/Src/BeebEm.rc index 5866e6ef..b955666f 100644 --- a/Src/BeebEm.rc +++ b/Src/BeebEm.rc @@ -722,7 +722,7 @@ BEGIN MENUITEM "Load Joystick Mapping...", IDM_LOADJOYMAP MENUITEM "Save Joystick Mapping...", IDM_SAVEJOYMAP END - MENUITEM "Reinitialize Joystick", IDM_INIT_JOYSTICK + MENUITEM "Initialise Joystick", IDM_INIT_JOYSTICK MENUITEM SEPARATOR MENUITEM "&Freeze when inactive", IDM_FREEZEINACTIVE MENUITEM "&Hide Cursor", IDM_HIDECURSOR diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index e636f085..5649ceb3 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -421,7 +421,7 @@ void BeebWin::ApplyPrefs() PrinterDisable(); /* Joysticks can only be initialised after the window is created (needs hwnd) */ - InitJoystick(); + InitJoystick(false); UpdateInitJoystickMenu(); LoadFDC(NULL, true); @@ -1263,28 +1263,29 @@ void BeebWin::UpdateInitJoystickMenu() } EnableMenuItem(IDM_INIT_JOYSTICK, Enable); + CheckMenuItem(IDM_INIT_JOYSTICK, m_JoystickState[0].Captured); } /****************************************************************************/ -bool BeebWin::InitJoystick() +bool BeebWin::InitJoystick(bool verbose) { bool Success = true; if ((m_MenuIdSticks == IDM_JOYSTICK || m_JoystickToKeys) && !m_JoystickState[0].Captured) { - Success = CaptureJoystick(0); + Success = CaptureJoystick(0, verbose); } if (Success && m_JoystickToKeys && !m_JoystickState[1].Captured) { - Success = CaptureJoystick(1); + Success = CaptureJoystick(1, verbose); } return Success; } -bool BeebWin::CaptureJoystick(int Index) +bool BeebWin::CaptureJoystick(int Index, bool verbose) { // Get joystick updates 20 times a second. // There's no need to use JOYSTICKID constants. @@ -1299,7 +1300,7 @@ bool BeebWin::CaptureJoystick(int Index) { m_JoystickState[Index].Captured = true; } - else if (Index == 0) + else if (verbose) { if (Result == JOYERR_UNPLUGGED) { @@ -1485,11 +1486,18 @@ void BeebWin::TranslateJoystick(int joyId) joyInfoEx.dwSize = sizeof(joyInfoEx); joyInfoEx.dwFlags = JOY_RETURNALL | JOY_RETURNPOVCTS; - if (!joyGetPosEx(joyId, &joyInfoEx)) + if (joyGetPosEx(joyId, &joyInfoEx) == JOYERR_NOERROR) { TranslateJoystickMove(joyId, joyInfoEx); TranslateJoystickButtons(joyId, joyInfoEx.dwButtons); } + else + { + // Joystick read failed - unplugged? + // Reset 'captured' flag and update menu entry + m_JoystickState[joyId].Captured = false; + UpdateInitJoystickMenu(); + } } } @@ -3598,7 +3606,7 @@ void BeebWin::HandleCommand(int MenuId) if (m_MenuIdSticks == IDM_JOYSTICK) { - InitJoystick(); + InitJoystick(false); } AtoDEnable(); @@ -3611,7 +3619,7 @@ void BeebWin::HandleCommand(int MenuId) break; case IDM_INIT_JOYSTICK: - InitJoystick(); + InitJoystick(true); UpdateInitJoystickMenu(); break; @@ -3619,7 +3627,7 @@ void BeebWin::HandleCommand(int MenuId) m_JoystickToKeys = !m_JoystickToKeys; if (m_JoystickToKeys) { - InitJoystick(); + InitJoystick(false); } CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); @@ -4484,7 +4492,7 @@ void BeebWin::OpenJoystickMapDialog() if (!m_JoystickToKeysWasEnabled) { m_JoystickToKeys = true; - InitJoystick(); + InitJoystick(false); } UserKeyboardDialog(m_hWnd, true); @@ -4713,7 +4721,7 @@ void BeebWin::CheckForLocalPrefs(const char *path, bool bLoadPrefs) HandleCommand(m_DisplayRenderer); InitMenu(); SetWindowText(m_hWnd, WindowTitle); - InitJoystick(); + InitJoystick(false); } } diff --git a/Src/beebwin.h b/Src/beebwin.h index ce3d3720..042c560f 100644 --- a/Src/beebwin.h +++ b/Src/beebwin.h @@ -572,8 +572,8 @@ class BeebWin { int ReadDisc(int Drive, bool bCheckForPrefs); void Load1770DiscImage(const char *FileName, int Drive, DiscType Type); void LoadTape(void); - bool InitJoystick(); - bool CaptureJoystick(int Index); + bool InitJoystick(bool verbose = false); + bool CaptureJoystick(int Index, bool verbose); void RestoreState(void); void SaveState(void); void NewDiscImage(int Drive); From 5e2f85c449056d3f1cf49ab04fa844b1c4515619 Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Sat, 20 Feb 2021 19:12:22 +0100 Subject: [PATCH 18/38] Added second BBC joystick support - Added second BBC joystick support - Select PC joystick and axes for each BBC joystick --- Src/BeebEm.rc | 27 +- Src/atodconv.cpp | 52 ++-- Src/atodconv.h | 12 +- Src/beebemrc.h | 11 + Src/beebwin.cpp | 609 +++++++++++++++++++++++++++++++++--------- Src/beebwin.h | 34 ++- Src/beebwinprefs.cpp | 28 +- Src/serialdevices.cpp | 4 +- 8 files changed, 613 insertions(+), 164 deletions(-) diff --git a/Src/BeebEm.rc b/Src/BeebEm.rc index b955666f..008083c4 100644 --- a/Src/BeebEm.rc +++ b/Src/BeebEm.rc @@ -709,9 +709,28 @@ BEGIN END POPUP "&Options" BEGIN - MENUITEM "&Joystick", IDM_JOYSTICK - MENUITEM "&Analogue Mousestick", IDM_ANALOGUE_MOUSESTICK - MENUITEM "&Digital Mousestick", IDM_DIGITAL_MOUSESTICK + POPUP "Joystick" + BEGIN + MENUITEM "First PC &Joystick", IDM_JOYSTICK + MENUITEM "&Analogue Mousestick", IDM_ANALOGUE_MOUSESTICK + MENUITEM "&Digital Mousestick", IDM_DIGITAL_MOUSESTICK + MENUITEM "Second PC Joystick", IDM_JOY1_PCJOY2 + MENUITEM SEPARATOR + MENUITEM "Primary Stick", IDM_JOY1_PRIMARY + MENUITEM "Right Thumbstick (XBox)", IDM_JOY1_SECONDARY1 + MENUITEM "Right Thumbstick (other)", IDM_JOY1_SECONDARY2 + END + POPUP "Second Joystick" + BEGIN + MENUITEM "Second PC Joystick", IDM_JOY2_PCJOY2 + MENUITEM "&Analogue Mousestick", IDM_JOY2_ANALOGUE_MOUSESTICK + MENUITEM "&Digital Mousestick", IDM_JOY2_DIGITAL_MOUSESTICK + MENUITEM "First PC Joystick", IDM_JOY2_PCJOY1 + MENUITEM SEPARATOR + MENUITEM "Primary Stick", IDM_JOY2_PRIMARY + MENUITEM "Right Thumbstick (XBox)", IDM_JOY2_SECONDARY1 + MENUITEM "Right Thumbstick (other)", IDM_JOY2_SECONDARY2 + END POPUP "Joystick To Keyboard" BEGIN MENUITEM "Enable Joystick To Keyboard", IDM_JOYSTICK_TO_KEYS @@ -722,7 +741,7 @@ BEGIN MENUITEM "Load Joystick Mapping...", IDM_LOADJOYMAP MENUITEM "Save Joystick Mapping...", IDM_SAVEJOYMAP END - MENUITEM "Initialise Joystick", IDM_INIT_JOYSTICK + MENUITEM "Initialise Joysticks", IDM_INIT_JOYSTICK MENUITEM SEPARATOR MENUITEM "&Freeze when inactive", IDM_FREEZEINACTIVE MENUITEM "&Hide Cursor", IDM_HIDECURSOR diff --git a/Src/atodconv.cpp b/Src/atodconv.cpp index d8367576..18af2a8d 100644 --- a/Src/atodconv.cpp +++ b/Src/atodconv.cpp @@ -32,11 +32,11 @@ Boston, MA 02110-1301, USA. #include "sysvia.h" #include "uefstate.h" -bool JoystickEnabled = false; +bool JoystickEnabled[2] = { false, false }; -/* X and Y positions for joystick 1 */ -int JoystickX; -int JoystickY; +/* X and Y positions for joystick 1 and 2 */ +int JoystickX[2]; +int JoystickY[2]; /* A to D state */ typedef struct AtoDStateT{ @@ -107,14 +107,19 @@ void AtoD_poll_real(void) switch (AtoDState.status & 3) { case 0: - value = JoystickX; + value = JoystickX[0]; break; case 1: - value = JoystickY; + value = JoystickY[0]; + break; + case 2: + value = JoystickX[1]; + break; + case 3: + value = JoystickY[1]; break; default: value = 0; - break; } AtoDState.status |= (value & 0xc000)>>10; @@ -123,7 +128,7 @@ void AtoD_poll_real(void) } /*--------------------------------------------------------------------------*/ -void AtoDInit(void) +void AtoDInit(int index) { AtoDState.datalatch = 0; AtoDState.high = 0; @@ -131,8 +136,8 @@ void AtoDInit(void) ClearTrigger(AtoDTrigger); /* Move joystick to middle */ - JoystickX = 32767; - JoystickY = 32767; + JoystickX[index] = 32767; + JoystickY[index] = 32767; /* Not busy, conversion complete (OS1.2 will then request another conversion) */ AtoDState.status = 0x40; @@ -140,25 +145,28 @@ void AtoDInit(void) } /*--------------------------------------------------------------------------*/ -void AtoDEnable(void) +void AtoDEnable(int index) { - JoystickEnabled = true; - AtoDInit(); + JoystickEnabled[index] = true; + AtoDInit(index); } /*--------------------------------------------------------------------------*/ -void AtoDDisable(void) +void AtoDDisable(int index) { - JoystickEnabled = false; - AtoDState.datalatch = 0; - AtoDState.status = 0x80; /* busy, conversion not complete */ - AtoDState.high = 0; - AtoDState.low = 0; - ClearTrigger(AtoDTrigger); + JoystickEnabled[index] = false; + if (!JoystickEnabled[0] && !JoystickEnabled[1]) + { + AtoDState.datalatch = 0; + AtoDState.status = 0x80; /* busy, conversion not complete */ + AtoDState.high = 0; + AtoDState.low = 0; + ClearTrigger(AtoDTrigger); + } /* Move joystick to middle (superpool looks at joystick even when not selected) */ - JoystickX = 32767; - JoystickY = 32767; + JoystickX[index] = 32767; + JoystickY[index] = 32767; } /*--------------------------------------------------------------------------*/ diff --git a/Src/atodconv.h b/Src/atodconv.h index 2ff34998..76686195 100644 --- a/Src/atodconv.h +++ b/Src/atodconv.h @@ -24,15 +24,15 @@ Boston, MA 02110-1301, USA. #ifndef ATODCONV_HEADER #define ATODCONV_HEADER -extern bool JoystickEnabled; -extern int JoystickX; /* 16 bit number, 0 = right */ -extern int JoystickY; /* 16 bit number, 0 = down */ +extern bool JoystickEnabled[2]; +extern int JoystickX[2]; /* 16 bit number, 0 = right */ +extern int JoystickY[2]; /* 16 bit number, 0 = down */ void AtoDWrite(int Address, int Value); unsigned char AtoDRead(int Address); -void AtoDInit(void); -void AtoDEnable(void); -void AtoDDisable(void); +void AtoDInit(int index); +void AtoDEnable(int index); +void AtoDDisable(int index); void SaveAtoDUEF(FILE *SUEF); void LoadAtoDUEF(FILE *SUEF); diff --git a/Src/beebemrc.h b/Src/beebemrc.h index f076c0be..3855ae06 100644 --- a/Src/beebemrc.h +++ b/Src/beebemrc.h @@ -442,6 +442,17 @@ Boston, MA 02110-1301, USA. #define IDM_AUTOLOADJOYMAP 40301 #define IDM_RESETJOYMAP 40302 #define IDM_INIT_JOYSTICK 40303 +#define IDM_JOY1_PCJOY2 40305 +#define IDM_JOY1_PRIMARY 40306 +#define IDM_JOY1_SECONDARY1 40307 +#define IDM_JOY1_SECONDARY2 40308 +#define IDM_JOY2_PCJOY1 40309 +#define IDM_JOY2_PCJOY2 40310 +#define IDM_JOY2_ANALOGUE_MOUSESTICK 40311 +#define IDM_JOY2_DIGITAL_MOUSESTICK 40312 +#define IDM_JOY2_PRIMARY 40313 +#define IDM_JOY2_SECONDARY1 40314 +#define IDM_JOY2_SECONDARY2 40315 #define IDM_CAPTUREMOUSE 40318 #define IDC_STATIC -1 diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index 5649ceb3..3d859a1e 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -209,16 +209,24 @@ BeebWin::BeebWin() m_DXResetPending = false; m_JoystickTarget = nullptr; - - for (int i = 0; i < 2; ++i) + m_JoystickTimerRunning = false; + for (int i = 0; i < NUM_PC_JOYSTICKS; ++i) { memset(&m_JoystickState[i].Caps, 0, sizeof(m_JoystickState[i].Caps)); + m_JoystickState[i].JoyIndex = 0; m_JoystickState[i].Deadband = 0; m_JoystickState[i].Captured = false; + m_JoystickState[i].JoystickToKeysActive = false; m_JoystickState[i].PrevAxes = 0; m_JoystickState[i].PrevBtns = 0; } + for (int i = 0; i < 2; ++i) + { + m_PCAxesForJoystick[i] = 0; + m_PCStickForJoystick[i] = 0; + } + m_customip[0] = 0; m_customport = 0; m_isFullScreen = false; @@ -422,7 +430,6 @@ void BeebWin::ApplyPrefs() /* Joysticks can only be initialised after the window is created (needs hwnd) */ InitJoystick(false); - UpdateInitJoystickMenu(); LoadFDC(NULL, true); RTCInit(); @@ -593,7 +600,8 @@ void BeebWin::ResetBeebSystem(Model NewModelType, bool LoadRoms) Disc8271Reset(); if (EconetEnabled) EconetReset(); //Rob: Reset1770(); - AtoDInit(); + AtoDInit(0); + AtoDInit(1); SetRomMenu(); FreeDiscImage(0); // Keep the disc images loaded @@ -1108,8 +1116,29 @@ void BeebWin::InitMenu(void) CheckMenuItem(IDM_JOYSTICK, false); CheckMenuItem(IDM_ANALOGUE_MOUSESTICK, false); CheckMenuItem(IDM_DIGITAL_MOUSESTICK, false); - if (m_MenuIdSticks != 0) - CheckMenuItem(m_MenuIdSticks, true); + CheckMenuItem(IDM_JOY1_PCJOY2, false); + if (m_MenuIdSticks[0] != 0) + CheckMenuItem(m_MenuIdSticks[0], true); + + CheckMenuItem(IDM_JOY1_PRIMARY, false); + CheckMenuItem(IDM_JOY1_SECONDARY1, false); + CheckMenuItem(IDM_JOY1_SECONDARY2, false); + if (m_MenuIdAxes[0] != 0) + CheckMenuItem(m_MenuIdAxes[0], true); + + CheckMenuItem(IDM_JOY2_PCJOY1, false); + CheckMenuItem(IDM_JOY2_ANALOGUE_MOUSESTICK, false); + CheckMenuItem(IDM_JOY2_DIGITAL_MOUSESTICK, false); + CheckMenuItem(IDM_JOY2_PCJOY2, false); + if (m_MenuIdSticks[1] != 0) + CheckMenuItem(m_MenuIdSticks[1], true); + + CheckMenuItem(IDM_JOY2_PRIMARY, false); + CheckMenuItem(IDM_JOY2_SECONDARY1, false); + CheckMenuItem(IDM_JOY2_SECONDARY2, false); + if (m_MenuIdAxes[1] != 0) + CheckMenuItem(m_MenuIdAxes[1], true); + CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); CheckMenuItem(IDM_AUTOLOADJOYMAP, m_AutoloadJoystickMap); CheckMenuItem(IDM_FREEZEINACTIVE, m_FreezeWhenInactive); @@ -1249,21 +1278,140 @@ void BeebWin::SetRomMenu(void) /****************************************************************************/ -void BeebWin::UpdateInitJoystickMenu() +void BeebWin::UpdateJoystickMenu() { bool Enable = false; - if (m_MenuIdSticks == IDM_JOYSTICK && !m_JoystickState[0].Captured) + if ((PCJoystick1On() || m_JoystickToKeys) && !m_JoystickState[0].Captured) { Enable = true; } - else if (m_JoystickToKeys && (!m_JoystickState[0].Captured || !m_JoystickState[1].Captured)) + else if ((PCJoystick2On() || m_JoystickToKeys) && !m_JoystickState[1].Captured) { Enable = true; } EnableMenuItem(IDM_INIT_JOYSTICK, Enable); CheckMenuItem(IDM_INIT_JOYSTICK, m_JoystickState[0].Captured); + + Enable = false; + if (m_MenuIdSticks[0] == IDM_JOYSTICK || m_MenuIdSticks[0] == IDM_JOY1_PCJOY2) + Enable = true; + EnableMenuItem(IDM_JOY1_PRIMARY, Enable); + EnableMenuItem(IDM_JOY1_SECONDARY1, Enable); + EnableMenuItem(IDM_JOY1_SECONDARY2, Enable); + + Enable = false; + if (m_MenuIdSticks[1] == IDM_JOY2_PCJOY1 || m_MenuIdSticks[1] == IDM_JOY2_PCJOY2) + Enable = true; + EnableMenuItem(IDM_JOY2_PRIMARY, Enable); + EnableMenuItem(IDM_JOY2_SECONDARY1, Enable); + EnableMenuItem(IDM_JOY2_SECONDARY2, Enable); +} + +/****************************************************************************/ + +bool BeebWin::PCJoystick1On() +{ + return m_MenuIdSticks[0] == IDM_JOYSTICK || m_MenuIdSticks[1] == IDM_JOY2_PCJOY1; +} + +/****************************************************************************/ + +bool BeebWin::PCJoystick2On() +{ + return m_MenuIdSticks[0] == IDM_JOY1_PCJOY2 || m_MenuIdSticks[1] == IDM_JOY2_PCJOY2; +} + +/****************************************************************************/ + +int BeebWin::MenuIdToStick(int menuId) +{ + int pcStick = 0; + switch (menuId) + { + case IDM_JOYSTICK: + case IDM_JOY2_PCJOY1: + pcStick = 1; + break; + case IDM_JOY1_PCJOY2: + case IDM_JOY2_PCJOY2: + pcStick = 2; + break; + } + return pcStick; +} + +/****************************************************************************/ + +int BeebWin::StickToMenuId(int bbcStick, int pcStick) +{ + int menuId = 0; + if (bbcStick == 0) + { + if (pcStick == 1) + menuId = IDM_JOYSTICK; + else if (pcStick == 2) + menuId = IDM_JOY1_PCJOY2; + } + else + { + if (pcStick == 1) + menuId = IDM_JOY2_PCJOY1; + else if (pcStick == 2) + menuId = IDM_JOY2_PCJOY2; + } + return menuId; +} + +/****************************************************************************/ + +int BeebWin::MenuIdToAxes(int menuId) +{ + int axes = 0; + switch (menuId) + { + case IDM_JOY1_PRIMARY: + case IDM_JOY2_PRIMARY: + axes = 1; + break; + case IDM_JOY1_SECONDARY1: + case IDM_JOY2_SECONDARY1: + axes = 2; + break; + case IDM_JOY1_SECONDARY2: + case IDM_JOY2_SECONDARY2: + axes = 3; + break; + } + return axes; +} + +int BeebWin::AxesToMenuId(int bbcStick, int pcAxes) +{ + int menuId = 0; + switch (pcAxes) + { + case 1: + if (bbcStick == 0) + menuId = IDM_JOY1_PRIMARY; + else + menuId = IDM_JOY2_PRIMARY; + break; + case 2: + if (bbcStick == 0) + menuId = IDM_JOY1_SECONDARY1; + else + menuId = IDM_JOY2_SECONDARY1; + break; + case 3: + if (bbcStick == 0) + menuId = IDM_JOY1_SECONDARY2; + else + menuId = IDM_JOY2_SECONDARY2; + break; + } + return menuId; } /****************************************************************************/ @@ -1272,37 +1420,106 @@ bool BeebWin::InitJoystick(bool verbose) { bool Success = true; - if ((m_MenuIdSticks == IDM_JOYSTICK || m_JoystickToKeys) && !m_JoystickState[0].Captured) + m_PCStickForJoystick[0] = MenuIdToStick(m_MenuIdSticks[0]); + m_PCAxesForJoystick[0] = MenuIdToAxes(m_MenuIdAxes[0]); + m_PCStickForJoystick[1] = MenuIdToStick(m_MenuIdSticks[1]); + m_PCAxesForJoystick[1] = MenuIdToAxes(m_MenuIdAxes[1]); + + // PC joystick 1 must be captured to be able to capture PC joystick 2 + if (PCJoystick1On() || PCJoystick2On() || m_JoystickToKeys) { - Success = CaptureJoystick(0, verbose); + if (!m_JoystickState[0].Captured) + { + Success = CaptureJoystick(0, verbose); + } + } + else + { + if (m_JoystickState[0].Captured) + { + m_JoystickState[0].Captured = false; + TranslateJoystick(0); + } } - if (Success && m_JoystickToKeys && !m_JoystickState[1].Captured) + if (PCJoystick2On() || m_JoystickToKeys) { - Success = CaptureJoystick(1, verbose); + if (m_JoystickState[0].Captured && !m_JoystickState[1].Captured) + { + Success = CaptureJoystick(1, verbose); + } + } + else + { + if (m_JoystickState[1].Captured) + { + m_JoystickState[1].Captured = false; + TranslateJoystick(1); + } } + if (PCJoystick1On() || PCJoystick2On() || m_JoystickToKeys) + { + if (!m_JoystickTimerRunning) + { + SetTimer(m_hWnd, 3, 20, NULL); + m_JoystickTimerRunning = true; + } + } + else + { + if (m_JoystickTimerRunning) + { + KillTimer(m_hWnd, 3); + m_JoystickTimerRunning = false; + } + } + + UpdateJoystickMenu(); + return Success; } bool BeebWin::CaptureJoystick(int Index, bool verbose) { - // Get joystick updates 20 times a second. - // There's no need to use JOYSTICKID constants. - MMRESULT Result = joySetCapture(m_hWnd, Index, 50, FALSE); + bool success = false; + + DWORD JoyIndex; + DWORD Result; + JOYINFOEX joyInfoEx; + UINT numDevs = joyGetNumDevs(); - if (Result == JOYERR_NOERROR) + // Scan for first present joystick index. It doesn't have to be + // consecutive number. + if (Index == 0) + JoyIndex = 0; + else + JoyIndex = m_JoystickState[Index - 1].JoyIndex + 1; + + // Find first joystick that is known AND connected + Result = JOYERR_UNPLUGGED; + while (Result != JOYERR_NOERROR && JoyIndex != numDevs) { - Result = joyGetDevCaps(Index, &m_JoystickState[Index].Caps, sizeof(JOYCAPS)); + memset(&joyInfoEx, 0, sizeof(joyInfoEx)); + joyInfoEx.dwSize = sizeof(joyInfoEx); + joyInfoEx.dwFlags = JOY_RETURNBUTTONS; + + Result = joyGetDevCaps(JoyIndex, &m_JoystickState[Index].Caps, sizeof(JOYCAPS)); + if (Result == JOYERR_NOERROR) + Result = joyGetPosEx(JoyIndex, &joyInfoEx); + if (Result != JOYERR_NOERROR) + ++JoyIndex; } - if (Result == JOYERR_NOERROR) + if (Result == ERROR_SUCCESS) { m_JoystickState[Index].Captured = true; + m_JoystickState[Index].JoyIndex = JoyIndex; + success = true; } else if (verbose) { - if (Result == JOYERR_UNPLUGGED) + if (Result == ERROR_DEVICE_NOT_CONNECTED || Result == JOYERR_UNPLUGGED) { char str[100]; sprintf(str, "Joystick %d is not plugged in", Index + 1); @@ -1318,34 +1535,28 @@ bool BeebWin::CaptureJoystick(int Index, bool verbose) } } - return Result == JOYERR_NOERROR; + return success; } - /****************************************************************************/ void BeebWin::SetJoystickButton(int index, bool button) { - if (m_MenuIdSticks == IDM_JOYSTICK) - JoystickButton[index] = button; + JoystickButton[index] = button; } /****************************************************************************/ -void BeebWin::ScaleJoystick(unsigned int x, unsigned int y) +void BeebWin::ScaleJoystick(int index, unsigned int x, unsigned int y, + unsigned int minX, unsigned int minY, unsigned int maxX, unsigned int maxY) { - if (m_MenuIdSticks == IDM_JOYSTICK) - { - JOYCAPS& caps = m_JoystickState[0].Caps; - - // Scale and reverse the readings - JoystickX = (int)((double)(caps.wXmax - x) * 65535.0 / - (double)(caps.wXmax - caps.wXmin)); - JoystickY = (int)((double)(caps.wYmax - y) * 65535.0 / - (double)(caps.wYmax - caps.wYmin)); - } + /* Scale and reverse the readings */ + JoystickX[index] = (int)((double)(maxX - x) * 65535.0 / + (double)(maxX - minX)); + JoystickY[index] = (int)((double)(maxY - y) * 65535.0 / + (double)(maxY - minY)); } /****************************************************************************/ -unsigned int BeebWin::GetJoystickAxes(JOYCAPS& caps, int deadband, JOYINFOEX& joyInfoEx) +unsigned int BeebWin::GetJoystickAxes(const JOYCAPS& caps, int deadband, const JOYINFOEX& joyInfoEx) { unsigned int axes = 0; @@ -1424,33 +1635,36 @@ void BeebWin::TranslateOrSendKey(int vkey, bool keyUp) } /****************************************************************************/ -void BeebWin::TranslateJoystickMove(int joyId, JOYINFOEX& joyInfoEx) +void BeebWin::TranslateAxes(int joyId, unsigned int axesState) { - JOYCAPS& caps = m_JoystickState[joyId].Caps; - unsigned int deadband = m_JoystickState[joyId].Deadband; - unsigned int& prevAxes = m_JoystickState[joyId].PrevAxes; int vkeys = (joyId == 1) ? BEEB_VKEY_JOY2_AXES : BEEB_VKEY_JOY1_AXES; + unsigned int& prevAxes = m_JoystickState[joyId].PrevAxes; - unsigned int axes = GetJoystickAxes(caps, deadband, joyInfoEx); - - if (axes != prevAxes) + if (axesState != prevAxes) { for (int axId = 0; axId < JOYSTICK_AXES_COUNT; ++axId) { - if ((axes & ~prevAxes) & (1 << axId)) + if ((axesState & ~prevAxes) & (1 << axId)) { TranslateOrSendKey(vkeys + axId, false); } - else if ((~axes & prevAxes) & (1 << axId)) + else if ((~axesState & prevAxes) & (1 << axId)) { TranslateOrSendKey(vkeys + axId, true); } } - - prevAxes = axes; + prevAxes = axesState; } } +/****************************************************************************/ +void BeebWin::TranslateJoystickMove(int joyId, const JOYINFOEX& joyInfoEx, const JOYCAPS& caps) +{ + unsigned int deadband = m_JoystickState[joyId].Deadband; + unsigned int axes = mainWin->GetJoystickAxes(caps, deadband, joyInfoEx); + TranslateAxes(joyId, axes); +} + /****************************************************************************/ void BeebWin::TranslateJoystickButtons(int joyId, unsigned int buttons) { @@ -1480,61 +1694,172 @@ void BeebWin::TranslateJoystickButtons(int joyId, unsigned int buttons) /****************************************************************************/ void BeebWin::TranslateJoystick(int joyId) { - if (m_JoystickToKeys) - { - JOYINFOEX joyInfoEx; - joyInfoEx.dwSize = sizeof(joyInfoEx); - joyInfoEx.dwFlags = JOY_RETURNALL | JOY_RETURNPOVCTS; - if (joyGetPosEx(joyId, &joyInfoEx) == JOYERR_NOERROR) + static const JOYCAPS dummyJoyCaps = { + 0, 0, "", + 0, 65535, 0, 65535, 0, 65535, + 16, 10, 1000, + 0, 65535, 0, 65535, 0, 65535 + }; + bool success = false; + JOYINFOEX joyInfoEx; + const JOYCAPS* joyCaps = &m_JoystickState[joyId].Caps; + DWORD joyIndex = m_JoystickState[joyId].JoyIndex; + + memset(&joyInfoEx, 0, sizeof(joyInfoEx)); + + joyInfoEx.dwSize = sizeof(joyInfoEx); + joyInfoEx.dwFlags = JOY_RETURNALL | JOY_RETURNPOVCTS; + + if (m_JoystickState[joyId].Captured) + { + MMRESULT result = joyGetPosEx(joyIndex, &joyInfoEx); + if (result == JOYERR_NOERROR) { - TranslateJoystickMove(joyId, joyInfoEx); - TranslateJoystickButtons(joyId, joyInfoEx.dwButtons); + success = true; } - else + } + + // If joystick is disabled or error occurs, reset all axes and buttons + if (!success) + { + joyCaps = &dummyJoyCaps; + joyInfoEx.dwXpos = joyInfoEx.dwYpos = joyInfoEx.dwZpos = 32768; + joyInfoEx.dwRpos = joyInfoEx.dwUpos = joyInfoEx.dwVpos = 32768; + joyInfoEx.dwPOV = JOY_POVCENTERED; + // Reset 'captured' flag and update menu entry + m_JoystickState[joyId].Captured = false; + UpdateJoystickMenu(); + } + + // PC joystick to BBC joystick + for (int bbcIndex = 0; bbcIndex < 2; ++bbcIndex) + { + if (m_PCStickForJoystick[bbcIndex] == joyId + 1) { - // Joystick read failed - unplugged? - // Reset 'captured' flag and update menu entry - m_JoystickState[joyId].Captured = false; - UpdateInitJoystickMenu(); + if (bbcIndex == 1 && m_PCStickForJoystick[0] == m_PCStickForJoystick[1] + && m_PCAxesForJoystick[0] != m_PCAxesForJoystick[1]) + { + // If both BBC joystick are mapped to the same PC gamepad, but not + // the same axes, use secondary button for second joystick + SetJoystickButton(bbcIndex, (joyInfoEx.dwButtons & JOY_BUTTON2) != 0); + } + else + { + SetJoystickButton(bbcIndex, (joyInfoEx.dwButtons & JOY_BUTTON1) != 0); + } + + if (m_MenuIdSticks[1] == 0) + { + // Second BBC joystick not enabled - map second button to BBC second button + SetJoystickButton(1, (joyInfoEx.dwButtons & JOY_BUTTON2) != 0); + } + + if (m_PCAxesForJoystick[bbcIndex] == 1) + { + ScaleJoystick(bbcIndex, joyInfoEx.dwXpos, joyInfoEx.dwYpos, + joyCaps->wXmin, joyCaps->wYmin, + joyCaps->wXmax, joyCaps->wYmax); + } + else if (m_PCAxesForJoystick[bbcIndex] == 2) + { + ScaleJoystick(bbcIndex, joyInfoEx.dwUpos, joyInfoEx.dwRpos, + joyCaps->wUmin, joyCaps->wRmin, + joyCaps->wUmax, joyCaps->wRmax); + } + else if (m_PCAxesForJoystick[bbcIndex] == 3) + { + ScaleJoystick(bbcIndex, joyInfoEx.dwZpos, joyInfoEx.dwRpos, + joyCaps->wZmin, joyCaps->wRmin, + joyCaps->wZmax, joyCaps->wRmax); + } } } + + // Joystick to keyboard mapping + if (m_JoystickToKeys) + { + TranslateJoystickMove(joyId, joyInfoEx, *joyCaps); + TranslateJoystickButtons(joyId, joyInfoEx.dwButtons); + m_JoystickState[joyId].JoystickToKeysActive = true; + } + + // Make sure to reset keyboard state + if (!m_JoystickToKeys && m_JoystickState[joyId].JoystickToKeysActive) + { + TranslateAxes(joyId, 0); + TranslateJoystickButtons(joyId, 0); + m_JoystickState[joyId].JoystickToKeysActive = true; + } +} + +/****************************************************************************/ +void BeebWin::ResetJoystick(void) +{ + m_JoystickState[0].Captured = false; + TranslateJoystick(0); + + m_JoystickState[1].Captured = false; + TranslateJoystick(1); } /****************************************************************************/ void BeebWin::SetMousestickButton(int index, bool button) { - if (m_MenuIdSticks == IDM_ANALOGUE_MOUSESTICK || - m_MenuIdSticks == IDM_DIGITAL_MOUSESTICK) + if (index == 0) { - JoystickButton[index] = button; + if (m_MenuIdSticks[0] == IDM_ANALOGUE_MOUSESTICK || + m_MenuIdSticks[0] == IDM_DIGITAL_MOUSESTICK) + { + SetJoystickButton(0, button); + } + if (m_MenuIdSticks[1] == IDM_JOY2_ANALOGUE_MOUSESTICK || + m_MenuIdSticks[1] == IDM_JOY2_DIGITAL_MOUSESTICK) + { + SetJoystickButton(1, button); + } + } + else + { + // Map right mouse button to secondary fire if second joystick is + // not enabled. + if (m_MenuIdSticks[1] == 0 && (m_MenuIdSticks[0] == IDM_ANALOGUE_MOUSESTICK || + m_MenuIdSticks[0] == IDM_DIGITAL_MOUSESTICK)) + { + SetJoystickButton(1, button); + } } } /****************************************************************************/ void BeebWin::ScaleMousestick(unsigned int x, unsigned int y) { - static int lastx = 32768; - static int lasty = 32768; + static int lastx[2] = { 32768, 32768 }; + static int lasty[2] = { 32768, 32768 }; - if (m_MenuIdSticks == IDM_ANALOGUE_MOUSESTICK) - { - JoystickX = (m_XWinSize - x) * 65535 / m_XWinSize; - JoystickY = (m_YWinSize - y) * 65535 / m_YWinSize; - } - else if (m_MenuIdSticks == IDM_DIGITAL_MOUSESTICK) + for (int index = 0; index < 2; ++index) { - int dx = x - lastx; - int dy = y - lasty; + if (index == 0 && m_MenuIdSticks[0] == IDM_ANALOGUE_MOUSESTICK || + index == 1 && m_MenuIdSticks[1] == IDM_JOY2_ANALOGUE_MOUSESTICK) + { + JoystickX[index] = (m_XWinSize - x) * 65535 / m_XWinSize; + JoystickY[index] = (m_YWinSize - y) * 65535 / m_YWinSize; + } + else if (index == 0 && m_MenuIdSticks[0] == IDM_DIGITAL_MOUSESTICK || + index == 1 && m_MenuIdSticks[1]== IDM_JOY2_DIGITAL_MOUSESTICK) + { + int dx = x - lastx[index]; + int dy = y - lasty[index]; - if (dx > 4) JoystickX = 0; - if (dx < -4) JoystickX = 65535; + if (dx > 4) JoystickX[index] = 0; + if (dx < -4) JoystickX[index] = 65535; - if (dy > 4) JoystickY = 0; - if (dy < -4) JoystickY = 65535; + if (dy > 4) JoystickY[index] = 0; + if (dy < -4) JoystickY[index] = 65535; - lastx = x; - lasty = y; + lastx[index] = x; + lasty[index] = y; + } } } @@ -1885,21 +2210,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle } break; - case MM_JOY1MOVE: - mainWin->ScaleJoystick(LOWORD(lParam), HIWORD(lParam)); - mainWin->TranslateJoystick(0); - break; - - case MM_JOY1BUTTONDOWN: - case MM_JOY1BUTTONUP: - mainWin->SetJoystickButton(0, ((UINT)uParam & JOY_BUTTON1) != 0); - mainWin->SetJoystickButton(1, ((UINT)uParam & JOY_BUTTON2) != 0); - break; - - case MM_JOY2MOVE: - mainWin->TranslateJoystick(1); - break; - case WM_INPUT: if (mainWin->m_MouseCaptured) { @@ -2034,6 +2344,15 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle mainWin->KillBootDiscTimer(); mainWin->DoShiftBreak(); } + else if (uParam == 3) + { + // Update joysticks + if (mainWin->m_JoystickState[0].Captured) + mainWin->TranslateJoystick(0); + + if (mainWin->m_JoystickState[1].Captured) + mainWin->TranslateJoystick(1); + } break; case WM_USER_KEYBOARD_DIALOG_CLOSED: @@ -3134,7 +3453,7 @@ void BeebWin::HandleCommand(int MenuId) { // Also switch on analogue mousestick (touch screen uses // mousestick position) - if (m_MenuIdSticks != IDM_ANALOGUE_MOUSESTICK) + if (m_MenuIdSticks[0] != IDM_ANALOGUE_MOUSESTICK) HandleCommand(IDM_ANALOGUE_MOUSESTICK); if (EthernetPortEnabled) @@ -3586,53 +3905,104 @@ void BeebWin::HandleCommand(int MenuId) case IDM_JOYSTICK: case IDM_ANALOGUE_MOUSESTICK: case IDM_DIGITAL_MOUSESTICK: + case IDM_JOY1_PCJOY2: /* Disable current selection */ - if (m_MenuIdSticks != 0) + if (m_MenuIdSticks[0] != 0) { - CheckMenuItem(m_MenuIdSticks, false); + CheckMenuItem(m_MenuIdSticks[0], false); + + AtoDDisable(0); + + SetJoystickButton(0, false); - AtoDDisable(); + if (m_MenuIdSticks[1] == 0) + { + SetJoystickButton(1, false); + } } - if (MenuId == m_MenuIdSticks) + if (MenuId == m_MenuIdSticks[0]) { - /* Joysticks switched off completely */ - m_MenuIdSticks = 0; + /* Joystick 1 switched off completely */ + m_MenuIdSticks[0] = 0; } else { /* Initialise new selection */ - m_MenuIdSticks = MenuId; + m_MenuIdSticks[0] = MenuId; - if (m_MenuIdSticks == IDM_JOYSTICK) - { - InitJoystick(false); - } - AtoDEnable(); + AtoDEnable(0); - if (JoystickEnabled) - CheckMenuItem(m_MenuIdSticks, true); + if (JoystickEnabled[0]) + CheckMenuItem(m_MenuIdSticks[0], true); else - m_MenuIdSticks = 0; + m_MenuIdSticks[0] = 0; } - UpdateInitJoystickMenu(); + + InitJoystick(false); + break; + + case IDM_JOY2_PCJOY1: + case IDM_JOY2_ANALOGUE_MOUSESTICK: + case IDM_JOY2_DIGITAL_MOUSESTICK: + case IDM_JOY2_PCJOY2: + /* Disable current selection */ + if (m_MenuIdSticks[1] != 0) + { + CheckMenuItem(m_MenuIdSticks[1], false); + + AtoDDisable(1); + + SetJoystickButton(1, false); + } + + if (MenuId == m_MenuIdSticks[1]) + { + /* Joystick 2 switched off completely */ + m_MenuIdSticks[1] = 0; + } + else + { + /* Initialise new selection */ + m_MenuIdSticks[1] = MenuId; + + AtoDEnable(1); + + if (JoystickEnabled[1]) + CheckMenuItem(m_MenuIdSticks[1], true); + else + m_MenuIdSticks[1] = 0; + } + InitJoystick(false); + break; + + case IDM_JOY1_PRIMARY: + case IDM_JOY1_SECONDARY1: + case IDM_JOY1_SECONDARY2: + CheckMenuItem(m_MenuIdAxes[0], false); + m_MenuIdAxes[0] = MenuId; + m_PCAxesForJoystick[0] = MenuIdToAxes(MenuId); + CheckMenuItem(m_MenuIdAxes[0], true); + break; + + case IDM_JOY2_PRIMARY: + case IDM_JOY2_SECONDARY1: + case IDM_JOY2_SECONDARY2: + CheckMenuItem(m_MenuIdAxes[1], false); + m_MenuIdAxes[1] = MenuId; + m_PCAxesForJoystick[1] = MenuIdToAxes(MenuId); + CheckMenuItem(m_MenuIdAxes[1], true); break; case IDM_INIT_JOYSTICK: + ResetJoystick(); InitJoystick(true); - UpdateInitJoystickMenu(); break; case IDM_JOYSTICK_TO_KEYS: m_JoystickToKeys = !m_JoystickToKeys; - if (m_JoystickToKeys) - { - InitJoystick(false); - } - + InitJoystick(false); CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); - - UpdateInitJoystickMenu(); break; case IDM_AUTOLOADJOYMAP: @@ -4512,6 +4882,7 @@ void BeebWin::UserKeyboardDialogClosed() if (m_JoystickToKeys && !m_JoystickToKeysWasEnabled) { m_JoystickToKeys = m_JoystickToKeysWasEnabled; + InitJoystick(false); } // Unmute diff --git a/Src/beebwin.h b/Src/beebwin.h index 042c560f..c4467d31 100644 --- a/Src/beebwin.h +++ b/Src/beebwin.h @@ -51,8 +51,10 @@ Boston, MA 02110-1301, USA. #define CFG_KEYBOARD_LAYOUT "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout" #define CFG_SCANCODE_MAP "Scancode Map" -#define JOYSTICK_MAX_AXES 16 -#define JOYSTICK_MAX_BTNS 16 +#define NUM_PC_JOYSTICKS 2 + +#define JOYSTICK_MAX_AXES 16 +#define JOYSTICK_MAX_BTNS 16 #define JOYSTICK_AXIS_UP 0 #define JOYSTICK_AXIS_DOWN 1 @@ -164,10 +166,12 @@ enum class MessageType { struct JoystickState { JOYCAPS Caps; + unsigned int JoyIndex; unsigned int Deadband; bool Captured; unsigned int PrevAxes; unsigned int PrevBtns; + bool JoystickToKeysActive; }; class BeebWin { @@ -257,10 +261,13 @@ class BeebWin { void DisplayClientAreaText(HDC hdc); void DisplayFDCBoardInfo(HDC hDC, int x, int y); void SetJoystickButton(int index, bool button); - void ScaleJoystick(unsigned int x, unsigned int y); - unsigned int GetJoystickAxes(JOYCAPS& caps, int deadband, JOYINFOEX& joyInfoEx); + void ScaleJoystick(int index, unsigned int x, unsigned int y, + unsigned int minX, unsigned int minY, + unsigned int maxX, unsigned int maxY); + unsigned int GetJoystickAxes(const JOYCAPS& caps, int deadband, const JOYINFOEX& joyInfoEx); void TranslateOrSendKey(int vkey, bool keyUp); - void TranslateJoystickMove(int joyId, JOYINFOEX& joyInfoEx); + void TranslateAxes(int joyId, unsigned int axesState); + void TranslateJoystickMove(int joyId, const JOYINFOEX& joyInfoEx, const JOYCAPS& caps); void TranslateJoystickButtons(int joyId, unsigned int buttons); void TranslateJoystick(int joyId); void SetMousestickButton(int index, bool button); @@ -362,8 +369,12 @@ class BeebWin { int m_MenuIdVolume; int m_MenuIdTiming; int m_FPSTarget; - JoystickState m_JoystickState[2]; - int m_MenuIdSticks; + bool m_JoystickTimerRunning; + JoystickState m_JoystickState[NUM_PC_JOYSTICKS]; + int m_PCStickForJoystick[2]; + int m_PCAxesForJoystick[2]; + int m_MenuIdSticks[2]; + int m_MenuIdAxes[2]; bool m_JoystickToKeys; bool m_AutoloadJoystickMap; HWND m_JoystickTarget; @@ -572,8 +583,13 @@ class BeebWin { int ReadDisc(int Drive, bool bCheckForPrefs); void Load1770DiscImage(const char *FileName, int Drive, DiscType Type); void LoadTape(void); + int MenuIdToStick(int menuId); + int StickToMenuId(int bbcStick, int pcStick); + int MenuIdToAxes(int menuId); + int AxesToMenuId(int bbcStick, int pcAxes); bool InitJoystick(bool verbose = false); bool CaptureJoystick(int Index, bool verbose); + void ResetJoystick(void); void RestoreState(void); void SaveState(void); void NewDiscImage(int Drive); @@ -623,7 +639,9 @@ class BeebWin { void ResetJoyMap(JoyMap* joymap); bool ReadJoyMap(const char *filename, JoyMap *joymap); bool WriteJoyMap(const char *filename, JoyMap *joymap); - void UpdateInitJoystickMenu(); + bool PCJoystick1On(); + bool PCJoystick2On(); + void UpdateJoystickMenu(); void Report(MessageType type, const char *format, ...); diff --git a/Src/beebwinprefs.cpp b/Src/beebwinprefs.cpp index cd64c2ef..5e0b1a8a 100644 --- a/Src/beebwinprefs.cpp +++ b/Src/beebwinprefs.cpp @@ -60,6 +60,7 @@ static const char *CFG_SOUND_SAMPLE_RATE = "SampleRate"; static const char *CFG_SOUND_VOLUME = "SoundVolume"; static const char *CFG_SOUND_ENABLED = "SoundEnabled"; static const char *CFG_OPTIONS_STICKS = "Sticks"; +static const char *CFG_OPTIONS_STICKS2 = "Sticks2"; static const char *CFG_OPTIONS_KEY_MAPPING = "KeyMapping"; static const char *CFG_OPTIONS_USER_KEY_MAP_FILE = "UserKeyMapFile"; static const char *CFG_OPTIONS_FREEZEINACTIVE = "FreezeWhenInactive"; @@ -260,9 +261,26 @@ void BeebWin::LoadPreferences() Music5000Enabled = false; if (m_Preferences.GetDWORDValue(CFG_OPTIONS_STICKS, dword)) - m_MenuIdSticks = dword; + m_MenuIdSticks[0] = dword; else - m_MenuIdSticks = 0; + m_MenuIdSticks[0] = 0; + + if (m_Preferences.GetDWORDValue(CFG_OPTIONS_STICKS2, dword)) + m_MenuIdSticks[1] = dword; + else + m_MenuIdSticks[1] = 0; + + if (m_Preferences.GetDWORDValue("JoystickAxes1", dword)) + m_PCAxesForJoystick[0] = dword; + else + m_PCAxesForJoystick[0] = 1; + m_MenuIdAxes[0] = AxesToMenuId(0, m_PCAxesForJoystick[0]); + + if (m_Preferences.GetDWORDValue("JoystickAxes2", dword)) + m_PCAxesForJoystick[1] = dword; + else + m_PCAxesForJoystick[1] = 1; + m_MenuIdAxes[1] = AxesToMenuId(1, m_PCAxesForJoystick[1]); if (!m_Preferences.GetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys)) m_JoystickToKeys = false; @@ -665,7 +683,11 @@ void BeebWin::SavePreferences(bool saveAll) m_Preferences.SetBoolValue("TextToSpeechEnabled", m_TextToSpeechEnabled); m_Preferences.SetBoolValue("Music5000Enabled", Music5000Enabled); - m_Preferences.SetDWORDValue(CFG_OPTIONS_STICKS, m_MenuIdSticks); + m_Preferences.SetDWORDValue(CFG_OPTIONS_STICKS, m_MenuIdSticks[0]); + m_Preferences.SetDWORDValue(CFG_OPTIONS_STICKS2, m_MenuIdSticks[1]); + m_Preferences.SetDWORDValue("JoystickAxes1", m_PCAxesForJoystick[0]); + m_Preferences.SetDWORDValue("JoystickAxes2", m_PCAxesForJoystick[1]); + m_Preferences.SetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys); m_Preferences.SetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, m_AutoloadJoystickMap); m_Preferences.SetDWORDValue(CFG_OPTIONS_STICK1_DEADBAND, m_JoystickState[0].Deadband); diff --git a/Src/serialdevices.cpp b/Src/serialdevices.cpp index 60db3f40..938c45f6 100644 --- a/Src/serialdevices.cpp +++ b/Src/serialdevices.cpp @@ -243,8 +243,8 @@ void TouchScreenReadScreen(bool check) { static int last_x = -1, last_y = -1, last_m = -1; - int x = (65535 - JoystickX) / (65536 / 120) + 1; - int y = JoystickY / (65536 / 90) + 1; + int x = (65535 - JoystickX[0]) / (65536 / 120) + 1; + int y = JoystickY[0] / (65536 / 90) + 1; if (last_x != x || last_y != y || last_m != AMXButtons || !check) { From 99d8c5e901ff55646405e975537a55d3d1aee5e3 Mon Sep 17 00:00:00 2001 From: Chris Needham Date: Sat, 20 Feb 2021 22:42:02 +0000 Subject: [PATCH 19/38] Removed "Basic Hardware Only" option This option disabled the ADC and Serial interface, presumably to make the emulator run faster. I've removed this option as it doesn't affect performance particularly, and simplifies joystick handling. --- Help/menus.html | 8 -------- Src/6502core.cpp | 7 ++----- Src/BeebEm.rc | 1 - Src/atodconv.cpp | 41 +++++++++-------------------------------- Src/atodconv.h | 5 +---- Src/beebemrc.h | 1 - Src/beebwin.cpp | 34 ++++++++++------------------------ Src/beebwinprefs.cpp | 3 --- 8 files changed, 22 insertions(+), 78 deletions(-) diff --git a/Help/menus.html b/Help/menus.html index 9211b270..1bcd8a04 100644 --- a/Help/menus.html +++ b/Help/menus.html @@ -623,14 +623,6 @@

Hardware Menu

- - - - -
-

BBC MicroBBC Micro

Basic Hardware OnlySwitches off Analogue to Digital (Joystick) and - Serial (printing, comms & tape) emulation. May - speed BeebEm up. -
Emulator Traps Enables the use of opcodes &x3 to access diff --git a/Src/6502core.cpp b/Src/6502core.cpp index 323f5210..c23d0435 100644 --- a/Src/6502core.cpp +++ b/Src/6502core.cpp @@ -240,7 +240,6 @@ bool IntDue=false; int CyclesToInt = NO_TIMER_INT_DUE; static bool Branched; // true if the instruction branched -bool BasicHardwareOnly = false; // false = all hardware, true = basic hardware only // 1 if first cycle happened // Get a two byte address from the program counter, and then post inc @@ -3112,10 +3111,8 @@ static void PollHardware(unsigned int nCycles) } VideoPoll(nCycles); - if (!BasicHardwareOnly) { - AtoD_poll(nCycles); - Serial_Poll(); - } + AtoD_poll(nCycles); + Serial_Poll(); Disc8271Poll(); Music5000Poll(nCycles); SoundPoll(); diff --git a/Src/BeebEm.rc b/Src/BeebEm.rc index 008083c4..bf2d54bc 100644 --- a/Src/BeebEm.rc +++ b/Src/BeebEm.rc @@ -689,7 +689,6 @@ BEGIN MENUITEM "SW RAM Board On/Off", IDM_SWRAMBOARD MENUITEM "Edit ROM Configuration", IDM_ROMCONFIG MENUITEM SEPARATOR - MENUITEM "Basic &Hardware Only", ID_BASIC_HARDWARE_ONLY MENUITEM "&Teletext Half Mode", ID_TELETEXTHALFMODE MENUITEM SEPARATOR MENUITEM "Econet On/Off", ID_ECONET diff --git a/Src/atodconv.cpp b/Src/atodconv.cpp index 18af2a8d..6b084059 100644 --- a/Src/atodconv.cpp +++ b/Src/atodconv.cpp @@ -32,8 +32,6 @@ Boston, MA 02110-1301, USA. #include "sysvia.h" #include "uefstate.h" -bool JoystickEnabled[2] = { false, false }; - /* X and Y positions for joystick 1 and 2 */ int JoystickX[2]; int JoystickY[2]; @@ -128,47 +126,26 @@ void AtoD_poll_real(void) } /*--------------------------------------------------------------------------*/ -void AtoDInit(int index) + +void AtoDInit() { AtoDState.datalatch = 0; AtoDState.high = 0; AtoDState.low = 0; ClearTrigger(AtoDTrigger); - /* Move joystick to middle */ - JoystickX[index] = 32767; - JoystickY[index] = 32767; + // Move both joysticks to middle + JoystickX[0] = 32767; + JoystickY[0] = 32767; - /* Not busy, conversion complete (OS1.2 will then request another conversion) */ + JoystickX[1] = 32767; + JoystickY[1] = 32767; + + // Not busy, conversion complete (OS1.2 will then request another conversion) AtoDState.status = 0x40; PulseSysViaCB1(); } -/*--------------------------------------------------------------------------*/ -void AtoDEnable(int index) -{ - JoystickEnabled[index] = true; - AtoDInit(index); -} - -/*--------------------------------------------------------------------------*/ -void AtoDDisable(int index) -{ - JoystickEnabled[index] = false; - if (!JoystickEnabled[0] && !JoystickEnabled[1]) - { - AtoDState.datalatch = 0; - AtoDState.status = 0x80; /* busy, conversion not complete */ - AtoDState.high = 0; - AtoDState.low = 0; - ClearTrigger(AtoDTrigger); - } - - /* Move joystick to middle (superpool looks at joystick even when not selected) */ - JoystickX[index] = 32767; - JoystickY[index] = 32767; -} - /*--------------------------------------------------------------------------*/ void SaveAtoDUEF(FILE *SUEF) { fput16(0x0474,SUEF); diff --git a/Src/atodconv.h b/Src/atodconv.h index 76686195..eaf584de 100644 --- a/Src/atodconv.h +++ b/Src/atodconv.h @@ -24,15 +24,12 @@ Boston, MA 02110-1301, USA. #ifndef ATODCONV_HEADER #define ATODCONV_HEADER -extern bool JoystickEnabled[2]; extern int JoystickX[2]; /* 16 bit number, 0 = right */ extern int JoystickY[2]; /* 16 bit number, 0 = down */ void AtoDWrite(int Address, int Value); unsigned char AtoDRead(int Address); -void AtoDInit(int index); -void AtoDEnable(int index); -void AtoDDisable(int index); +void AtoDInit(); void SaveAtoDUEF(FILE *SUEF); void LoadAtoDUEF(FILE *SUEF); diff --git a/Src/beebemrc.h b/Src/beebemrc.h index 3855ae06..03d37980 100644 --- a/Src/beebemrc.h +++ b/Src/beebemrc.h @@ -311,7 +311,6 @@ Boston, MA 02110-1301, USA. #define ID_TAPE_NORMAL 40141 #define IDM_MUSIC5000 40142 #define ID_TELETEXTHALFMODE 40143 -#define ID_BASIC_HARDWARE_ONLY 40144 #define ID_PSAMPLES 40148 #define IDM_FIXEDSPEED100 40151 #define IDM_FIXEDSPEED5 40154 diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index 3d859a1e..d3c1a23a 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -411,7 +411,6 @@ void BeebWin::ApplyPrefs() ResetJoyMapToDefaultUser(); - InitMenu(); ShowMenu(true); @@ -600,8 +599,7 @@ void BeebWin::ResetBeebSystem(Model NewModelType, bool LoadRoms) Disc8271Reset(); if (EconetEnabled) EconetReset(); //Rob: Reset1770(); - AtoDInit(0); - AtoDInit(1); + AtoDInit(); SetRomMenu(); FreeDiscImage(0); // Keep the disc images loaded @@ -1694,7 +1692,6 @@ void BeebWin::TranslateJoystickButtons(int joyId, unsigned int buttons) /****************************************************************************/ void BeebWin::TranslateJoystick(int joyId) { - static const JOYCAPS dummyJoyCaps = { 0, 0, "", 0, 65535, 0, 65535, 0, 65535, @@ -3239,7 +3236,6 @@ void BeebWin::UpdateLEDMenu() { } void BeebWin::UpdateOptiMenu() { - CheckMenuItem(ID_BASIC_HARDWARE_ONLY, BasicHardwareOnly); CheckMenuItem(ID_TELETEXTHALFMODE, TeletextHalfMode); CheckMenuItem(ID_PSAMPLES, PartSamples); } @@ -3911,7 +3907,9 @@ void BeebWin::HandleCommand(int MenuId) { CheckMenuItem(m_MenuIdSticks[0], false); - AtoDDisable(0); + // Reset joystick position to centre + JoystickX[0] = 32767; + JoystickY[0] = 32767; SetJoystickButton(0, false); @@ -3931,12 +3929,7 @@ void BeebWin::HandleCommand(int MenuId) /* Initialise new selection */ m_MenuIdSticks[0] = MenuId; - AtoDEnable(0); - - if (JoystickEnabled[0]) - CheckMenuItem(m_MenuIdSticks[0], true); - else - m_MenuIdSticks[0] = 0; + CheckMenuItem(m_MenuIdSticks[0], true); } InitJoystick(false); @@ -3951,7 +3944,9 @@ void BeebWin::HandleCommand(int MenuId) { CheckMenuItem(m_MenuIdSticks[1], false); - AtoDDisable(1); + // Reset joystick position to centre + JoystickX[1] = 32767; + JoystickY[1] = 32767; SetJoystickButton(1, false); } @@ -3966,13 +3961,9 @@ void BeebWin::HandleCommand(int MenuId) /* Initialise new selection */ m_MenuIdSticks[1] = MenuId; - AtoDEnable(1); - - if (JoystickEnabled[1]) - CheckMenuItem(m_MenuIdSticks[1], true); - else - m_MenuIdSticks[1] = 0; + CheckMenuItem(m_MenuIdSticks[1], true); } + InitJoystick(false); break; @@ -4391,11 +4382,6 @@ void BeebWin::HandleCommand(int MenuId) UpdateOptiMenu(); break; - case ID_BASIC_HARDWARE_ONLY: - BasicHardwareOnly = !BasicHardwareOnly; - UpdateOptiMenu(); - break; - case ID_PSAMPLES: PartSamples = !PartSamples; UpdateOptiMenu(); diff --git a/Src/beebwinprefs.cpp b/Src/beebwinprefs.cpp index 5e0b1a8a..18146b56 100644 --- a/Src/beebwinprefs.cpp +++ b/Src/beebwinprefs.cpp @@ -485,8 +485,6 @@ void BeebWin::LoadPreferences() } } - if (!m_Preferences.GetBoolValue("Basic Hardware", BasicHardwareOnly)) - BasicHardwareOnly = false; if (!m_Preferences.GetBoolValue("Teletext Half Mode", TeletextHalfMode)) TeletextHalfMode = false; @@ -749,7 +747,6 @@ void BeebWin::SavePreferences(bool saveAll) m_Preferences.SetBinaryValue(CFG_TUBE_TYPE, &TubeType, 1); - m_Preferences.SetBoolValue("Basic Hardware", BasicHardwareOnly); m_Preferences.SetBoolValue("Teletext Half Mode", TeletextHalfMode); m_Preferences.SetBoolValue("TeletextAdapterEnabled", TeletextAdapterEnabled); m_Preferences.SetBoolValue("TeletextLocalhost", TeletextLocalhost); From dd89799c5ea80309cf82b3f21622d172e90e1923 Mon Sep 17 00:00:00 2001 From: Chris Needham Date: Sun, 21 Feb 2021 00:01:46 +0000 Subject: [PATCH 20/38] Removed "Basic Hardware Only" option --- Src/6502core.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Src/6502core.h b/Src/6502core.h index 89914360..5c1d5779 100644 --- a/Src/6502core.h +++ b/Src/6502core.h @@ -91,7 +91,6 @@ void AdjustForIORead(void); void AdjustForIOWrite(void); extern int OpCodes; -extern bool BasicHardwareOnly; void WriteInstructionCounts(const char *FileName); From c0063505dc9cc5c2e131a72330874b30de19f733 Mon Sep 17 00:00:00 2001 From: Chris Needham Date: Sun, 21 Feb 2021 00:03:38 +0000 Subject: [PATCH 21/38] Map additional joystick buttons to analogue port PB0 and PB1 --- Src/beebwin.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index d3c1a23a..6082ab0d 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -1703,8 +1703,6 @@ void BeebWin::TranslateJoystick(int joyId) const JOYCAPS* joyCaps = &m_JoystickState[joyId].Caps; DWORD joyIndex = m_JoystickState[joyId].JoyIndex; - memset(&joyInfoEx, 0, sizeof(joyInfoEx)); - joyInfoEx.dwSize = sizeof(joyInfoEx); joyInfoEx.dwFlags = JOY_RETURNALL | JOY_RETURNPOVCTS; @@ -1737,19 +1735,19 @@ void BeebWin::TranslateJoystick(int joyId) if (bbcIndex == 1 && m_PCStickForJoystick[0] == m_PCStickForJoystick[1] && m_PCAxesForJoystick[0] != m_PCAxesForJoystick[1]) { - // If both BBC joystick are mapped to the same PC gamepad, but not - // the same axes, use secondary button for second joystick - SetJoystickButton(bbcIndex, (joyInfoEx.dwButtons & JOY_BUTTON2) != 0); + // If both BBC joysticks are mapped to the same PC gamepad, but not + // the same axes, map buttons 2 and 4 to the Beeb's PB1 input + SetJoystickButton(bbcIndex, (joyInfoEx.dwButtons & (JOY_BUTTON2 | JOY_BUTTON4)) != 0); } else { - SetJoystickButton(bbcIndex, (joyInfoEx.dwButtons & JOY_BUTTON1) != 0); + SetJoystickButton(bbcIndex, (joyInfoEx.dwButtons & (JOY_BUTTON1 | JOY_BUTTON3)) != 0); } if (m_MenuIdSticks[1] == 0) { - // Second BBC joystick not enabled - map second button to BBC second button - SetJoystickButton(1, (joyInfoEx.dwButtons & JOY_BUTTON2) != 0); + // Second BBC joystick not enabled - map buttons 2 and 4 to Beeb's PB1 input + SetJoystickButton(1, (joyInfoEx.dwButtons & (JOY_BUTTON2 | JOY_BUTTON4)) != 0); } if (m_PCAxesForJoystick[bbcIndex] == 1) From 39c9a91db11e6ed4592fde83eb25771859992c31 Mon Sep 17 00:00:00 2001 From: Chris Needham Date: Sun, 21 Feb 2021 14:09:32 +0000 Subject: [PATCH 22/38] Change Digital Mousestick handling This fixes an issue where the joystick position would never be centred. With this change, the joystick is centred when the mouse is in the centre of the BeebEm window. --- Src/beebwin.cpp | 46 +++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index 6082ab0d..fca057ca 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -1829,31 +1829,47 @@ void BeebWin::SetMousestickButton(int index, bool button) /****************************************************************************/ void BeebWin::ScaleMousestick(unsigned int x, unsigned int y) { - static int lastx[2] = { 32768, 32768 }; - static int lasty[2] = { 32768, 32768 }; - for (int index = 0; index < 2; ++index) { + int XPos = (m_XWinSize - x) * 65535 / m_XWinSize; + int YPos = (m_YWinSize - y) * 65535 / m_YWinSize; + if (index == 0 && m_MenuIdSticks[0] == IDM_ANALOGUE_MOUSESTICK || index == 1 && m_MenuIdSticks[1] == IDM_JOY2_ANALOGUE_MOUSESTICK) { - JoystickX[index] = (m_XWinSize - x) * 65535 / m_XWinSize; - JoystickY[index] = (m_YWinSize - y) * 65535 / m_YWinSize; + JoystickX[index] = XPos; + JoystickY[index] = YPos; } else if (index == 0 && m_MenuIdSticks[0] == IDM_DIGITAL_MOUSESTICK || - index == 1 && m_MenuIdSticks[1]== IDM_JOY2_DIGITAL_MOUSESTICK) + index == 1 && m_MenuIdSticks[1] == IDM_JOY2_DIGITAL_MOUSESTICK) { - int dx = x - lastx[index]; - int dy = y - lasty[index]; - - if (dx > 4) JoystickX[index] = 0; - if (dx < -4) JoystickX[index] = 65535; + const int Threshold = 2000; - if (dy > 4) JoystickY[index] = 0; - if (dy < -4) JoystickY[index] = 65535; + if (XPos < 32768 - Threshold) + { + JoystickX[index] = 0; + } + else if (XPos > 32768 + Threshold) + { + JoystickX[index] = 65535; + } + else + { + JoystickX[index] = 32768; + } - lastx[index] = x; - lasty[index] = y; + if (YPos < 32768 - Threshold) + { + JoystickY[index] = 0; + } + else if (YPos > 32768 + Threshold) + { + JoystickY[index] = 65535; + } + else + { + JoystickY[index] = 32768; + } } } } From 16cfa7537d66e9fb386603006e295d80cdeb3a31 Mon Sep 17 00:00:00 2001 From: Tadeusz Kijkowski Date: Tue, 23 Feb 2021 09:21:22 +0100 Subject: [PATCH 23/38] More Joysticks - Moved joystick handling to separate class and files - Up to 4 joysticks mapped to keyboard --- Src/BeebEm.vcxproj | 3 + Src/BeebEm.vcxproj.filters | 9 + Src/JoystickHandler.cpp | 1086 ++++++++++++++++++++++++++++++++++++ Src/JoystickHandler.h | 169 ++++++ Src/SelectKeyDialog.cpp | 21 +- Src/atodconv.cpp | 2 +- Src/atodconv.h | 6 +- Src/beebwin.cpp | 815 ++------------------------- Src/beebwin.h | 98 +--- Src/beebwinio.cpp | 299 +--------- Src/beebwinprefs.cpp | 57 +- Src/keymapping.h | 40 ++ 12 files changed, 1361 insertions(+), 1244 deletions(-) create mode 100644 Src/JoystickHandler.cpp create mode 100644 Src/JoystickHandler.h create mode 100644 Src/keymapping.h diff --git a/Src/BeebEm.vcxproj b/Src/BeebEm.vcxproj index ed032c12..66fc7c88 100644 --- a/Src/BeebEm.vcxproj +++ b/Src/BeebEm.vcxproj @@ -182,6 +182,7 @@ + @@ -342,6 +343,8 @@ + + diff --git a/Src/BeebEm.vcxproj.filters b/Src/BeebEm.vcxproj.filters index 86d4a7b5..0a71d39c 100644 --- a/Src/BeebEm.vcxproj.filters +++ b/Src/BeebEm.vcxproj.filters @@ -246,6 +246,9 @@ Source Files + + Source Files + @@ -494,6 +497,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/Src/JoystickHandler.cpp b/Src/JoystickHandler.cpp new file mode 100644 index 00000000..481731ab --- /dev/null +++ b/Src/JoystickHandler.cpp @@ -0,0 +1,1086 @@ +/**************************************************************** +BeebEm - BBC Micro and Master 128 Emulator +Copyright (C) 1994 Nigel Magnay +Copyright (C) 1997 Mike Wyatt +Copyright (C) 1998 Robert Schmidt +Copyright (C) 2001 Richard Gellman +Copyright (C) 2004 Ken Lowe +Copyright (C) 2004 Rob O'Donnell +Copyright (C) 2005 Jon Welch +Copyright (C) 2021 Chris Needham, Tadeusz Kijkowski + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +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. + +You should have received a copy of the GNU General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. +****************************************************************/ + +#include "beebwin.h" +#include "beebemrc.h" +#include "userkybd.h" +#include "sysvia.h" +#include "filedialog.h" +#include "SelectKeyDialog.h" + +#define JOYMAP_TOKEN "*** BeebEm Joystick Map ***" + +static const char *CFG_OPTIONS_STICKS = "Sticks"; +static const char *CFG_OPTIONS_STICKS2 = "Sticks2"; +static const char *CFG_OPTIONS_STICKS_TO_KEYS = "SticksToKeys"; +static const char *CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP = "AutoloadJoystickMap"; +static const char *CFG_OPTIONS_STICK_DEADBAND = "StickToKeysDeadBand"; + +#define DEFAULT_JOY_DEADBAND 4096 + +/*****************************************************************************/ +HWND JoystickHandler::GetHwnd() { return m_BeebWin->m_hWnd; } + +int JoystickHandler::GetXWinSize() { return m_BeebWin->m_XWinSize; } + +int JoystickHandler::GetYWinSize() { return m_BeebWin->m_YWinSize; } + +void JoystickHandler::CheckMenuItem(UINT id, bool checked) { m_BeebWin->CheckMenuItem(id, checked); } + +void JoystickHandler::EnableMenuItem(UINT id, bool enabled) { m_BeebWin->EnableMenuItem(id, enabled); } + +/*****************************************************************************/ +/* All this stuff will go away when joystick configuration is done via dialog + */ +struct JoystickMenuIdsType { + UINT Joysticks[2]; + UINT Axes[3]; + UINT AnalogMousestick; + UINT DigitalMousestick; +}; + +/*****************************************************************************/ +constexpr JoystickMenuIdsType JoystickMenuIds[NUM_BBC_JOYSTICKS] { + { + { IDM_JOYSTICK, IDM_JOY1_PCJOY2 }, + { IDM_JOY1_PRIMARY, IDM_JOY1_SECONDARY1, IDM_JOY1_SECONDARY2 }, + IDM_ANALOGUE_MOUSESTICK, + IDM_DIGITAL_MOUSESTICK, + }, + { + { IDM_JOY2_PCJOY1, IDM_JOY2_PCJOY2 }, + { IDM_JOY2_PRIMARY, IDM_JOY2_SECONDARY1, IDM_JOY2_SECONDARY2 }, + IDM_JOY2_ANALOGUE_MOUSESTICK, + IDM_JOY2_DIGITAL_MOUSESTICK, + } +}; + + +/****************************************************************************/ +int JoystickHandler::MenuIdToStick(int bbcIdx, UINT menuId) +{ + for (int pcIdx = 0; pcIdx < _countof(JoystickMenuIdsType::Joysticks); ++pcIdx) + { + if (menuId == JoystickMenuIds[bbcIdx].Joysticks[pcIdx]) + return pcIdx + 1; + } + return 0; +} + +/****************************************************************************/ +UINT JoystickHandler::StickToMenuId(int bbcIdx, int pcStick) +{ + if (pcStick > 0 && pcStick - 1 < _countof(JoystickMenuIdsType::Joysticks)) + return JoystickMenuIds[bbcIdx].Joysticks[pcStick - 1]; + return 0; +} + +/****************************************************************************/ +int JoystickHandler::MenuIdToAxes(int bbcIdx, UINT menuId) +{ + for (int axesIdx = 0; axesIdx < _countof(JoystickMenuIdsType::Axes); ++axesIdx) + { + if (menuId == JoystickMenuIds[bbcIdx].Axes[axesIdx]) + return axesIdx + 1; + } +} + +/****************************************************************************/ +UINT JoystickHandler::AxesToMenuId(int bbcIdx, int pcAxes) +{ + if (pcAxes > 0 && pcAxes - 1 < _countof(JoystickMenuIdsType::Axes)) + return JoystickMenuIds[bbcIdx].Axes[pcAxes - 1]; + return 0; +} + +/****************************************************************************/ +// Update joystick configuration from checked menu items +void JoystickHandler::UpdateJoystickConfig(int bbcIdx) +{ + m_JoystickConfig[bbcIdx].Enabled = (m_MenuIdAxes[bbcIdx] != 0); + m_JoystickConfig[bbcIdx].PCStick = MenuIdToStick(bbcIdx, m_MenuIdSticks[bbcIdx]); + m_JoystickConfig[bbcIdx].PCAxes = MenuIdToAxes(bbcIdx, m_MenuIdAxes[bbcIdx]); + m_JoystickConfig[bbcIdx].AnalogMousestick = (m_MenuIdAxes[bbcIdx] == JoystickMenuIds[bbcIdx].AnalogMousestick); + m_JoystickConfig[bbcIdx].DigitalMousestick = (m_MenuIdAxes[bbcIdx] == JoystickMenuIds[bbcIdx].DigitalMousestick); +} + +/****************************************************************************/ +// Check if this PC joystick is assigned to this BBC joystick +bool JoystickHandler::IsPCJoystickAssigned(int pcIdx, int bbcIdx) +{ + return m_JoystickConfig[bbcIdx].PCStick == pcIdx + 1; +} + +/****************************************************************************/ +// Check if PC joystick is assigned to any BBC joystick +// TODO: Update m_PCStickForJoystick early and use it here +bool JoystickHandler::IsPCJoystickOn(int pcIdx) +{ + if (pcIdx >= _countof(JoystickMenuIdsType::Joysticks)) + return false; + + for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) + { + if (IsPCJoystickAssigned(pcIdx, bbcIdx)) + return true; + } + + return false; +} + +/*****************************************************************************/ +void JoystickHandler::InitMenu() +{ + for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) + { + for (int pcIdx = 0; pcIdx < _countof(JoystickMenuIdsType::Joysticks); ++pcIdx) + { + CheckMenuItem(JoystickMenuIds[bbcIdx].Joysticks[pcIdx], false); + } + CheckMenuItem(JoystickMenuIds[bbcIdx].AnalogMousestick, false); + CheckMenuItem(JoystickMenuIds[bbcIdx].DigitalMousestick, false); + if (m_MenuIdSticks[bbcIdx] != 0) + CheckMenuItem(m_MenuIdSticks[bbcIdx], true); + + for (int axesIdx = 0; axesIdx < _countof(JoystickMenuIdsType::Joysticks); ++axesIdx) + { + CheckMenuItem(JoystickMenuIds[bbcIdx].Axes[axesIdx], false); + } + if (m_MenuIdAxes[bbcIdx] != 0) + CheckMenuItem(m_MenuIdAxes[bbcIdx], true); + + UpdateJoystickConfig(bbcIdx); + } + + CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); + CheckMenuItem(IDM_AUTOLOADJOYMAP, m_AutoloadJoystickMap); +} + +/****************************************************************************/ +void JoystickHandler::UpdateJoystickMenu() +{ + bool EnableInit = false; + + // Enable "Initialize Joysticks" menu item if any more joysticks could be possibly captured (or always?) + for (int pcIdx = 0; pcIdx < NUM_PC_JOYSTICKS; ++pcIdx) + { + if ((m_JoystickToKeys || IsPCJoystickOn(pcIdx)) && !m_PCJoystickState[pcIdx].Captured) + EnableInit = true; + } + EnableMenuItem(IDM_INIT_JOYSTICK, EnableInit); + + // Check "Initialize Joysticks" menu item if any joystick is already captured + CheckMenuItem(IDM_INIT_JOYSTICK, m_PCJoystickState[0].Captured); + + // Enable axes menu items if any PC joystick is assigned to the related BBC joystick + for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) + { + bool EnableAxes = false; + for (int pcIdx = 0; pcIdx < NUM_PC_JOYSTICKS; ++pcIdx) + { + if (IsPCJoystickAssigned(pcIdx, bbcIdx)) + EnableAxes = true; + } + for (int axesIdx = 0; axesIdx < _countof(JoystickMenuIdsType::Axes); ++axesIdx) + EnableMenuItem(JoystickMenuIds[bbcIdx].Axes[axesIdx], EnableAxes); + } +} + +/****************************************************************************/ +void JoystickHandler::ProcessMenuCommand(int bbcIdx, UINT MenuId) +{ + /* Disable current selection */ + if (m_MenuIdSticks[bbcIdx] != 0) + { + CheckMenuItem(m_MenuIdSticks[bbcIdx], false); + + // Reset joystick position to centre + JoystickX[bbcIdx] = 32767; + JoystickY[bbcIdx] = 32767; + + SetJoystickButton(bbcIdx, false); + + if (bbcIdx == 0 && !m_JoystickConfig[1].Enabled) + { + SetJoystickButton(1, false); + } + } + + if (MenuId == m_MenuIdSticks[bbcIdx]) + { + /* Joystick switched off completely */ + m_MenuIdSticks[bbcIdx] = 0; + } + else + { + /* Initialise new selection */ + m_MenuIdSticks[bbcIdx] = MenuId; + + CheckMenuItem(m_MenuIdSticks[bbcIdx], true); + } + UpdateJoystickConfig(bbcIdx); + InitJoystick(false); + +} + +/****************************************************************************/ +void JoystickHandler::ProcessAxesMenuCommand(int bbcIdx, UINT MenuId) +{ + CheckMenuItem(m_MenuIdAxes[bbcIdx], false); + m_MenuIdAxes[bbcIdx] = MenuId; + UpdateJoystickConfig(bbcIdx); + CheckMenuItem(m_MenuIdAxes[bbcIdx], true); +} + +/****************************************************************************/ +void JoystickHandler::ToggleJoystickToKeys(void) +{ + m_JoystickToKeys = !m_JoystickToKeys; + InitJoystick(false); + CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); +} + +/****************************************************************************/ +void JoystickHandler::ToggleAutoloadJoystickMap(void) +{ + m_AutoloadJoystickMap = !m_AutoloadJoystickMap; + CheckMenuItem(IDM_AUTOLOADJOYMAP, m_AutoloadJoystickMap); +} + +/****************************************************************************/ +void JoystickHandler::ResetJoystick(void) +{ + for (int pcIdx = 0; pcIdx < NUM_PC_JOYSTICKS; ++pcIdx) + { + m_PCJoystickState[pcIdx].Captured = false; + TranslateJoystick(pcIdx); + } + +} + +/****************************************************************************/ +bool JoystickHandler::InitJoystick(bool verbose) +{ + bool Success = true; + + ResetJoystick(); + + // TODO: Joystick ordering here + if (IsPCJoystickOn(0) || IsPCJoystickOn(1) || m_JoystickToKeys) + { + for (int pcIdx = 0; pcIdx < NUM_PC_JOYSTICKS; ++pcIdx) + { + Success = CaptureJoystick(pcIdx, verbose); + if (!Success) + break; + } + + if (!m_JoystickTimerRunning) + { + SetTimer(GetHwnd(), 3, 20, NULL); + m_JoystickTimerRunning = true; + } + } + else + { + if (m_JoystickTimerRunning) + { + KillTimer(GetHwnd(), 3); + m_JoystickTimerRunning = false; + } + } + + UpdateJoystickMenu(); + + return Success; +} + +/****************************************************************************/ +// TODO: Change it all - scan all joysticks first and then order and assign +bool JoystickHandler::CaptureJoystick(int Index, bool verbose) +{ + bool success = false; + + DWORD JoyIndex; + DWORD Result; + JOYINFOEX joyInfoEx; + UINT numDevs = joyGetNumDevs(); + + // Scan for first present joystick index. It doesn't have to be + // consecutive number. + if (Index == 0) + JoyIndex = 0; + else + JoyIndex = m_PCJoystickState[Index - 1].JoyIndex + 1; + + // Find first joystick that is known AND connected + Result = JOYERR_UNPLUGGED; + while (Result != JOYERR_NOERROR && JoyIndex != numDevs) + { + memset(&joyInfoEx, 0, sizeof(joyInfoEx)); + joyInfoEx.dwSize = sizeof(joyInfoEx); + joyInfoEx.dwFlags = JOY_RETURNBUTTONS; + + Result = joyGetDevCaps(JoyIndex, &m_PCJoystickState[Index].Caps, sizeof(JOYCAPS)); + if (Result == JOYERR_NOERROR) + Result = joyGetPosEx(JoyIndex, &joyInfoEx); + if (Result != JOYERR_NOERROR) + ++JoyIndex; + } + + if (Result == ERROR_SUCCESS) + { + m_PCJoystickState[Index].Captured = true; + m_PCJoystickState[Index].JoyIndex = JoyIndex; + success = true; + } + else if (verbose) + { + if (Result == ERROR_DEVICE_NOT_CONNECTED || Result == JOYERR_UNPLUGGED) + { + char str[100]; + sprintf(str, "Joystick %d is not plugged in", Index + 1); + + MessageBox(GetHwnd(), str, WindowTitle, MB_OK | MB_ICONWARNING); + } + else + { + char str[100]; + sprintf(str, "Failed to initialise joystick %d", Index + 1); + + MessageBox(GetHwnd(), str, WindowTitle, MB_OK | MB_ICONWARNING); + } + } + + return success; +} + +/****************************************************************************/ +void JoystickHandler::SetJoystickButton(int index, bool value) +{ + JoystickButton[index] = value; +} + +/****************************************************************************/ +void JoystickHandler::ScaleJoystick(int index, unsigned int x, unsigned int y, + unsigned int minX, unsigned int minY, unsigned int maxX, unsigned int maxY) +{ + /* Scale and reverse the readings */ + JoystickX[index] = (int)((double)(maxX - x) * 65535.0 / + (double)(maxX - minX)); + JoystickY[index] = (int)((double)(maxY - y) * 65535.0 / + (double)(maxY - minY)); +} + +/****************************************************************************/ +void JoystickHandler::SetMousestickButton(int index, bool value) +{ + if (index == 0) + { + // Left mouse button + for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) + { + if (m_JoystickConfig[bbcIdx].AnalogMousestick || + m_JoystickConfig[bbcIdx].DigitalMousestick) + { + SetJoystickButton(bbcIdx, value); + } + } + } + else + { + // Map right mouse button to secondary fire if second joystick is + // not enabled. + if (!m_JoystickConfig[1].Enabled && + (m_JoystickConfig[0].AnalogMousestick || m_JoystickConfig[0].DigitalMousestick)) + { + SetJoystickButton(1, value); + } + } +} + +/****************************************************************************/ +void JoystickHandler::ScaleMousestick(unsigned int x, unsigned int y) +{ + for (int index = 0; index < 2; ++index) + { + int XPos = (GetXWinSize() - x) * 65535 / GetXWinSize(); + int YPos = (GetYWinSize() - y) * 65535 / GetYWinSize(); + + if (m_JoystickConfig[index].AnalogMousestick) + { + JoystickX[index] = XPos; + JoystickY[index] = YPos; + } + else if (m_JoystickConfig[index].DigitalMousestick) + { + const int Threshold = 2000; + + if (XPos < 32768 - Threshold) + { + JoystickX[index] = 0; + } + else if (XPos > 32768 + Threshold) + { + JoystickX[index] = 65535; + } + else + { + JoystickX[index] = 32768; + } + + if (YPos < 32768 - Threshold) + { + JoystickY[index] = 0; + } + else if (YPos > 32768 + Threshold) + { + JoystickY[index] = 65535; + } + else + { + JoystickY[index] = 32768; + } + } + } +} + +/****************************************************************************/ +unsigned int JoystickHandler::GetJoystickAxes(const JOYCAPS& caps, int deadband, const JOYINFOEX& joyInfoEx) +{ + using Info = JOYINFOEX; + using Caps = JOYCAPS; + unsigned int axes = 0; + + auto Scale = [&caps, &joyInfoEx](DWORD Info::*pos, UINT Caps::*min, UINT Caps::*max) { + return (int)((double)(joyInfoEx.*pos - caps.*min) * 65535.0 / + (double)(caps.*max - caps.*min)); + }; + + auto Detect = [&axes, deadband](const int val, const int nbit, const int pbit) { + if (val < 32768 - deadband) + axes |= (1 << nbit); + else if (val > 32768 + deadband) + axes |= (1 << pbit); + }; + + int ty = Scale(&Info::dwYpos, &Caps::wYmin, &Caps::wYmax); + int tx = Scale(&Info::dwXpos, &Caps::wXmin, &Caps::wXmax); + int tz = Scale(&Info::dwZpos, &Caps::wZmin, &Caps::wZmin); + int tr = Scale(&Info::dwRpos, &Caps::wRmin, &Caps::wRmax); + int tu = Scale(&Info::dwUpos, &Caps::wUmin, &Caps::wUmax); + int tv = Scale(&Info::dwVpos, &Caps::wVmin, &Caps::wVmax); + + Detect(ty, JOYSTICK_AXIS_UP, JOYSTICK_AXIS_DOWN); + Detect(tx, JOYSTICK_AXIS_LEFT, JOYSTICK_AXIS_RIGHT); + Detect(tz, JOYSTICK_AXIS_Z_N, JOYSTICK_AXIS_Z_P); + Detect(tr, JOYSTICK_AXIS_R_N, JOYSTICK_AXIS_R_P); + Detect(tu, JOYSTICK_AXIS_U_N, JOYSTICK_AXIS_U_P); + Detect(tv, JOYSTICK_AXIS_V_N, JOYSTICK_AXIS_V_P); + + if (joyInfoEx.dwPOV != JOY_POVCENTERED) + { + if (joyInfoEx.dwPOV >= 29250 || joyInfoEx.dwPOV < 6750) + axes |= (1 << JOYSTICK_AXIS_HAT_UP); + if (joyInfoEx.dwPOV >= 2250 && joyInfoEx.dwPOV < 15750) + axes |= (1 << JOYSTICK_AXIS_HAT_RIGHT); + if (joyInfoEx.dwPOV >= 11250 && joyInfoEx.dwPOV < 24750) + axes |= (1 << JOYSTICK_AXIS_HAT_DOWN); + if (joyInfoEx.dwPOV >= 20250 && joyInfoEx.dwPOV < 33750) + axes |= (1 << JOYSTICK_AXIS_HAT_LEFT); + } + + return axes; +} + +/****************************************************************************/ +// Translate joystick position changes to key up or down message +void JoystickHandler::TranslateAxes(int joyId, unsigned int axesState) +{ + int vkeys = BEEB_VKEY_JOY_START + joyId * (JOYSTICK_MAX_AXES + JOYSTICK_MAX_BTNS); + unsigned int& prevAxes = m_PCJoystickState[joyId].PrevAxes; + + if (axesState != prevAxes) + { + for (int axId = 0; axId < JOYSTICK_AXES_COUNT; ++axId) + { + if ((axesState & ~prevAxes) & (1 << axId)) + { + TranslateOrSendKey(vkeys + axId, false); + } + else if ((~axesState & prevAxes) & (1 << axId)) + { + TranslateOrSendKey(vkeys + axId, true); + } + } + prevAxes = axesState; + } +} + +/****************************************************************************/ +void JoystickHandler::TranslateJoystickButtons(int joyId, unsigned int buttons) +{ + const int BUTTON_COUNT = 32; + + unsigned int& prevBtns = m_PCJoystickState[joyId].PrevBtns; + int vkeys = BEEB_VKEY_JOY_START + JOYSTICK_MAX_AXES + + joyId * (JOYSTICK_MAX_AXES + JOYSTICK_MAX_BTNS); + + if (buttons != prevBtns) + { + for (int btnId = 0; btnId < BUTTON_COUNT; ++btnId) + { + if ((buttons & ~prevBtns) & (1 << btnId)) + { + TranslateOrSendKey(vkeys + btnId, false); + } + else if ((~buttons & prevBtns) & (1 << btnId)) + { + TranslateOrSendKey(vkeys + btnId, true); + } + } + + prevBtns = buttons; + } +} + +/****************************************************************************/ +void JoystickHandler::TranslateOrSendKey(int vkey, bool keyUp) +{ + if (m_JoystickTarget == nullptr) + { + int row, col; + m_BeebWin->TranslateKey(vkey, keyUp, row, col); + } + else if (!keyUp) + { + // Keyboard input dialog is visible - translate button down to key down + // message and send to dialog + PostMessage(m_JoystickTarget, WM_KEYDOWN, vkey, 0); + } +} + +/****************************************************************************/ +void JoystickHandler::TranslateJoystick(int joyId) +{ + static const JOYCAPS dummyJoyCaps = { + 0, 0, "", + 0, 65535, 0, 65535, 0, 65535, + 16, 10, 1000, + 0, 65535, 0, 65535, 0, 65535 + }; + bool success = false; + JOYINFOEX joyInfoEx; + + const JOYCAPS* joyCaps = &m_PCJoystickState[joyId].Caps; + DWORD joyIndex = m_PCJoystickState[joyId].JoyIndex; + + joyInfoEx.dwSize = sizeof(joyInfoEx); + joyInfoEx.dwFlags = JOY_RETURNALL | JOY_RETURNPOVCTS; + + if (m_PCJoystickState[joyId].Captured && + joyGetPosEx(joyIndex, &joyInfoEx) == JOYERR_NOERROR) + success = true; + + // If joystick is disabled or error occurs, reset all axes and buttons + if (!success) + { + joyCaps = &dummyJoyCaps; + joyInfoEx.dwXpos = joyInfoEx.dwYpos = joyInfoEx.dwZpos = 32768; + joyInfoEx.dwRpos = joyInfoEx.dwUpos = joyInfoEx.dwVpos = 32768; + joyInfoEx.dwPOV = JOY_POVCENTERED; + // Reset 'captured' flag and update menu entry + m_PCJoystickState[joyId].Captured = false; + UpdateJoystickMenu(); + } + + // PC joystick to BBC joystick + for (int bbcIndex = 0; bbcIndex < NUM_BBC_JOYSTICKS; ++bbcIndex) + { + if (IsPCJoystickAssigned(joyId, bbcIndex)) + { + if (bbcIndex == 1 && m_JoystickConfig[0].PCStick == m_JoystickConfig[1].PCStick + && m_JoystickConfig[0].PCAxes != m_JoystickConfig[1].PCAxes) + { + // If both BBC joysticks are mapped to the same PC gamepad, but not + // the same axes, map buttons 2 and 4 to the Beeb's PB1 input + SetJoystickButton(bbcIndex, (joyInfoEx.dwButtons & (JOY_BUTTON2 | JOY_BUTTON4)) != 0); + } + else + { + SetJoystickButton(bbcIndex, (joyInfoEx.dwButtons & (JOY_BUTTON1 | JOY_BUTTON3)) != 0); + } + + if (bbcIndex == 0 && !m_JoystickConfig[1].Enabled) + { + // Second BBC joystick not enabled - map buttons 2 and 4 to Beeb's PB1 input + SetJoystickButton(1, (joyInfoEx.dwButtons & (JOY_BUTTON2 | JOY_BUTTON4)) != 0); + } + + int axesConfig = m_JoystickConfig[bbcIndex].PCAxes; + if (axesConfig == 1) + { + ScaleJoystick(bbcIndex, joyInfoEx.dwXpos, joyInfoEx.dwYpos, + joyCaps->wXmin, joyCaps->wYmin, + joyCaps->wXmax, joyCaps->wYmax); + } + else if (axesConfig == 2) + { + ScaleJoystick(bbcIndex, joyInfoEx.dwUpos, joyInfoEx.dwRpos, + joyCaps->wUmin, joyCaps->wRmin, + joyCaps->wUmax, joyCaps->wRmax); + } + else if (axesConfig == 3) + { + ScaleJoystick(bbcIndex, joyInfoEx.dwZpos, joyInfoEx.dwRpos, + joyCaps->wZmin, joyCaps->wRmin, + joyCaps->wZmax, joyCaps->wRmax); + } + } + } + + // Joystick to keyboard mapping + if (m_JoystickToKeys) + { + auto axes = GetJoystickAxes(*joyCaps, m_Deadband, joyInfoEx); + TranslateAxes(joyId, axes); + TranslateJoystickButtons(joyId, joyInfoEx.dwButtons); + m_PCJoystickState[joyId].JoystickToKeysActive = true; + } + + // Make sure to reset keyboard state + if (!m_JoystickToKeys && m_PCJoystickState[joyId].JoystickToKeysActive) + { + TranslateAxes(joyId, 0); + TranslateJoystickButtons(joyId, 0); + m_PCJoystickState[joyId].JoystickToKeysActive = false; + } +} + +/*****************************************************************************/ +void JoystickHandler::UpdateJoysticks() +{ + for (int idx = 0; idx < NUM_PC_JOYSTICKS; ++idx) + { + if (m_PCJoystickState[0].Captured) + TranslateJoystick(idx); + } +} + +/*****************************************************************************/ +// Look for file specific joystick map +void JoystickHandler::CheckForJoystickMap(const char *path) +{ + char file[_MAX_PATH]; + char drive[_MAX_DRIVE]; + char dir[_MAX_DIR]; + char filename[_MAX_FNAME]; + + if (!m_AutoloadJoystickMap || !path || !path[0]) + return; + + _splitpath(path, drive, dir, filename, NULL); + + // Look for prefs file with the same name with ".jmap" extension + _makepath(file, drive, dir, filename, "jmap"); + + if (GetFileAttributes(file) != INVALID_FILE_ATTRIBUTES) + { + ReadJoyMap(file, &JoystickMap); + } + else + { + // Mapping file not found - reset to default + ResetJoyMapToDefaultUser(); + + // Set jmap path even if the file doesn't exist + strcpy(m_JoystickMapPath, file); + } +} + +/****************************************************************************/ +void JoystickHandler::ResetJoystickMap() +{ + if (MessageBox(GetHwnd(), "Clear joystick to keyboard mapping table?", WindowTitle, MB_YESNO | MB_ICONQUESTION) != IDYES) + return; + + ResetJoyMap(&JoystickMap); +} + +/****************************************************************************/ +void JoystickHandler::ResetJoyMap(JoyMap* joymap) +{ + // Initialize all input to unassigned + for (auto& mapping : *joymap) + { + mapping[0].row = mapping[1].row = UNASSIGNED_ROW; + mapping[0].col = mapping[1].col = 0; + mapping[0].shift = false; + mapping[1].shift = true; + } +} + +/****************************************************************************/ +void JoystickHandler::ResetJoyMapToDefaultUser(void) +{ + char keymap[_MAX_PATH]; + strcpy(keymap, "DefaultUser.jmap"); + m_BeebWin->GetDataPath(m_BeebWin->m_UserDataPath, keymap); + ResetJoyMap(&JoystickMap); + if (GetFileAttributes(keymap) != INVALID_FILE_ATTRIBUTES) + ReadJoyMap(keymap, &JoystickMap); +} + +/****************************************************************************/ +static void makeupper(char* str) +{ + if (str == NULL) + return; + while (*str) + { + *str = static_cast(toupper(*str)); + ++str; + } +} + +/****************************************************************************/ +bool JoystickHandler::ReadJoyMap(const char *filename, JoyMap *joymap) +{ + bool success = true; + FILE *infile = m_BeebWin->OpenReadFile(filename, "key map", JOYMAP_TOKEN "\n"); + char buf[256]; + JoyMap newJoyMap; + int line = 1; + + if (infile == NULL) + return false; + + ResetJoyMap(&newJoyMap); + + while (fgets(buf, 255, infile) != NULL) + { + char *ptr, *inputName, *keyName1, *keyName2; + + ++line; + + // Read at most three tokens from line + inputName = strtok_s(buf, " \t\n", &ptr); + keyName1 = strtok_s(NULL, " \t\n", &ptr); + keyName2 = strtok_s(NULL, " \t\n", &ptr); + + // Ignore empty lines or lines starting with '#' + if (inputName == NULL || inputName[0] == '#') + continue; + + if (keyName1 == NULL) + { + char errstr[500]; + sprintf(errstr, "Invalid line in joystick mapping file:\n %s\n line %d\n", + filename, line); + MessageBox(GetHwnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); + success = false; + break; + } + + // Get vkey number and mapping entry from input name + int vkey = SelectKeyDialog::JoyVKeyByName(inputName); + if (vkey < BEEB_VKEY_JOY_START || vkey >= BEEB_VKEY_JOY_END) + { + char errstr[500]; + sprintf(errstr, "Invalid input name in joystick mapping file:\n %s\n line %d\n", + filename, line); + MessageBox(GetHwnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); + success = false; + break; + } + KeyMapping* mapping = newJoyMap[vkey - BEEB_VKEY_JOY_START]; + + // Get shift state and BBC key from key name + bool shift1 = false; + makeupper(keyName1); + + if (strncmp(keyName1, "SH+", 3) == 0) + { + shift1 = true; + keyName1 += 3; + } + + const BBCKey* key1 = GetBBCKeyByName(keyName1); + if (key1->row == UNASSIGNED_ROW && strcmp(keyName1, "NONE") != 0) + { + char errstr[500]; + sprintf(errstr, "Invalid key name in joystick mapping file:\n %s\n line %d\n", + filename, line); + MessageBox(GetHwnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); + success = false; + break; + } + + if (keyName2 == NULL) + { + // Shifted and unshifted input map to the same key + mapping[0].row = mapping[1].row = key1->row; + mapping[0].col = mapping[1].col = key1->column; + mapping[0].shift = false; + mapping[1].shift = true; + } + else + { + bool shift2 = false; + makeupper(keyName2); + + if (strncmp(keyName2, "SH+", 3) == 0) + { + shift2 = true; + keyName2 += 3; + } + + const BBCKey* key2 = GetBBCKeyByName(keyName2); + if (key2->row == UNASSIGNED_ROW && strcmp(keyName2, "NONE") != 0) + { + char errstr[500]; + sprintf(errstr, "Invalid key name in joystick mapping file:\n %s\n line %d\n", + filename, line); + MessageBox(GetHwnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); + success = false; + break; + } + + mapping[0].row = key1->row; + mapping[0].col = key1->column; + mapping[0].shift = shift1; + mapping[1].row = key2->row; + mapping[1].col = key2->column; + mapping[1].shift = shift2; + } + } + + if (success) + { + memcpy(joymap, &newJoyMap, sizeof(newJoyMap)); + } + + return success; +} + +/****************************************************************************/ +void JoystickHandler::LoadJoystickMap() +{ + char DefaultPath[_MAX_PATH]; + char FileName[_MAX_PATH]; + const char* filter = "Joystick Map File (*.jmap)\0*.jmap\0"; + + // If Autoload is enabled, look for joystick mapping near disks, + // otherwise start in user data path. + if (m_AutoloadJoystickMap) + { + m_BeebWin->GetPreferences().GetStringValue("DiscsPath", DefaultPath); + m_BeebWin->GetDataPath(m_BeebWin->GetUserDataPath(), DefaultPath); + } + else + { + strcpy(DefaultPath, m_BeebWin->GetUserDataPath()); + } + + FileDialog fileDialog(GetHwnd(), FileName, sizeof(FileName), DefaultPath, filter); + + if (m_JoystickMapPath[0] == '\0' && !m_AutoloadJoystickMap) + { + fileDialog.SetInitial("DefaultUser.jmap"); + } + else + { + fileDialog.SetInitial(m_JoystickMapPath); + } + + if (fileDialog.Open()) + { + if (ReadJoyMap(FileName, &JoystickMap)) + strcpy(m_JoystickMapPath, FileName); + } +} + +/****************************************************************************/ +bool JoystickHandler::WriteJoyMap(const char *filename, JoyMap *joymap) +{ + FILE* outfile = m_BeebWin->OpenWriteFile(filename, "joystick map"); + + if (outfile == NULL) + return false; + + fprintf(outfile, JOYMAP_TOKEN "\n\n"); + + for (int i = 0; i < BEEB_VKEY_JOY_COUNT; ++i) + { + KeyMapping* mapping = (*joymap)[i]; + if (mapping[0].row != UNASSIGNED_ROW + || mapping[1].row != UNASSIGNED_ROW) + { + const char* inputName = SelectKeyDialog::KeyName(i + BEEB_VKEY_JOY_START); + + if (mapping[0].row == mapping[1].row + && mapping[0].col == mapping[1].col + && !mapping[0].shift && mapping[1].shift) + { + // Shifted and unshifted input map to the same key - write one name + const BBCKey* key = GetBBCKeyByRowAndCol(mapping[0].row, mapping[0].col); + fprintf(outfile, "%-13s %s\n", + inputName, + key->name); + } + else + { + // Separate mapping for shifted and unshifted + const BBCKey* key1 = GetBBCKeyByRowAndCol(mapping[0].row, mapping[0].col); + bool key1shift = mapping[0].shift && mapping[0].row != UNASSIGNED_ROW; + const BBCKey* key2 = GetBBCKeyByRowAndCol(mapping[1].row, mapping[1].col); + bool key2shift = mapping[1].shift && mapping[1].row != UNASSIGNED_ROW; + fprintf(outfile, "%-13s %s%-*s %s%s\n", + inputName, + key1shift ? "SH+" : "", + // Align for longest possible: "SHIFT-LOCK+SH" + key1shift ? 10 : 13, + key1->name, + key2shift ? "SH+" : "", + key2->name); + } + } + } + + fclose(outfile); + + return true; +} + +/****************************************************************************/ +static bool hasFileExt(const char* fileName, const char* fileExt) +{ + const size_t fileExtLen = strlen(fileExt); + const size_t fileNameLen = strlen(fileName); + + return fileNameLen >= fileExtLen && + _stricmp(fileName + fileNameLen - fileExtLen, fileExt) == 0; +} + +/****************************************************************************/ +void JoystickHandler::SaveJoystickMap() +{ + char DefaultPath[_MAX_PATH]; + // Add space for .jmap exstension + char FileName[_MAX_PATH + 5]; + const char* filter = "Joystick Map File (*.jmap)\0*.jmap\0"; + + // If Autoload is enabled, store joystick mapping near disks, + // otherwise start in user data path. + if (m_AutoloadJoystickMap) + { + m_BeebWin->GetPreferences().GetStringValue("DiscsPath", DefaultPath); + m_BeebWin->GetDataPath(m_BeebWin->GetUserDataPath(), DefaultPath); + } + else + { + strcpy(DefaultPath, m_BeebWin->GetUserDataPath()); + } + + FileDialog fileDialog(GetHwnd(), FileName, sizeof(FileName), DefaultPath, filter); + + if (m_JoystickMapPath[0] == '\0' && !m_AutoloadJoystickMap) + { + fileDialog.SetInitial("DefaultUser.jmap"); + } + else + { + fileDialog.SetInitial(m_JoystickMapPath); + } + + if (fileDialog.Save()) + { + if (!hasFileExt(FileName, ".jmap")) + strcat(FileName, ".jmap"); + + if (WriteJoyMap(FileName, &JoystickMap)) + strcpy(m_JoystickMapPath, FileName); + } +} + +static const char *CFG_OPTIONS_STICK1_DEADBAND = "Stick1ToKeysDeadBand"; +static const char *CFG_OPTIONS_STICK2_DEADBAND = "Stick2ToKeysDeadBand"; + +/****************************************************************************/ +void JoystickHandler::ReadPreferences(Preferences& preferences) +{ + DWORD dword; + if (preferences.GetDWORDValue(CFG_OPTIONS_STICKS, dword)) + m_MenuIdSticks[0] = dword; + else + m_MenuIdSticks[0] = 0; + + if (preferences.GetDWORDValue(CFG_OPTIONS_STICKS2, dword)) + m_MenuIdSticks[1] = dword; + else + m_MenuIdSticks[1] = 0; + + if (preferences.GetDWORDValue("JoystickAxes1", dword)) + m_JoystickConfig[0].PCAxes = dword; + else + m_JoystickConfig[0].PCAxes = 1; + m_MenuIdAxes[0] = AxesToMenuId(0, m_JoystickConfig[0].PCAxes); + + if (preferences.GetDWORDValue("JoystickAxes2", dword)) + m_JoystickConfig[1].PCAxes = dword; + else + m_JoystickConfig[1].PCAxes = 1; + m_MenuIdAxes[1] = AxesToMenuId(1, m_JoystickConfig[1].PCAxes); + + if (!preferences.GetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys)) + m_JoystickToKeys = false; + + if (!preferences.GetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, + m_AutoloadJoystickMap)) + m_AutoloadJoystickMap = false; + + preferences.EraseValue("Stick1ToKeysDeadBand"); + preferences.EraseValue("Stick2ToKeysDeadBand"); + + if (preferences.GetDWORDValue(CFG_OPTIONS_STICK_DEADBAND, dword)) + m_Deadband = dword; + else + m_Deadband = DEFAULT_JOY_DEADBAND; +} + +/****************************************************************************/ +void JoystickHandler::WritePreferences(Preferences& preferences) +{ + preferences.SetDWORDValue(CFG_OPTIONS_STICKS, m_MenuIdSticks[0]); + preferences.SetDWORDValue(CFG_OPTIONS_STICKS2, m_MenuIdSticks[1]); + preferences.SetDWORDValue("JoystickAxes1", m_JoystickConfig[0].PCAxes); + preferences.SetDWORDValue("JoystickAxes2", m_JoystickConfig[1].PCAxes); + + preferences.SetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys); + preferences.SetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, m_AutoloadJoystickMap); + preferences.SetDWORDValue(CFG_OPTIONS_STICK_DEADBAND, m_Deadband); +} diff --git a/Src/JoystickHandler.h b/Src/JoystickHandler.h new file mode 100644 index 00000000..b512397e --- /dev/null +++ b/Src/JoystickHandler.h @@ -0,0 +1,169 @@ +/**************************************************************** +BeebEm - BBC Micro and Master 128 Emulator +Copyright (C) 1994 Nigel Magnay +Copyright (C) 1997 Mike Wyatt +Copyright (C) 1998 Robert Schmidt +Copyright (C) 2001 Richard Gellman +Copyright (C) 2004 Ken Lowe +Copyright (C) 2004 Rob O'Donnell +Copyright (C) 2005 Jon Welch +Copyright (C) 2021 Chris Needham, Tadeusz Kijkowski + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +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. + +You should have received a copy of the GNU General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. +****************************************************************/ + +#ifndef BEEBWINJOYSTICK_H +#define BEEBWINJOYSTICK_H + +#include "atodconv.h" +#include "beebemrc.h" +#include "keymapping.h" + +#define NUM_PC_JOYSTICKS 4 + +#define JOYSTICK_MAX_AXES 16 +#define JOYSTICK_MAX_BTNS 16 + +#define JOYSTICK_AXIS_UP 0 +#define JOYSTICK_AXIS_DOWN 1 +#define JOYSTICK_AXIS_LEFT 2 +#define JOYSTICK_AXIS_RIGHT 3 +#define JOYSTICK_AXIS_Z_N 4 +#define JOYSTICK_AXIS_Z_P 5 +#define JOYSTICK_AXIS_R_N 6 +#define JOYSTICK_AXIS_R_P 7 +#define JOYSTICK_AXIS_U_N 8 +#define JOYSTICK_AXIS_U_P 9 +#define JOYSTICK_AXIS_V_N 10 +#define JOYSTICK_AXIS_V_P 11 +#define JOYSTICK_AXIS_HAT_UP 12 +#define JOYSTICK_AXIS_HAT_DOWN 13 +#define JOYSTICK_AXIS_HAT_LEFT 14 +#define JOYSTICK_AXIS_HAT_RIGHT 15 +#define JOYSTICK_AXES_COUNT 16 + +#define BEEB_VKEY_JOY_START 256 +#define BEEB_VKEY_JOY_COUNT (NUM_PC_JOYSTICKS * \ + (JOYSTICK_MAX_AXES + JOYSTICK_MAX_BTNS)) // 4*32 = 128 +#define BEEB_VKEY_JOY_END (BEEB_VKEY_JOY_START + BEEB_VKEY_JOY_COUNT) // 256+128 = 384 + +#define BEEB_VKEY_COUNT BEEB_VKEY_JOY_END + +typedef KeyPair JoyMap[BEEB_VKEY_JOY_COUNT]; + +class BeebWin; + +struct PCJoystickState { + JOYCAPS Caps{}; + unsigned int JoyIndex{ 0 }; + bool Captured{ false }; + unsigned int PrevAxes{ 0 }; + unsigned int PrevBtns{ 0 }; + bool JoystickToKeysActive{ false }; +}; + +struct BBCJoystickConfig { + bool Enabled{ false }; + int PCStick{ 0 }; + int PCAxes{ 0 }; + bool AnalogMousestick{ false }; + bool DigitalMousestick{ false }; +}; + +class JoystickHandler +{ + BeebWin* m_BeebWin; + bool m_JoystickTimerRunning{ false }; + PCJoystickState m_PCJoystickState[NUM_PC_JOYSTICKS]; + BBCJoystickConfig m_JoystickConfig[NUM_BBC_JOYSTICKS]; + int m_MenuIdSticks[NUM_BBC_JOYSTICKS]{}; + int m_MenuIdAxes[NUM_BBC_JOYSTICKS]{}; + int m_Deadband; + bool m_JoystickToKeys{ false }; + bool m_AutoloadJoystickMap{ false }; + HWND m_JoystickTarget{ nullptr }; + char m_JoystickMapPath[_MAX_PATH]{}; + +public: + JoystickHandler(BeebWin* beebWin) : m_BeebWin{ beebWin } {} + + /* Accessors */ + int GetMenuIdSticks(int bbcIdx) { return m_MenuIdSticks[bbcIdx]; } + bool GetJoystickToKeys() { return m_JoystickToKeys; } + void SetJoystickToKeys(bool enabled) { m_JoystickToKeys = enabled; } + void SetJoystickTarget(HWND target) { m_JoystickTarget = target; } + + /* BeebWin access */ + HWND GetHwnd(); + int GetXWinSize(); + int GetYWinSize(); + void CheckMenuItem(UINT id, bool checked); + void EnableMenuItem(UINT id, bool enabled); + + /* Menu handling - will soon be gone */ + int MenuIdToStick(int bbcIdx, UINT menuId); + UINT StickToMenuId(int bbcIdx, int pcStick); + int MenuIdToAxes(int bbcIdx, UINT menuId); + UINT AxesToMenuId(int bbcIdx, int pcAxes); + void UpdateJoystickConfig(int bbcIdx); + bool IsPCJoystickAssigned(int pcIdx, int bbcIdx); + bool IsPCJoystickOn(int pcIdx); + void InitMenu(void); + void UpdateJoystickMenu(void); + void ProcessMenuCommand(int bbcIdx, UINT menuId); + void ProcessAxesMenuCommand(int bbcIdx, UINT menuId); + void ToggleJoystickToKeys(); + void ToggleAutoloadJoystickMap(); + + /* Initialization */ + void ResetJoystick(void); + bool InitJoystick(bool verbose = false); + bool CaptureJoystick(int Index, bool verbose); + + /* BBC Analog */ + void SetJoystickButton(int index, bool value); + void ScaleJoystick(int index, unsigned int x, unsigned int y, + unsigned int minX, unsigned int minY, + unsigned int maxX, unsigned int maxY); + + /* Mousestick */ + void SetMousestickButton(int index, bool button); + void ScaleMousestick(unsigned int x, unsigned int y); + + /* Joystick to keyboard */ + unsigned int GetJoystickAxes(const JOYCAPS& caps, int deadband, const JOYINFOEX& joyInfoEx); + void TranslateAxes(int joyId, unsigned int axesState); + void TranslateJoystickButtons(int joyId, unsigned int buttons); + void TranslateOrSendKey(int vkey, bool keyUp); + void TranslateJoystick(int joyId); + void UpdateJoysticks(void); + + /* Joystick to keyboard mapping */ + void CheckForJoystickMap(const char *path); + void ResetJoystickMap(void); + void ResetJoyMap(JoyMap* joymap); + void ResetJoyMapToDefaultUser(void); + bool ReadJoyMap(const char *filename, JoyMap *joymap); + void LoadJoystickMap(void); + void SaveJoystickMap(void); + bool WriteJoyMap(const char *filename, JoyMap *joymap); + + /* Preferences */ + void ReadPreferences(Preferences& preferences); + void WritePreferences(Preferences& preferences); +}; + +#endif diff --git a/Src/SelectKeyDialog.cpp b/Src/SelectKeyDialog.cpp index 2529b88a..0267d53a 100644 --- a/Src/SelectKeyDialog.cpp +++ b/Src/SelectKeyDialog.cpp @@ -121,12 +121,12 @@ INT_PTR SelectKeyDialog::DlgProc( if (LOWORD(wParam) == WA_INACTIVE) { hCurrentDialog = nullptr; - mainWin->m_JoystickTarget = nullptr; + mainWin->m_Joysticks.SetJoystickTarget(nullptr); } else { hCurrentDialog = m_hwnd; - mainWin->m_JoystickTarget = m_hwnd; + mainWin->m_Joysticks.SetJoystickTarget(m_hwnd); hCurrentAccelTable = nullptr; } break; @@ -224,17 +224,12 @@ LPCSTR SelectKeyDialog::KeyName(int Key) Key -= BEEB_VKEY_JOY_START; - if (Key >= BEEB_VKEY_JOY2_AXES - BEEB_VKEY_JOY1_AXES) - { - strcpy(Name, "Joy2"); - Key -= BEEB_VKEY_JOY2_AXES - BEEB_VKEY_JOY1_AXES; - } - else - { - strcpy(Name, "Joy1"); - } + int joyIdx = Key / (JOYSTICK_MAX_AXES + JOYSTICK_MAX_BTNS); + Key -= joyIdx * (JOYSTICK_MAX_AXES + JOYSTICK_MAX_BTNS); + + sprintf(Name, "Joy%d", joyIdx + 1); - if (Key < BEEB_VKEY_JOY1_BTN1 - BEEB_VKEY_JOY1_AXES) + if (Key < JOYSTICK_MAX_AXES) { if (Key == JOYSTICK_AXIS_UP) { @@ -260,7 +255,7 @@ LPCSTR SelectKeyDialog::KeyName(int Key) } else { - sprintf(Name + strlen(Name), "Btn%d", Key - (BEEB_VKEY_JOY1_BTN1 - BEEB_VKEY_JOY1_AXES) + 1); + sprintf(Name + strlen(Name), "Btn%d", Key - JOYSTICK_MAX_AXES + 1); } return Name; diff --git a/Src/atodconv.cpp b/Src/atodconv.cpp index 6b084059..2c6cb64c 100644 --- a/Src/atodconv.cpp +++ b/Src/atodconv.cpp @@ -121,7 +121,7 @@ void AtoD_poll_real(void) } AtoDState.status |= (value & 0xc000)>>10; - AtoDState.high = value>>8; + AtoDState.high = static_cast(value>>8); AtoDState.low = value & 0xf0; } diff --git a/Src/atodconv.h b/Src/atodconv.h index eaf584de..48cfe00a 100644 --- a/Src/atodconv.h +++ b/Src/atodconv.h @@ -24,8 +24,10 @@ Boston, MA 02110-1301, USA. #ifndef ATODCONV_HEADER #define ATODCONV_HEADER -extern int JoystickX[2]; /* 16 bit number, 0 = right */ -extern int JoystickY[2]; /* 16 bit number, 0 = down */ +#define NUM_BBC_JOYSTICKS 2 + +extern int JoystickX[NUM_BBC_JOYSTICKS]; /* 16 bit number, 0 = right */ +extern int JoystickY[NUM_BBC_JOYSTICKS]; /* 16 bit number, 0 = down */ void AtoDWrite(int Address, int Value); unsigned char AtoDRead(int Address); diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index fca057ca..e541f43c 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -133,8 +133,10 @@ static KeyMap logicalMapping; static KeyMap *transTable = &defaultMapping; static JoyMap *joystickTable = &JoystickMap; + /****************************************************************************/ BeebWin::BeebWin() + : m_Joysticks(this) { m_DXInit = false; m_hWnd = NULL; @@ -163,7 +165,6 @@ BeebWin::BeebWin() memset(&UserKeymap, 0, sizeof(KeyMap)); memset(m_UserKeyMapPath, 0, sizeof(m_UserKeyMapPath)); memset(&JoystickMap, 0, sizeof(JoyMap)); - memset(m_JoystickMapPath, 0, sizeof(m_JoystickMapPath)); m_hBitmap = m_hOldObj = m_hDCBitmap = NULL; m_screen = m_screen_blur = NULL; m_ScreenRefreshCount = 0; @@ -208,25 +209,6 @@ BeebWin::BeebWin() m_DXSmoothMode7Only = false; m_DXResetPending = false; - m_JoystickTarget = nullptr; - m_JoystickTimerRunning = false; - for (int i = 0; i < NUM_PC_JOYSTICKS; ++i) - { - memset(&m_JoystickState[i].Caps, 0, sizeof(m_JoystickState[i].Caps)); - m_JoystickState[i].JoyIndex = 0; - m_JoystickState[i].Deadband = 0; - m_JoystickState[i].Captured = false; - m_JoystickState[i].JoystickToKeysActive = false; - m_JoystickState[i].PrevAxes = 0; - m_JoystickState[i].PrevBtns = 0; - } - - for (int i = 0; i < 2; ++i) - { - m_PCAxesForJoystick[i] = 0; - m_PCStickForJoystick[i] = 0; - } - m_customip[0] = 0; m_customport = 0; m_isFullScreen = false; @@ -341,8 +323,7 @@ bool BeebWin::Initialise() ReadROMFile(RomFile, RomConfig); ApplyPrefs(); - if (m_AutoloadJoystickMap) - CheckForJoystickMap(m_CommandLineFileName1); + m_Joysticks.CheckForJoystickMap(m_CommandLineFileName1); if (m_DebugScript[0] != '\0') { @@ -409,7 +390,7 @@ void BeebWin::ApplyPrefs() GetDataPath(m_UserDataPath, keymap); ReadKeyMap(keymap, &defaultMapping); - ResetJoyMapToDefaultUser(); + m_Joysticks.ResetJoyMapToDefaultUser(); InitMenu(); ShowMenu(true); @@ -428,7 +409,7 @@ void BeebWin::ApplyPrefs() PrinterDisable(); /* Joysticks can only be initialised after the window is created (needs hwnd) */ - InitJoystick(false); + m_Joysticks.InitJoystick(false); LoadFDC(NULL, true); RTCInit(); @@ -1111,34 +1092,6 @@ void BeebWin::InitMenu(void) CheckMenuItem(ID_RTCY2KADJUST, RTCY2KAdjust); // Options - CheckMenuItem(IDM_JOYSTICK, false); - CheckMenuItem(IDM_ANALOGUE_MOUSESTICK, false); - CheckMenuItem(IDM_DIGITAL_MOUSESTICK, false); - CheckMenuItem(IDM_JOY1_PCJOY2, false); - if (m_MenuIdSticks[0] != 0) - CheckMenuItem(m_MenuIdSticks[0], true); - - CheckMenuItem(IDM_JOY1_PRIMARY, false); - CheckMenuItem(IDM_JOY1_SECONDARY1, false); - CheckMenuItem(IDM_JOY1_SECONDARY2, false); - if (m_MenuIdAxes[0] != 0) - CheckMenuItem(m_MenuIdAxes[0], true); - - CheckMenuItem(IDM_JOY2_PCJOY1, false); - CheckMenuItem(IDM_JOY2_ANALOGUE_MOUSESTICK, false); - CheckMenuItem(IDM_JOY2_DIGITAL_MOUSESTICK, false); - CheckMenuItem(IDM_JOY2_PCJOY2, false); - if (m_MenuIdSticks[1] != 0) - CheckMenuItem(m_MenuIdSticks[1], true); - - CheckMenuItem(IDM_JOY2_PRIMARY, false); - CheckMenuItem(IDM_JOY2_SECONDARY1, false); - CheckMenuItem(IDM_JOY2_SECONDARY2, false); - if (m_MenuIdAxes[1] != 0) - CheckMenuItem(m_MenuIdAxes[1], true); - - CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); - CheckMenuItem(IDM_AUTOLOADJOYMAP, m_AutoloadJoystickMap); CheckMenuItem(IDM_FREEZEINACTIVE, m_FreezeWhenInactive); CheckMenuItem(IDM_HIDECURSOR, m_HideCursor); CheckMenuItem(IDM_DEFAULTKYBDMAPPING, false); @@ -1151,6 +1104,7 @@ void BeebWin::InitMenu(void) CheckMenuItem(IDM_AUTOSAVE_PREFS_CMOS, m_AutoSavePrefsCMOS); CheckMenuItem(IDM_AUTOSAVE_PREFS_FOLDERS, m_AutoSavePrefsFolders); CheckMenuItem(IDM_AUTOSAVE_PREFS_ALL, m_AutoSavePrefsAll); + m_Joysticks.InitMenu(); } void BeebWin::UpdateDisplayRendererMenu() { @@ -1274,606 +1228,6 @@ void BeebWin::SetRomMenu(void) } } -/****************************************************************************/ - -void BeebWin::UpdateJoystickMenu() -{ - bool Enable = false; - - if ((PCJoystick1On() || m_JoystickToKeys) && !m_JoystickState[0].Captured) - { - Enable = true; - } - else if ((PCJoystick2On() || m_JoystickToKeys) && !m_JoystickState[1].Captured) - { - Enable = true; - } - - EnableMenuItem(IDM_INIT_JOYSTICK, Enable); - CheckMenuItem(IDM_INIT_JOYSTICK, m_JoystickState[0].Captured); - - Enable = false; - if (m_MenuIdSticks[0] == IDM_JOYSTICK || m_MenuIdSticks[0] == IDM_JOY1_PCJOY2) - Enable = true; - EnableMenuItem(IDM_JOY1_PRIMARY, Enable); - EnableMenuItem(IDM_JOY1_SECONDARY1, Enable); - EnableMenuItem(IDM_JOY1_SECONDARY2, Enable); - - Enable = false; - if (m_MenuIdSticks[1] == IDM_JOY2_PCJOY1 || m_MenuIdSticks[1] == IDM_JOY2_PCJOY2) - Enable = true; - EnableMenuItem(IDM_JOY2_PRIMARY, Enable); - EnableMenuItem(IDM_JOY2_SECONDARY1, Enable); - EnableMenuItem(IDM_JOY2_SECONDARY2, Enable); -} - -/****************************************************************************/ - -bool BeebWin::PCJoystick1On() -{ - return m_MenuIdSticks[0] == IDM_JOYSTICK || m_MenuIdSticks[1] == IDM_JOY2_PCJOY1; -} - -/****************************************************************************/ - -bool BeebWin::PCJoystick2On() -{ - return m_MenuIdSticks[0] == IDM_JOY1_PCJOY2 || m_MenuIdSticks[1] == IDM_JOY2_PCJOY2; -} - -/****************************************************************************/ - -int BeebWin::MenuIdToStick(int menuId) -{ - int pcStick = 0; - switch (menuId) - { - case IDM_JOYSTICK: - case IDM_JOY2_PCJOY1: - pcStick = 1; - break; - case IDM_JOY1_PCJOY2: - case IDM_JOY2_PCJOY2: - pcStick = 2; - break; - } - return pcStick; -} - -/****************************************************************************/ - -int BeebWin::StickToMenuId(int bbcStick, int pcStick) -{ - int menuId = 0; - if (bbcStick == 0) - { - if (pcStick == 1) - menuId = IDM_JOYSTICK; - else if (pcStick == 2) - menuId = IDM_JOY1_PCJOY2; - } - else - { - if (pcStick == 1) - menuId = IDM_JOY2_PCJOY1; - else if (pcStick == 2) - menuId = IDM_JOY2_PCJOY2; - } - return menuId; -} - -/****************************************************************************/ - -int BeebWin::MenuIdToAxes(int menuId) -{ - int axes = 0; - switch (menuId) - { - case IDM_JOY1_PRIMARY: - case IDM_JOY2_PRIMARY: - axes = 1; - break; - case IDM_JOY1_SECONDARY1: - case IDM_JOY2_SECONDARY1: - axes = 2; - break; - case IDM_JOY1_SECONDARY2: - case IDM_JOY2_SECONDARY2: - axes = 3; - break; - } - return axes; -} - -int BeebWin::AxesToMenuId(int bbcStick, int pcAxes) -{ - int menuId = 0; - switch (pcAxes) - { - case 1: - if (bbcStick == 0) - menuId = IDM_JOY1_PRIMARY; - else - menuId = IDM_JOY2_PRIMARY; - break; - case 2: - if (bbcStick == 0) - menuId = IDM_JOY1_SECONDARY1; - else - menuId = IDM_JOY2_SECONDARY1; - break; - case 3: - if (bbcStick == 0) - menuId = IDM_JOY1_SECONDARY2; - else - menuId = IDM_JOY2_SECONDARY2; - break; - } - return menuId; -} - -/****************************************************************************/ - -bool BeebWin::InitJoystick(bool verbose) -{ - bool Success = true; - - m_PCStickForJoystick[0] = MenuIdToStick(m_MenuIdSticks[0]); - m_PCAxesForJoystick[0] = MenuIdToAxes(m_MenuIdAxes[0]); - m_PCStickForJoystick[1] = MenuIdToStick(m_MenuIdSticks[1]); - m_PCAxesForJoystick[1] = MenuIdToAxes(m_MenuIdAxes[1]); - - // PC joystick 1 must be captured to be able to capture PC joystick 2 - if (PCJoystick1On() || PCJoystick2On() || m_JoystickToKeys) - { - if (!m_JoystickState[0].Captured) - { - Success = CaptureJoystick(0, verbose); - } - } - else - { - if (m_JoystickState[0].Captured) - { - m_JoystickState[0].Captured = false; - TranslateJoystick(0); - } - } - - if (PCJoystick2On() || m_JoystickToKeys) - { - if (m_JoystickState[0].Captured && !m_JoystickState[1].Captured) - { - Success = CaptureJoystick(1, verbose); - } - } - else - { - if (m_JoystickState[1].Captured) - { - m_JoystickState[1].Captured = false; - TranslateJoystick(1); - } - } - - if (PCJoystick1On() || PCJoystick2On() || m_JoystickToKeys) - { - if (!m_JoystickTimerRunning) - { - SetTimer(m_hWnd, 3, 20, NULL); - m_JoystickTimerRunning = true; - } - } - else - { - if (m_JoystickTimerRunning) - { - KillTimer(m_hWnd, 3); - m_JoystickTimerRunning = false; - } - } - - UpdateJoystickMenu(); - - return Success; -} - -bool BeebWin::CaptureJoystick(int Index, bool verbose) -{ - bool success = false; - - DWORD JoyIndex; - DWORD Result; - JOYINFOEX joyInfoEx; - UINT numDevs = joyGetNumDevs(); - - // Scan for first present joystick index. It doesn't have to be - // consecutive number. - if (Index == 0) - JoyIndex = 0; - else - JoyIndex = m_JoystickState[Index - 1].JoyIndex + 1; - - // Find first joystick that is known AND connected - Result = JOYERR_UNPLUGGED; - while (Result != JOYERR_NOERROR && JoyIndex != numDevs) - { - memset(&joyInfoEx, 0, sizeof(joyInfoEx)); - joyInfoEx.dwSize = sizeof(joyInfoEx); - joyInfoEx.dwFlags = JOY_RETURNBUTTONS; - - Result = joyGetDevCaps(JoyIndex, &m_JoystickState[Index].Caps, sizeof(JOYCAPS)); - if (Result == JOYERR_NOERROR) - Result = joyGetPosEx(JoyIndex, &joyInfoEx); - if (Result != JOYERR_NOERROR) - ++JoyIndex; - } - - if (Result == ERROR_SUCCESS) - { - m_JoystickState[Index].Captured = true; - m_JoystickState[Index].JoyIndex = JoyIndex; - success = true; - } - else if (verbose) - { - if (Result == ERROR_DEVICE_NOT_CONNECTED || Result == JOYERR_UNPLUGGED) - { - char str[100]; - sprintf(str, "Joystick %d is not plugged in", Index + 1); - - MessageBox(m_hWnd, str, WindowTitle, MB_OK | MB_ICONWARNING); - } - else - { - char str[100]; - sprintf(str, "Failed to initialise joystick %d", Index + 1); - - MessageBox(m_hWnd, str, WindowTitle, MB_OK | MB_ICONWARNING); - } - } - - return success; -} - -/****************************************************************************/ -void BeebWin::SetJoystickButton(int index, bool button) -{ - JoystickButton[index] = button; -} - -/****************************************************************************/ -void BeebWin::ScaleJoystick(int index, unsigned int x, unsigned int y, - unsigned int minX, unsigned int minY, unsigned int maxX, unsigned int maxY) -{ - /* Scale and reverse the readings */ - JoystickX[index] = (int)((double)(maxX - x) * 65535.0 / - (double)(maxX - minX)); - JoystickY[index] = (int)((double)(maxY - y) * 65535.0 / - (double)(maxY - minY)); -} - -/****************************************************************************/ -unsigned int BeebWin::GetJoystickAxes(const JOYCAPS& caps, int deadband, const JOYINFOEX& joyInfoEx) -{ - unsigned int axes = 0; - - int tx = (int)((double)(joyInfoEx.dwXpos - caps.wXmin) * 65535.0 / - (double)(caps.wXmax - caps.wXmin)); - int ty = (int)((double)(joyInfoEx.dwYpos - caps.wYmin) * 65535.0 / - (double)(caps.wYmax - caps.wYmin)); - int tz = (int)((double)(joyInfoEx.dwZpos - caps.wZmin) * 65535.0 / - (double)(caps.wZmax - caps.wZmin)); - int tr = (int)((double)(joyInfoEx.dwRpos - caps.wRmin) * 65535.0 / - (double)(caps.wRmax - caps.wRmin)); - int tu = (int)((double)(joyInfoEx.dwUpos - caps.wUmin) * 65535.0 / - (double)(caps.wUmax - caps.wUmin)); - int tv = (int)((double)(joyInfoEx.dwVpos - caps.wVmin) * 65535.0 / - (double)(caps.wVmax - caps.wVmin)); - - if (ty < 32768 - deadband) - axes |= (1 << JOYSTICK_AXIS_UP); - else if (ty > 32768 + deadband) - axes |= (1 << JOYSTICK_AXIS_DOWN); - - if (tx < 32768 - deadband) - axes |= (1 << JOYSTICK_AXIS_LEFT); - else if (tx > 32768 + deadband) - axes |= (1 << JOYSTICK_AXIS_RIGHT); - - if (tz < 32768 - deadband) - axes |= (1 << JOYSTICK_AXIS_Z_N); - else if (tz > 32768 + deadband) - axes |= (1 << JOYSTICK_AXIS_Z_P); - - if (tr < 32768 - deadband) - axes |= (1 << JOYSTICK_AXIS_R_N); - else if (tr > 32768 + deadband) - axes |= (1 << JOYSTICK_AXIS_R_P); - - if (tu < 32768 - deadband) - axes |= (1 << JOYSTICK_AXIS_U_N); - else if (tu > 32768 + deadband) - axes |= (1 << JOYSTICK_AXIS_U_P); - - if (tv < 32768 - deadband) - axes |= (1 << JOYSTICK_AXIS_V_N); - else if (tv > 32768 + deadband) - axes |= (1 << JOYSTICK_AXIS_V_P); - - if (joyInfoEx.dwPOV != JOY_POVCENTERED) - { - if (joyInfoEx.dwPOV >= 29250 || joyInfoEx.dwPOV < 6750) - axes |= (1 << JOYSTICK_AXIS_HAT_UP); - if (joyInfoEx.dwPOV >= 2250 && joyInfoEx.dwPOV < 15750) - axes |= (1 << JOYSTICK_AXIS_HAT_RIGHT); - if (joyInfoEx.dwPOV >= 11250 && joyInfoEx.dwPOV < 24750) - axes |= (1 << JOYSTICK_AXIS_HAT_DOWN); - if (joyInfoEx.dwPOV >= 20250 && joyInfoEx.dwPOV < 33750) - axes |= (1 << JOYSTICK_AXIS_HAT_LEFT); - } - - return axes; -} - -/****************************************************************************/ -void BeebWin::TranslateOrSendKey(int vkey, bool keyUp) -{ - if (m_JoystickTarget == nullptr) - { - int row, col; - TranslateKey(vkey, keyUp, row, col); - } - else if (!keyUp) - { - // Keyboard input dialog is visible - translate button down to key down - // message and send to dialog - PostMessage(m_JoystickTarget, WM_KEYDOWN, vkey, 0); - } -} - -/****************************************************************************/ -void BeebWin::TranslateAxes(int joyId, unsigned int axesState) -{ - int vkeys = (joyId == 1) ? BEEB_VKEY_JOY2_AXES : BEEB_VKEY_JOY1_AXES; - unsigned int& prevAxes = m_JoystickState[joyId].PrevAxes; - - if (axesState != prevAxes) - { - for (int axId = 0; axId < JOYSTICK_AXES_COUNT; ++axId) - { - if ((axesState & ~prevAxes) & (1 << axId)) - { - TranslateOrSendKey(vkeys + axId, false); - } - else if ((~axesState & prevAxes) & (1 << axId)) - { - TranslateOrSendKey(vkeys + axId, true); - } - } - prevAxes = axesState; - } -} - -/****************************************************************************/ -void BeebWin::TranslateJoystickMove(int joyId, const JOYINFOEX& joyInfoEx, const JOYCAPS& caps) -{ - unsigned int deadband = m_JoystickState[joyId].Deadband; - unsigned int axes = mainWin->GetJoystickAxes(caps, deadband, joyInfoEx); - TranslateAxes(joyId, axes); -} - -/****************************************************************************/ -void BeebWin::TranslateJoystickButtons(int joyId, unsigned int buttons) -{ - const int BUTTON_COUNT = 32; - - unsigned int& prevBtns = m_JoystickState[joyId].PrevBtns; - int vkeys = (joyId == 1) ? BEEB_VKEY_JOY2_BTN1 : BEEB_VKEY_JOY1_BTN1; - - if (buttons != prevBtns) - { - for (int btnId = 0; btnId < BUTTON_COUNT; ++btnId) - { - if ((buttons & ~prevBtns) & (1 << btnId)) - { - TranslateOrSendKey(vkeys + btnId, false); - } - else if ((~buttons & prevBtns) & (1 << btnId)) - { - TranslateOrSendKey(vkeys + btnId, true); - } - } - - prevBtns = buttons; - } -} - -/****************************************************************************/ -void BeebWin::TranslateJoystick(int joyId) -{ - static const JOYCAPS dummyJoyCaps = { - 0, 0, "", - 0, 65535, 0, 65535, 0, 65535, - 16, 10, 1000, - 0, 65535, 0, 65535, 0, 65535 - }; - bool success = false; - JOYINFOEX joyInfoEx; - const JOYCAPS* joyCaps = &m_JoystickState[joyId].Caps; - DWORD joyIndex = m_JoystickState[joyId].JoyIndex; - - joyInfoEx.dwSize = sizeof(joyInfoEx); - joyInfoEx.dwFlags = JOY_RETURNALL | JOY_RETURNPOVCTS; - - if (m_JoystickState[joyId].Captured) - { - MMRESULT result = joyGetPosEx(joyIndex, &joyInfoEx); - if (result == JOYERR_NOERROR) - { - success = true; - } - } - - // If joystick is disabled or error occurs, reset all axes and buttons - if (!success) - { - joyCaps = &dummyJoyCaps; - joyInfoEx.dwXpos = joyInfoEx.dwYpos = joyInfoEx.dwZpos = 32768; - joyInfoEx.dwRpos = joyInfoEx.dwUpos = joyInfoEx.dwVpos = 32768; - joyInfoEx.dwPOV = JOY_POVCENTERED; - // Reset 'captured' flag and update menu entry - m_JoystickState[joyId].Captured = false; - UpdateJoystickMenu(); - } - - // PC joystick to BBC joystick - for (int bbcIndex = 0; bbcIndex < 2; ++bbcIndex) - { - if (m_PCStickForJoystick[bbcIndex] == joyId + 1) - { - if (bbcIndex == 1 && m_PCStickForJoystick[0] == m_PCStickForJoystick[1] - && m_PCAxesForJoystick[0] != m_PCAxesForJoystick[1]) - { - // If both BBC joysticks are mapped to the same PC gamepad, but not - // the same axes, map buttons 2 and 4 to the Beeb's PB1 input - SetJoystickButton(bbcIndex, (joyInfoEx.dwButtons & (JOY_BUTTON2 | JOY_BUTTON4)) != 0); - } - else - { - SetJoystickButton(bbcIndex, (joyInfoEx.dwButtons & (JOY_BUTTON1 | JOY_BUTTON3)) != 0); - } - - if (m_MenuIdSticks[1] == 0) - { - // Second BBC joystick not enabled - map buttons 2 and 4 to Beeb's PB1 input - SetJoystickButton(1, (joyInfoEx.dwButtons & (JOY_BUTTON2 | JOY_BUTTON4)) != 0); - } - - if (m_PCAxesForJoystick[bbcIndex] == 1) - { - ScaleJoystick(bbcIndex, joyInfoEx.dwXpos, joyInfoEx.dwYpos, - joyCaps->wXmin, joyCaps->wYmin, - joyCaps->wXmax, joyCaps->wYmax); - } - else if (m_PCAxesForJoystick[bbcIndex] == 2) - { - ScaleJoystick(bbcIndex, joyInfoEx.dwUpos, joyInfoEx.dwRpos, - joyCaps->wUmin, joyCaps->wRmin, - joyCaps->wUmax, joyCaps->wRmax); - } - else if (m_PCAxesForJoystick[bbcIndex] == 3) - { - ScaleJoystick(bbcIndex, joyInfoEx.dwZpos, joyInfoEx.dwRpos, - joyCaps->wZmin, joyCaps->wRmin, - joyCaps->wZmax, joyCaps->wRmax); - } - } - } - - // Joystick to keyboard mapping - if (m_JoystickToKeys) - { - TranslateJoystickMove(joyId, joyInfoEx, *joyCaps); - TranslateJoystickButtons(joyId, joyInfoEx.dwButtons); - m_JoystickState[joyId].JoystickToKeysActive = true; - } - - // Make sure to reset keyboard state - if (!m_JoystickToKeys && m_JoystickState[joyId].JoystickToKeysActive) - { - TranslateAxes(joyId, 0); - TranslateJoystickButtons(joyId, 0); - m_JoystickState[joyId].JoystickToKeysActive = true; - } -} - -/****************************************************************************/ -void BeebWin::ResetJoystick(void) -{ - m_JoystickState[0].Captured = false; - TranslateJoystick(0); - - m_JoystickState[1].Captured = false; - TranslateJoystick(1); -} - -/****************************************************************************/ -void BeebWin::SetMousestickButton(int index, bool button) -{ - if (index == 0) - { - if (m_MenuIdSticks[0] == IDM_ANALOGUE_MOUSESTICK || - m_MenuIdSticks[0] == IDM_DIGITAL_MOUSESTICK) - { - SetJoystickButton(0, button); - } - if (m_MenuIdSticks[1] == IDM_JOY2_ANALOGUE_MOUSESTICK || - m_MenuIdSticks[1] == IDM_JOY2_DIGITAL_MOUSESTICK) - { - SetJoystickButton(1, button); - } - } - else - { - // Map right mouse button to secondary fire if second joystick is - // not enabled. - if (m_MenuIdSticks[1] == 0 && (m_MenuIdSticks[0] == IDM_ANALOGUE_MOUSESTICK || - m_MenuIdSticks[0] == IDM_DIGITAL_MOUSESTICK)) - { - SetJoystickButton(1, button); - } - } -} - -/****************************************************************************/ -void BeebWin::ScaleMousestick(unsigned int x, unsigned int y) -{ - for (int index = 0; index < 2; ++index) - { - int XPos = (m_XWinSize - x) * 65535 / m_XWinSize; - int YPos = (m_YWinSize - y) * 65535 / m_YWinSize; - - if (index == 0 && m_MenuIdSticks[0] == IDM_ANALOGUE_MOUSESTICK || - index == 1 && m_MenuIdSticks[1] == IDM_JOY2_ANALOGUE_MOUSESTICK) - { - JoystickX[index] = XPos; - JoystickY[index] = YPos; - } - else if (index == 0 && m_MenuIdSticks[0] == IDM_DIGITAL_MOUSESTICK || - index == 1 && m_MenuIdSticks[1] == IDM_JOY2_DIGITAL_MOUSESTICK) - { - const int Threshold = 2000; - - if (XPos < 32768 - Threshold) - { - JoystickX[index] = 0; - } - else if (XPos > 32768 + Threshold) - { - JoystickX[index] = 65535; - } - else - { - JoystickX[index] = 32768; - } - - if (YPos < 32768 - Threshold) - { - JoystickY[index] = 0; - } - else if (YPos > 32768 + Threshold) - { - JoystickY[index] = 65535; - } - else - { - JoystickY[index] = 32768; - } - } - } -} - /****************************************************************************/ void BeebWin::SetAMXPosition(unsigned int x, unsigned int y) { @@ -2263,7 +1617,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle mainWin->m_RelMousePos.y = mainWin->m_YWinSize; } - mainWin->ScaleMousestick(mainWin->m_RelMousePos.x, + mainWin->m_Joysticks.ScaleMousestick(mainWin->m_RelMousePos.x, mainWin->m_RelMousePos.y); mainWin->ChangeAMXPosition(xDelta, yDelta); @@ -2276,7 +1630,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle int xPos = GET_X_LPARAM(lParam); int yPos = GET_Y_LPARAM(lParam); - mainWin->ScaleMousestick(xPos, yPos); + mainWin->m_Joysticks.ScaleMousestick(xPos, yPos); mainWin->SetAMXPosition(xPos, yPos); // Experiment: show menu in full screen when cursor moved to top of window @@ -2291,13 +1645,13 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle } else { - mainWin->SetMousestickButton(0, (uParam & MK_LBUTTON) != 0); + mainWin->m_Joysticks.SetMousestickButton(0, (uParam & MK_LBUTTON) != 0); AMXButtons |= AMX_LEFT_BUTTON; } break; case WM_LBUTTONUP: - mainWin->SetMousestickButton(0, (uParam & MK_LBUTTON) != 0); + mainWin->m_Joysticks.SetMousestickButton(0, (uParam & MK_LBUTTON) != 0); AMXButtons &= ~AMX_LEFT_BUTTON; break; @@ -2310,12 +1664,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle break; case WM_RBUTTONDOWN: - mainWin->SetMousestickButton(1, (uParam & MK_RBUTTON) != 0); + mainWin->m_Joysticks.SetMousestickButton(1, (uParam & MK_RBUTTON) != 0); AMXButtons |= AMX_RIGHT_BUTTON; break; case WM_RBUTTONUP: - mainWin->SetMousestickButton(1, (uParam & MK_RBUTTON) != 0); + mainWin->m_Joysticks.SetMousestickButton(1, (uParam & MK_RBUTTON) != 0); AMXButtons &= ~AMX_RIGHT_BUTTON; break; @@ -2357,12 +1711,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle } else if (uParam == 3) { - // Update joysticks - if (mainWin->m_JoystickState[0].Captured) - mainWin->TranslateJoystick(0); - - if (mainWin->m_JoystickState[1].Captured) - mainWin->TranslateJoystick(1); + mainWin->m_Joysticks.UpdateJoysticks(); } break; @@ -3463,7 +2812,7 @@ void BeebWin::HandleCommand(int MenuId) { // Also switch on analogue mousestick (touch screen uses // mousestick position) - if (m_MenuIdSticks[0] != IDM_ANALOGUE_MOUSESTICK) + if (m_Joysticks.GetMenuIdSticks(0) != IDM_ANALOGUE_MOUSESTICK) HandleCommand(IDM_ANALOGUE_MOUSESTICK); if (EthernetPortEnabled) @@ -3916,103 +3265,38 @@ void BeebWin::HandleCommand(int MenuId) case IDM_ANALOGUE_MOUSESTICK: case IDM_DIGITAL_MOUSESTICK: case IDM_JOY1_PCJOY2: - /* Disable current selection */ - if (m_MenuIdSticks[0] != 0) - { - CheckMenuItem(m_MenuIdSticks[0], false); - - // Reset joystick position to centre - JoystickX[0] = 32767; - JoystickY[0] = 32767; - - SetJoystickButton(0, false); - - if (m_MenuIdSticks[1] == 0) - { - SetJoystickButton(1, false); - } - } - - if (MenuId == m_MenuIdSticks[0]) - { - /* Joystick 1 switched off completely */ - m_MenuIdSticks[0] = 0; - } - else - { - /* Initialise new selection */ - m_MenuIdSticks[0] = MenuId; - - CheckMenuItem(m_MenuIdSticks[0], true); - } - - InitJoystick(false); + m_Joysticks.ProcessMenuCommand(0, MenuId); break; case IDM_JOY2_PCJOY1: case IDM_JOY2_ANALOGUE_MOUSESTICK: case IDM_JOY2_DIGITAL_MOUSESTICK: case IDM_JOY2_PCJOY2: - /* Disable current selection */ - if (m_MenuIdSticks[1] != 0) - { - CheckMenuItem(m_MenuIdSticks[1], false); - - // Reset joystick position to centre - JoystickX[1] = 32767; - JoystickY[1] = 32767; - - SetJoystickButton(1, false); - } - - if (MenuId == m_MenuIdSticks[1]) - { - /* Joystick 2 switched off completely */ - m_MenuIdSticks[1] = 0; - } - else - { - /* Initialise new selection */ - m_MenuIdSticks[1] = MenuId; - - CheckMenuItem(m_MenuIdSticks[1], true); - } - - InitJoystick(false); + m_Joysticks.ProcessMenuCommand(1, MenuId); break; case IDM_JOY1_PRIMARY: case IDM_JOY1_SECONDARY1: case IDM_JOY1_SECONDARY2: - CheckMenuItem(m_MenuIdAxes[0], false); - m_MenuIdAxes[0] = MenuId; - m_PCAxesForJoystick[0] = MenuIdToAxes(MenuId); - CheckMenuItem(m_MenuIdAxes[0], true); + m_Joysticks.ProcessAxesMenuCommand(0, MenuId); break; case IDM_JOY2_PRIMARY: case IDM_JOY2_SECONDARY1: case IDM_JOY2_SECONDARY2: - CheckMenuItem(m_MenuIdAxes[1], false); - m_MenuIdAxes[1] = MenuId; - m_PCAxesForJoystick[1] = MenuIdToAxes(MenuId); - CheckMenuItem(m_MenuIdAxes[1], true); + m_Joysticks.ProcessAxesMenuCommand(1, MenuId); break; case IDM_INIT_JOYSTICK: - ResetJoystick(); - InitJoystick(true); + m_Joysticks.InitJoystick(true); break; case IDM_JOYSTICK_TO_KEYS: - m_JoystickToKeys = !m_JoystickToKeys; - InitJoystick(false); - CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); + m_Joysticks.ToggleJoystickToKeys(); break; case IDM_AUTOLOADJOYMAP: - m_AutoloadJoystickMap = !m_AutoloadJoystickMap; - CheckMenuItem(IDM_AUTOLOADJOYMAP, m_AutoloadJoystickMap); + m_Joysticks.ToggleAutoloadJoystickMap(); break; case IDM_FREEZEINACTIVE: @@ -4039,15 +3323,15 @@ void BeebWin::HandleCommand(int MenuId) break; case IDM_LOADJOYMAP: - LoadJoystickMap(); + m_Joysticks.LoadJoystickMap(); break; case IDM_SAVEJOYMAP: - SaveJoystickMap(); + m_Joysticks.SaveJoystickMap(); break; case IDM_RESETJOYMAP: - ResetJoystickMap(); + m_Joysticks.ResetJoystickMap(); break; case IDM_DEFINEKEYMAP: @@ -4835,7 +4119,7 @@ void BeebWin::OpenUserKeyboardDialog() m_WasPaused = mainWin->IsPaused(); - m_JoystickToKeysWasEnabled = m_JoystickToKeys; + m_JoystickToKeysWasEnabled = m_Joysticks.GetJoystickToKeys(); if (!m_WasPaused) { @@ -4857,12 +4141,12 @@ void BeebWin::OpenJoystickMapDialog() } // Enable mapping to capture keys in dialog - m_JoystickToKeysWasEnabled = m_JoystickToKeys; + m_JoystickToKeysWasEnabled = m_Joysticks.GetJoystickToKeys(); if (!m_JoystickToKeysWasEnabled) { - m_JoystickToKeys = true; - InitJoystick(false); + m_Joysticks.SetJoystickToKeys(true); + m_Joysticks.InitJoystick(false); } UserKeyboardDialog(m_hWnd, true); @@ -4879,10 +4163,10 @@ void BeebWin::UserKeyboardDialogClosed() } // Restore joystick state - if (m_JoystickToKeys && !m_JoystickToKeysWasEnabled) + if (m_Joysticks.GetJoystickToKeys() && !m_JoystickToKeysWasEnabled) { - m_JoystickToKeys = m_JoystickToKeysWasEnabled; - InitJoystick(false); + m_Joysticks.SetJoystickToKeys(m_JoystickToKeysWasEnabled); + m_Joysticks.InitJoystick(false); } // Unmute @@ -5029,37 +4313,6 @@ void BeebWin::ParseCommandLine() } } -/*****************************************************************************/ -// Look for file specific joystick map -void BeebWin::CheckForJoystickMap(const char *path) -{ - char file[_MAX_PATH]; - char drive[_MAX_DRIVE]; - char dir[_MAX_DIR]; - char filename[_MAX_FNAME]; - - if (!path || !path[0] || !m_AutoloadJoystickMap) - return; - - _splitpath(path, drive, dir, filename, NULL); - - // Look for prefs file with the same name with ".jmap" extension - _makepath(file, drive, dir, filename, "jmap"); - - if (GetFileAttributes(file) != INVALID_FILE_ATTRIBUTES) - { - ReadJoyMap(file, &JoystickMap); - } - else - { - // Mapping file not found - reset to default - ResetJoyMapToDefaultUser(); - - // Set jmap path even if the file doesn't exist - strcpy(m_JoystickMapPath, file); - } -} - /*****************************************************************************/ // Check for preference files in the same directory as the file specified void BeebWin::CheckForLocalPrefs(const char *path, bool bLoadPrefs) @@ -5092,7 +4345,7 @@ void BeebWin::CheckForLocalPrefs(const char *path, bool bLoadPrefs) HandleCommand(m_DisplayRenderer); InitMenu(); SetWindowText(m_hWnd, WindowTitle); - InitJoystick(false); + m_Joysticks.InitJoystick(false); } } @@ -5111,9 +4364,9 @@ void BeebWin::CheckForLocalPrefs(const char *path, bool bLoadPrefs) } } - if (bLoadPrefs && m_AutoloadJoystickMap) + if (bLoadPrefs) { - CheckForJoystickMap(path); + m_Joysticks.CheckForJoystickMap(path); } } diff --git a/Src/beebwin.h b/Src/beebwin.h index c4467d31..1225ed0e 100644 --- a/Src/beebwin.h +++ b/Src/beebwin.h @@ -43,6 +43,8 @@ Boston, MA 02110-1301, USA. #include "port.h" #include "preferences.h" #include "video.h" +#include "keymapping.h" +#include "JoystickHandler.h" /* Used in message boxes */ #define GETHWND (mainWin->GethWnd()) @@ -51,51 +53,9 @@ Boston, MA 02110-1301, USA. #define CFG_KEYBOARD_LAYOUT "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout" #define CFG_SCANCODE_MAP "Scancode Map" -#define NUM_PC_JOYSTICKS 2 - -#define JOYSTICK_MAX_AXES 16 -#define JOYSTICK_MAX_BTNS 16 - -#define JOYSTICK_AXIS_UP 0 -#define JOYSTICK_AXIS_DOWN 1 -#define JOYSTICK_AXIS_LEFT 2 -#define JOYSTICK_AXIS_RIGHT 3 -#define JOYSTICK_AXIS_Z_N 4 -#define JOYSTICK_AXIS_Z_P 5 -#define JOYSTICK_AXIS_R_N 6 -#define JOYSTICK_AXIS_R_P 7 -#define JOYSTICK_AXIS_U_N 8 -#define JOYSTICK_AXIS_U_P 9 -#define JOYSTICK_AXIS_V_N 10 -#define JOYSTICK_AXIS_V_P 11 -#define JOYSTICK_AXIS_HAT_UP 12 -#define JOYSTICK_AXIS_HAT_DOWN 13 -#define JOYSTICK_AXIS_HAT_LEFT 14 -#define JOYSTICK_AXIS_HAT_RIGHT 15 -#define JOYSTICK_AXES_COUNT 16 - -#define BEEB_VKEY_JOY_START 256 -#define BEEB_VKEY_JOY1_AXES BEEB_VKEY_JOY_START -#define BEEB_VKEY_JOY1_BTN1 (BEEB_VKEY_JOY1_AXES + JOYSTICK_MAX_AXES) // 256+16 = 272 -#define BEEB_VKEY_JOY2_AXES (BEEB_VKEY_JOY1_BTN1 + JOYSTICK_MAX_BTNS) // 272+16 = 288 -#define BEEB_VKEY_JOY2_BTN1 (BEEB_VKEY_JOY2_AXES + JOYSTICK_MAX_AXES) // 288+16 = 304 -#define BEEB_VKEY_JOY_END (BEEB_VKEY_JOY2_BTN1 + JOYSTICK_MAX_BTNS) // 304+16 = 320 - -#define BEEB_VKEY_JOY_COUNT (BEEB_VKEY_JOY_END - BEEB_VKEY_JOY_START) -#define BEEB_VKEY_COUNT BEEB_VKEY_JOY_END #define UNASSIGNED_ROW -9 -struct KeyMapping { - int row; // Beeb row - int col; // Beeb col - bool shift; // Beeb shift state -}; - -typedef KeyMapping KeyPair[2]; -typedef KeyPair KeyMap[256]; // Indices are: [Virt key][shift state] -typedef KeyPair JoyMap[BEEB_VKEY_JOY_COUNT]; - extern const char *WindowTitle; typedef union EightUChars { @@ -164,16 +124,6 @@ enum class MessageType { Info }; -struct JoystickState { - JOYCAPS Caps; - unsigned int JoyIndex; - unsigned int Deadband; - bool Captured; - unsigned int PrevAxes; - unsigned int PrevBtns; - bool JoystickToKeysActive; -}; - class BeebWin { public: @@ -260,18 +210,6 @@ class BeebWin { bool IsWindowMinimized() const; void DisplayClientAreaText(HDC hdc); void DisplayFDCBoardInfo(HDC hDC, int x, int y); - void SetJoystickButton(int index, bool button); - void ScaleJoystick(int index, unsigned int x, unsigned int y, - unsigned int minX, unsigned int minY, - unsigned int maxX, unsigned int maxY); - unsigned int GetJoystickAxes(const JOYCAPS& caps, int deadband, const JOYINFOEX& joyInfoEx); - void TranslateOrSendKey(int vkey, bool keyUp); - void TranslateAxes(int joyId, unsigned int axesState); - void TranslateJoystickMove(int joyId, const JOYINFOEX& joyInfoEx, const JOYCAPS& caps); - void TranslateJoystickButtons(int joyId, unsigned int buttons); - void TranslateJoystick(int joyId); - void SetMousestickButton(int index, bool button); - void ScaleMousestick(unsigned int x, unsigned int y); void HandleCommand(int MenuId); void SetAMXPosition(unsigned int x, unsigned int y); void ChangeAMXPosition(int deltaX, int deltaY); @@ -294,7 +232,6 @@ class BeebWin { void ResetTiming(void); int TranslateKey(int vkey, bool keyUp, int &row, int &col); void ParseCommandLine(void); - void CheckForJoystickMap(const char *path); void CheckForLocalPrefs(const char *path, bool bLoadPrefs); void FindCommandLineFile(char *CmdLineFile); void HandleCommandLineFile(int drive, const char *CmdLineFile); @@ -369,15 +306,7 @@ class BeebWin { int m_MenuIdVolume; int m_MenuIdTiming; int m_FPSTarget; - bool m_JoystickTimerRunning; - JoystickState m_JoystickState[NUM_PC_JOYSTICKS]; - int m_PCStickForJoystick[2]; - int m_PCAxesForJoystick[2]; - int m_MenuIdSticks[2]; - int m_MenuIdAxes[2]; - bool m_JoystickToKeys; - bool m_AutoloadJoystickMap; - HWND m_JoystickTarget; + JoystickHandler m_Joysticks; bool m_HideCursor; bool m_CaptureMouse; bool m_MouseCaptured; @@ -387,8 +316,8 @@ class BeebWin { bool m_KeyMapAS; bool m_KeyMapFunc; char m_UserKeyMapPath[_MAX_PATH]; - char m_JoystickMapPath[_MAX_PATH]; bool m_ShiftPressed; + /* indexes are [vkey][0: row/1: col][shift pressed] */ int m_vkeyPressed[BEEB_VKEY_COUNT][2][2]; char m_AppPath[_MAX_PATH]; char m_UserDataPath[_MAX_PATH]; @@ -583,13 +512,6 @@ class BeebWin { int ReadDisc(int Drive, bool bCheckForPrefs); void Load1770DiscImage(const char *FileName, int Drive, DiscType Type); void LoadTape(void); - int MenuIdToStick(int menuId); - int StickToMenuId(int bbcStick, int pcStick); - int MenuIdToAxes(int menuId); - int AxesToMenuId(int bbcStick, int pcAxes); - bool InitJoystick(bool verbose = false); - bool CaptureJoystick(int Index, bool verbose); - void ResetJoystick(void); void RestoreState(void); void SaveState(void); void NewDiscImage(int Drive); @@ -626,23 +548,12 @@ class BeebWin { bool RebootSystem(); void LoadUserKeyMap(void); void SaveUserKeyMap(void); - void ResetJoystickMap(void); - void LoadJoystickMap(void); - void SaveJoystickMap(void); FILE* OpenReadFile(const char *filename, const char *typeDescr, const char *token); FILE* OpenWriteFile(const char *filename, const char *typeDescr); bool ReadKeyMap(const char *filename, KeyMap *keymap); bool WriteKeyMap(const char *filename, KeyMap *keymap); - void ResetJoyMapToDefaultUser(void); - void ResetJoyMap(JoyMap* joymap); - bool ReadJoyMap(const char *filename, JoyMap *joymap); - bool WriteJoyMap(const char *filename, JoyMap *joymap); - bool PCJoystick1On(); - bool PCJoystick2On(); - void UpdateJoystickMenu(); - void Report(MessageType type, const char *format, ...); bool RegCreateKey(HKEY hKeyRoot, LPCSTR lpSubKey); @@ -655,6 +566,7 @@ class BeebWin { // Preferences void LoadPreferences(); void SavePreferences(bool saveAll); + Preferences& GetPreferences() { return m_Preferences; } private: enum class PaletteType : char { diff --git a/Src/beebwinio.cpp b/Src/beebwinio.cpp index 73008353..f8bc5604 100644 --- a/Src/beebwinio.cpp +++ b/Src/beebwinio.cpp @@ -54,8 +54,6 @@ using namespace Gdiplus; // Token written to start of map file #define KEYMAP_TOKEN "*** BeebEm Keymap ***" -#define JOYMAP_TOKEN "*** BeebEm Joystick Map ***" - extern EDCB ExtBoard; extern bool DiscLoaded[2]; // Set to true when a disc image has been loaded. extern char CDiscName[2][256]; // Filename of disc current in drive 0 and 1; @@ -305,7 +303,7 @@ void BeebWin::LoadTape(void) LoadCSWTape(FileName); } - CheckForJoystickMap(FileName); + m_Joysticks.CheckForJoystickMap(FileName); } } @@ -1181,93 +1179,6 @@ void BeebWin::SaveUserKeyMap() } } -/****************************************************************************/ -void BeebWin::ResetJoystickMap() -{ - if (MessageBox(GETHWND, "Clear joystick to keyboard mapping table?", WindowTitle, MB_YESNO | MB_ICONQUESTION) != IDYES) - return; - - ResetJoyMap(&JoystickMap); -} - -/****************************************************************************/ -void BeebWin::LoadJoystickMap() -{ - char DefaultPath[_MAX_PATH]; - char FileName[_MAX_PATH]; - const char* filter = "Joystick Map File (*.jmap)\0*.jmap\0"; - - // If Autoload is enabled, look for joystick mapping near disks, - // otherwise start in user data path. - if (m_AutoloadJoystickMap) - { - m_Preferences.GetStringValue("DiscsPath", DefaultPath); - GetDataPath(m_UserDataPath, DefaultPath); - } - else - { - strcpy(DefaultPath, m_UserDataPath); - } - - FileDialog fileDialog(m_hWnd, FileName, sizeof(FileName), DefaultPath, filter); - - if (m_JoystickMapPath[0] == '\0' && !m_AutoloadJoystickMap) - { - fileDialog.SetInitial("DefaultUser.jmap"); - } - else - { - fileDialog.SetInitial(m_JoystickMapPath); - } - - if (fileDialog.Open()) - { - if (ReadJoyMap(FileName, &JoystickMap)) - strcpy(m_JoystickMapPath, FileName); - } -} - -/****************************************************************************/ -void BeebWin::SaveJoystickMap() -{ - char DefaultPath[_MAX_PATH]; - // Add space for .jmap exstension - char FileName[_MAX_PATH + 5]; - const char* filter = "Joystick Map File (*.jmap)\0*.jmap\0"; - - // If Autoload is enabled, store joystick mapping near disks, - // otherwise start in user data path. - if (m_AutoloadJoystickMap) - { - m_Preferences.GetStringValue("DiscsPath", DefaultPath); - GetDataPath(m_UserDataPath, DefaultPath); - } - else - { - strcpy(DefaultPath, m_UserDataPath); - } - - FileDialog fileDialog(m_hWnd, FileName, sizeof(FileName), DefaultPath, filter); - - if (m_JoystickMapPath[0] == '\0' && !m_AutoloadJoystickMap) - { - fileDialog.SetInitial("DefaultUser.jmap"); - } - else - { - fileDialog.SetInitial(m_JoystickMapPath); - } - - if (fileDialog.Save()) - { - if (!hasFileExt(FileName, ".jmap")) - strcat(FileName, ".jmap"); - - if (WriteJoyMap(FileName, &JoystickMap)) - strcpy(m_JoystickMapPath, FileName); - } -} - FILE* BeebWin::OpenReadFile(const char *filename, const char *typeDescr, const char *token) { @@ -1339,162 +1250,6 @@ bool BeebWin::ReadKeyMap(const char *filename, KeyMap *keymap) return success; } -/****************************************************************************/ -void BeebWin::ResetJoyMapToDefaultUser(void) -{ - char keymap[_MAX_PATH]; - strcpy(keymap, "DefaultUser.jmap"); - GetDataPath(m_UserDataPath, keymap); - ResetJoyMap(&JoystickMap); - if (GetFileAttributes(keymap) != INVALID_FILE_ATTRIBUTES) - ReadJoyMap(keymap, &JoystickMap); -} - -/****************************************************************************/ -void BeebWin::ResetJoyMap(JoyMap* joymap) -{ - // Initialize all input to unassigned - for (auto& mapping : *joymap) - { - mapping[0].row = mapping[1].row = UNASSIGNED_ROW; - mapping[0].col = mapping[1].col = 0; - mapping[0].shift = false; - mapping[1].shift = true; - } -} - -/****************************************************************************/ -static void makeupper(char* str) -{ - if (str == NULL) - return; - while (*str) - { - *str = static_cast(toupper(*str)); - ++str; - } -} - -/****************************************************************************/ -bool BeebWin::ReadJoyMap(const char *filename, JoyMap *joymap) -{ - bool success = true; - FILE *infile = OpenReadFile(filename, "key map", JOYMAP_TOKEN "\n"); - char buf[256]; - JoyMap newJoyMap; - int line = 1; - - if (infile == NULL) - return false; - - ResetJoyMap(&newJoyMap); - - while (fgets(buf, 255, infile) != NULL) - { - char *ptr, *inputName, *keyName1, *keyName2; - - ++line; - - // Read at most three tokens from line - inputName = strtok_s(buf, " \t\n", &ptr); - keyName1 = strtok_s(NULL, " \t\n", &ptr); - keyName2 = strtok_s(NULL, " \t\n", &ptr); - - // Ignore empty lines or lines starting with '#' - if (inputName == NULL || inputName[0] == '#') - continue; - - if (keyName1 == NULL) - { - char errstr[500]; - sprintf(errstr, "Invalid line in joystick mapping file:\n %s\n line %d\n", - filename, line); - MessageBox(GETHWND, errstr, WindowTitle, MB_OK | MB_ICONERROR); - success = false; - break; - } - - // Get vkey number and mapping entry from input name - int vkey = SelectKeyDialog::JoyVKeyByName(inputName); - if (vkey < BEEB_VKEY_JOY_START || vkey >= BEEB_VKEY_JOY_END) - { - char errstr[500]; - sprintf(errstr, "Invalid input name in joystick mapping file:\n %s\n line %d\n", - filename, line); - MessageBox(GETHWND, errstr, WindowTitle, MB_OK | MB_ICONERROR); - success = false; - break; - } - KeyMapping* mapping = newJoyMap[vkey - BEEB_VKEY_JOY_START]; - - // Get shift state and BBC key from key name - bool shift1 = false; - makeupper(keyName1); - - if (strncmp(keyName1, "SH+", 3) == 0) - { - shift1 = true; - keyName1 += 3; - } - - const BBCKey* key1 = GetBBCKeyByName(keyName1); - if (key1->row == UNASSIGNED_ROW && strcmp(keyName1, "NONE") != 0) - { - char errstr[500]; - sprintf(errstr, "Invalid key name in joystick mapping file:\n %s\n line %d\n", - filename, line); - MessageBox(GETHWND, errstr, WindowTitle, MB_OK | MB_ICONERROR); - success = false; - break; - } - - if (keyName2 == NULL) - { - // Shifted and unshifted input map to the same key - mapping[0].row = mapping[1].row = key1->row; - mapping[0].col = mapping[1].col = key1->column; - mapping[0].shift = false; - mapping[1].shift = true; - } - else - { - bool shift2 = false; - makeupper(keyName2); - - if (strncmp(keyName2, "SH+", 3) == 0) - { - shift2 = true; - keyName2 += 3; - } - - const BBCKey* key2 = GetBBCKeyByName(keyName2); - if (key2->row == UNASSIGNED_ROW && strcmp(keyName2, "NONE") != 0) - { - char errstr[500]; - sprintf(errstr, "Invalid key name in joystick mapping file:\n %s\n line %d\n", - filename, line); - MessageBox(GETHWND, errstr, WindowTitle, MB_OK | MB_ICONERROR); - success = false; - break; - } - - mapping[0].row = key1->row; - mapping[0].col = key1->column; - mapping[0].shift = shift1; - mapping[1].row = key2->row; - mapping[1].col = key2->column; - mapping[1].shift = shift2; - } - } - - if (success) - { - memcpy(joymap, &newJoyMap, sizeof(newJoyMap)); - } - - return success; -} - /****************************************************************************/ FILE* BeebWin::OpenWriteFile(const char *filename, const char *typeDescr) { @@ -1547,58 +1302,6 @@ bool BeebWin::WriteKeyMap(const char *filename, KeyMap *keymap) return true; } -/****************************************************************************/ -bool BeebWin::WriteJoyMap(const char *filename, JoyMap *joymap) -{ - FILE* outfile = OpenWriteFile(filename, "joystick map"); - - if (outfile == NULL) - return false; - - fprintf(outfile, JOYMAP_TOKEN "\n\n"); - - for (int i = 0; i < BEEB_VKEY_JOY_COUNT; ++i) - { - KeyMapping* mapping = (*joymap)[i]; - if (mapping[0].row != UNASSIGNED_ROW - || mapping[1].row != UNASSIGNED_ROW) - { - const char* inputName = SelectKeyDialog::KeyName(i + BEEB_VKEY_JOY_START); - - if (mapping[0].row == mapping[1].row - && mapping[0].col == mapping[1].col - && !mapping[0].shift && mapping[1].shift) - { - // Shifted and unshifted input map to the same key - write one name - const BBCKey* key = GetBBCKeyByRowAndCol(mapping[0].row, mapping[0].col); - fprintf(outfile, "%-13s %s\n", - inputName, - key->name); - } - else - { - // Separate mapping for shifted and unshifted - const BBCKey* key1 = GetBBCKeyByRowAndCol(mapping[0].row, mapping[0].col); - bool key1shift = mapping[0].shift && mapping[0].row != UNASSIGNED_ROW; - const BBCKey* key2 = GetBBCKeyByRowAndCol(mapping[1].row, mapping[1].col); - bool key2shift = mapping[1].shift && mapping[1].row != UNASSIGNED_ROW; - fprintf(outfile, "%-13s %s%-*s %s%s\n", - inputName, - key1shift ? "SH+" : "", - // Align for longest possible: "SHIFT-LOCK+SH" - key1shift ? 10 : 13, - key1->name, - key2shift ? "SH+" : "", - key2->name); - } - } - } - - fclose(outfile); - - return true; -} - /****************************************************************************/ /* Clipboard support */ diff --git a/Src/beebwinprefs.cpp b/Src/beebwinprefs.cpp index 18146b56..c5e162d6 100644 --- a/Src/beebwinprefs.cpp +++ b/Src/beebwinprefs.cpp @@ -59,8 +59,6 @@ static const char *CFG_VIEW_MONITOR = "Monitor"; static const char *CFG_SOUND_SAMPLE_RATE = "SampleRate"; static const char *CFG_SOUND_VOLUME = "SoundVolume"; static const char *CFG_SOUND_ENABLED = "SoundEnabled"; -static const char *CFG_OPTIONS_STICKS = "Sticks"; -static const char *CFG_OPTIONS_STICKS2 = "Sticks2"; static const char *CFG_OPTIONS_KEY_MAPPING = "KeyMapping"; static const char *CFG_OPTIONS_USER_KEY_MAP_FILE = "UserKeyMapFile"; static const char *CFG_OPTIONS_FREEZEINACTIVE = "FreezeWhenInactive"; @@ -76,17 +74,11 @@ static const char *CFG_PRINTER_PORT = "PrinterPort"; static const char *CFG_PRINTER_FILE = "PrinterFile"; static const char *CFG_MACHINE_TYPE = "MachineType"; static const char *CFG_TUBE_TYPE = "TubeType"; -static const char *CFG_OPTIONS_STICKS_TO_KEYS = "SticksToKeys"; -static const char *CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP = "AutoloadJoystickMap"; -static const char *CFG_OPTIONS_STICK1_DEADBAND = "Stick1ToKeysDeadBand"; -static const char *CFG_OPTIONS_STICK2_DEADBAND = "Stick2ToKeysDeadBand"; #define LED_COLOUR_TYPE (LEDByte&4)>>2 #define LED_SHOW_KB (LEDByte&1) #define LED_SHOW_DISC (LEDByte&2)>>1 -#define DEFAULT_JOY_DEADBAND 4096 - extern unsigned char CMOSDefault[64]; void BeebWin::LoadPreferences() @@ -260,44 +252,7 @@ void BeebWin::LoadPreferences() if (!m_Preferences.GetBoolValue("Music5000Enabled", Music5000Enabled)) Music5000Enabled = false; - if (m_Preferences.GetDWORDValue(CFG_OPTIONS_STICKS, dword)) - m_MenuIdSticks[0] = dword; - else - m_MenuIdSticks[0] = 0; - - if (m_Preferences.GetDWORDValue(CFG_OPTIONS_STICKS2, dword)) - m_MenuIdSticks[1] = dword; - else - m_MenuIdSticks[1] = 0; - - if (m_Preferences.GetDWORDValue("JoystickAxes1", dword)) - m_PCAxesForJoystick[0] = dword; - else - m_PCAxesForJoystick[0] = 1; - m_MenuIdAxes[0] = AxesToMenuId(0, m_PCAxesForJoystick[0]); - - if (m_Preferences.GetDWORDValue("JoystickAxes2", dword)) - m_PCAxesForJoystick[1] = dword; - else - m_PCAxesForJoystick[1] = 1; - m_MenuIdAxes[1] = AxesToMenuId(1, m_PCAxesForJoystick[1]); - - if (!m_Preferences.GetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys)) - m_JoystickToKeys = false; - - if (!m_Preferences.GetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, - m_AutoloadJoystickMap)) - m_AutoloadJoystickMap = false; - - if (m_Preferences.GetDWORDValue(CFG_OPTIONS_STICK1_DEADBAND, dword)) - m_JoystickState[0].Deadband = dword; - else - m_JoystickState[0].Deadband = DEFAULT_JOY_DEADBAND; - - if (m_Preferences.GetDWORDValue(CFG_OPTIONS_STICK2_DEADBAND, dword)) - m_JoystickState[1].Deadband = dword; - else - m_JoystickState[1].Deadband = DEFAULT_JOY_DEADBAND; + m_Joysticks.ReadPreferences(m_Preferences); if (!m_Preferences.GetBoolValue(CFG_OPTIONS_FREEZEINACTIVE, m_FreezeWhenInactive)) m_FreezeWhenInactive = true; @@ -681,16 +636,6 @@ void BeebWin::SavePreferences(bool saveAll) m_Preferences.SetBoolValue("TextToSpeechEnabled", m_TextToSpeechEnabled); m_Preferences.SetBoolValue("Music5000Enabled", Music5000Enabled); - m_Preferences.SetDWORDValue(CFG_OPTIONS_STICKS, m_MenuIdSticks[0]); - m_Preferences.SetDWORDValue(CFG_OPTIONS_STICKS2, m_MenuIdSticks[1]); - m_Preferences.SetDWORDValue("JoystickAxes1", m_PCAxesForJoystick[0]); - m_Preferences.SetDWORDValue("JoystickAxes2", m_PCAxesForJoystick[1]); - - m_Preferences.SetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys); - m_Preferences.SetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, m_AutoloadJoystickMap); - m_Preferences.SetDWORDValue(CFG_OPTIONS_STICK1_DEADBAND, m_JoystickState[0].Deadband); - m_Preferences.SetDWORDValue(CFG_OPTIONS_STICK2_DEADBAND, m_JoystickState[1].Deadband); - m_Preferences.SetBoolValue(CFG_OPTIONS_FREEZEINACTIVE, m_FreezeWhenInactive); m_Preferences.SetBoolValue(CFG_OPTIONS_HIDE_CURSOR, m_HideCursor); m_Preferences.SetBoolValue(CFG_OPTIONS_CAPTURE_MOUSE, m_CaptureMouse); diff --git a/Src/keymapping.h b/Src/keymapping.h new file mode 100644 index 00000000..0b9b26a4 --- /dev/null +++ b/Src/keymapping.h @@ -0,0 +1,40 @@ +/**************************************************************** +BeebEm - BBC Micro and Master 128 Emulator +Copyright (C) 1994 Nigel Magnay +Copyright (C) 1997 Mike Wyatt +Copyright (C) 1998 Robert Schmidt +Copyright (C) 2001 Richard Gellman +Copyright (C) 2004 Ken Lowe +Copyright (C) 2004 Rob O'Donnell +Copyright (C) 2005 Jon Welch +Copyright (C) 2021 Chris Needham, Tadeusz Kijkowski + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +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. + +You should have received a copy of the GNU General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. +****************************************************************/ + +#ifndef BEEBEM_KEYMAPPING_H +#define BEEBEM_KEYMAPPING_H + +struct KeyMapping { + int row; // Beeb row + int col; // Beeb col + bool shift; // Beeb shift state +}; + +typedef KeyMapping KeyPair[2]; +typedef KeyPair KeyMap[256]; // Indices are: [Virt key][shift state] + +#endif From 72a14bd87b2c1fc6807dea1a2cf28fcf52c51c2c Mon Sep 17 00:00:00 2001 From: Tadeusz Kijkowski Date: Tue, 23 Feb 2021 12:07:34 +0100 Subject: [PATCH 24/38] Changed stick prefs to avoid menu id in prefs file --- Src/BeebEm.rc | 4 +- Src/JoystickHandler.cpp | 162 ++++++++++++++++++++++++++++++---------- Src/JoystickHandler.h | 4 + Src/beebwinprefs.cpp | 2 + 4 files changed, 129 insertions(+), 43 deletions(-) diff --git a/Src/BeebEm.rc b/Src/BeebEm.rc index bf2d54bc..14aa7bbb 100644 --- a/Src/BeebEm.rc +++ b/Src/BeebEm.rc @@ -711,9 +711,9 @@ BEGIN POPUP "Joystick" BEGIN MENUITEM "First PC &Joystick", IDM_JOYSTICK + MENUITEM "Second PC Joystick", IDM_JOY1_PCJOY2 MENUITEM "&Analogue Mousestick", IDM_ANALOGUE_MOUSESTICK MENUITEM "&Digital Mousestick", IDM_DIGITAL_MOUSESTICK - MENUITEM "Second PC Joystick", IDM_JOY1_PCJOY2 MENUITEM SEPARATOR MENUITEM "Primary Stick", IDM_JOY1_PRIMARY MENUITEM "Right Thumbstick (XBox)", IDM_JOY1_SECONDARY1 @@ -721,10 +721,10 @@ BEGIN END POPUP "Second Joystick" BEGIN + MENUITEM "First PC Joystick", IDM_JOY2_PCJOY1 MENUITEM "Second PC Joystick", IDM_JOY2_PCJOY2 MENUITEM "&Analogue Mousestick", IDM_JOY2_ANALOGUE_MOUSESTICK MENUITEM "&Digital Mousestick", IDM_JOY2_DIGITAL_MOUSESTICK - MENUITEM "First PC Joystick", IDM_JOY2_PCJOY1 MENUITEM SEPARATOR MENUITEM "Primary Stick", IDM_JOY2_PRIMARY MENUITEM "Right Thumbstick (XBox)", IDM_JOY2_SECONDARY1 diff --git a/Src/JoystickHandler.cpp b/Src/JoystickHandler.cpp index 481731ab..a564ea5f 100644 --- a/Src/JoystickHandler.cpp +++ b/Src/JoystickHandler.cpp @@ -34,12 +34,6 @@ Boston, MA 02110-1301, USA. #define JOYMAP_TOKEN "*** BeebEm Joystick Map ***" -static const char *CFG_OPTIONS_STICKS = "Sticks"; -static const char *CFG_OPTIONS_STICKS2 = "Sticks2"; -static const char *CFG_OPTIONS_STICKS_TO_KEYS = "SticksToKeys"; -static const char *CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP = "AutoloadJoystickMap"; -static const char *CFG_OPTIONS_STICK_DEADBAND = "StickToKeysDeadBand"; - #define DEFAULT_JOY_DEADBAND 4096 /*****************************************************************************/ @@ -105,15 +99,15 @@ int JoystickHandler::MenuIdToAxes(int bbcIdx, UINT menuId) for (int axesIdx = 0; axesIdx < _countof(JoystickMenuIdsType::Axes); ++axesIdx) { if (menuId == JoystickMenuIds[bbcIdx].Axes[axesIdx]) - return axesIdx + 1; + return axesIdx; } } /****************************************************************************/ UINT JoystickHandler::AxesToMenuId(int bbcIdx, int pcAxes) { - if (pcAxes > 0 && pcAxes - 1 < _countof(JoystickMenuIdsType::Axes)) - return JoystickMenuIds[bbcIdx].Axes[pcAxes - 1]; + if (pcAxes >= 0 && pcAxes < _countof(JoystickMenuIdsType::Axes)) + return JoystickMenuIds[bbcIdx].Axes[pcAxes]; return 0; } @@ -172,8 +166,6 @@ void JoystickHandler::InitMenu() } if (m_MenuIdAxes[bbcIdx] != 0) CheckMenuItem(m_MenuIdAxes[bbcIdx], true); - - UpdateJoystickConfig(bbcIdx); } CheckMenuItem(IDM_JOYSTICK_TO_KEYS, m_JoystickToKeys); @@ -642,19 +634,19 @@ void JoystickHandler::TranslateJoystick(int joyId) } int axesConfig = m_JoystickConfig[bbcIndex].PCAxes; - if (axesConfig == 1) + if (axesConfig == 0) { ScaleJoystick(bbcIndex, joyInfoEx.dwXpos, joyInfoEx.dwYpos, joyCaps->wXmin, joyCaps->wYmin, joyCaps->wXmax, joyCaps->wYmax); } - else if (axesConfig == 2) + else if (axesConfig == 1) { ScaleJoystick(bbcIndex, joyInfoEx.dwUpos, joyInfoEx.dwRpos, joyCaps->wUmin, joyCaps->wRmin, joyCaps->wUmax, joyCaps->wRmax); } - else if (axesConfig == 3) + else if (axesConfig == 2) { ScaleJoystick(bbcIndex, joyInfoEx.dwZpos, joyInfoEx.dwRpos, joyCaps->wZmin, joyCaps->wRmin, @@ -1027,34 +1019,113 @@ void JoystickHandler::SaveJoystickMap() } } -static const char *CFG_OPTIONS_STICK1_DEADBAND = "Stick1ToKeysDeadBand"; -static const char *CFG_OPTIONS_STICK2_DEADBAND = "Stick2ToKeysDeadBand"; +/* Backward compatibility */ +static const char *CFG_OPTIONS_STICKS = "Sticks"; + +/* New joystick options */ +static const char *CFG_OPTIONS_STICK_PCSTICK = "Stick%dPCStick"; +static const char *CFG_OPTIONS_STICK_PCAXES = "Stick%dPCAxes"; +static const char *CFG_OPTIONS_STICK_ANALOG = "Stick%dAnalogMousepad"; +static const char *CFG_OPTIONS_STICK_DIGITAL = "Stick%dDigitalMousepad"; + +static const char *CFG_OPTIONS_STICKS_TO_KEYS = "SticksToKeys"; +static const char *CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP = "AutoloadJoystickMap"; +static const char *CFG_OPTIONS_STICKS_DEADBAND = "SticksToKeysDeadBand"; + +/****************************************************************************/ +bool JoystickHandler::GetNthBoolValue(Preferences& preferences, const char* format, int idx, bool& value) +{ + char option_str[256]; + sprintf_s(option_str, format, idx); + return preferences.GetBoolValue(option_str, value); +} + +/****************************************************************************/ +bool JoystickHandler::GetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD& value) +{ + char option_str[256]; + sprintf_s(option_str, format, idx); + return preferences.GetDWORDValue(option_str, value); +} + +/****************************************************************************/ +void JoystickHandler::SetNthBoolValue(Preferences& preferences, const char* format, int idx, bool value) +{ + char option_str[256]; + sprintf_s(option_str, format, idx); + preferences.SetBoolValue(option_str, value); +} + +/****************************************************************************/ +void JoystickHandler::SetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD value) +{ + char option_str[256]; + sprintf_s(option_str, format, idx); + preferences.SetDWORDValue(option_str, value); +} /****************************************************************************/ void JoystickHandler::ReadPreferences(Preferences& preferences) { DWORD dword; + unsigned char b; + bool flag; + + /* Clear joystick configuration */ + for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) + { + BBCJoystickConfig& config = m_JoystickConfig[bbcIdx]; + + m_MenuIdSticks[bbcIdx] = 0; + m_MenuIdAxes[bbcIdx] = 0; + + config.Enabled = false; + config.PCStick = 0; + config.PCAxes = 0; + config.AnalogMousestick = false; + config.DigitalMousestick = false; + } + + /* Backward compatibility - "Sticks" contains MenuId */ if (preferences.GetDWORDValue(CFG_OPTIONS_STICKS, dword)) + { m_MenuIdSticks[0] = dword; - else - m_MenuIdSticks[0] = 0; + m_JoystickConfig[0].Enabled = true; + m_JoystickConfig[0].PCStick = MenuIdToStick(0, dword); + m_JoystickConfig[0].AnalogMousestick = (dword == JoystickMenuIds[0].AnalogMousestick); + m_JoystickConfig[0].DigitalMousestick = (dword == JoystickMenuIds[0].DigitalMousestick); + } - if (preferences.GetDWORDValue(CFG_OPTIONS_STICKS2, dword)) - m_MenuIdSticks[1] = dword; - else - m_MenuIdSticks[1] = 0; + /* New joystick options */ + for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) + { + BBCJoystickConfig& config = m_JoystickConfig[bbcIdx]; - if (preferences.GetDWORDValue("JoystickAxes1", dword)) - m_JoystickConfig[0].PCAxes = dword; - else - m_JoystickConfig[0].PCAxes = 1; - m_MenuIdAxes[0] = AxesToMenuId(0, m_JoystickConfig[0].PCAxes); + if (GetNthBoolValue(preferences, CFG_OPTIONS_STICK_ANALOG, bbcIdx + 1, flag) && flag) + { + config.AnalogMousestick = true; + config.Enabled = true; + m_MenuIdSticks[bbcIdx] = JoystickMenuIds[bbcIdx].AnalogMousestick; + } + else if (GetNthBoolValue(preferences, CFG_OPTIONS_STICK_DIGITAL, bbcIdx + 1, flag) && flag) + { + config.DigitalMousestick = true; + config.Enabled = true; + m_MenuIdSticks[bbcIdx] = JoystickMenuIds[bbcIdx].DigitalMousestick; - if (preferences.GetDWORDValue("JoystickAxes2", dword)) - m_JoystickConfig[1].PCAxes = dword; - else - m_JoystickConfig[1].PCAxes = 1; - m_MenuIdAxes[1] = AxesToMenuId(1, m_JoystickConfig[1].PCAxes); + } + else if (GetNthDWORDValue(preferences, CFG_OPTIONS_STICK_PCSTICK, bbcIdx + 1, dword) && dword != 0) + { + config.PCStick = dword; + config.Enabled = true; + m_MenuIdSticks[bbcIdx] = StickToMenuId(bbcIdx, dword); + } + + if (GetNthDWORDValue(preferences, CFG_OPTIONS_STICK_PCAXES, bbcIdx + 1, dword)) + config.PCAxes = dword; + + m_MenuIdAxes[bbcIdx] = AxesToMenuId(bbcIdx, config.PCAxes); + } if (!preferences.GetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys)) m_JoystickToKeys = false; @@ -1063,24 +1134,33 @@ void JoystickHandler::ReadPreferences(Preferences& preferences) m_AutoloadJoystickMap)) m_AutoloadJoystickMap = false; - preferences.EraseValue("Stick1ToKeysDeadBand"); - preferences.EraseValue("Stick2ToKeysDeadBand"); - - if (preferences.GetDWORDValue(CFG_OPTIONS_STICK_DEADBAND, dword)) + if (preferences.GetDWORDValue(CFG_OPTIONS_STICKS_DEADBAND, dword)) m_Deadband = dword; else m_Deadband = DEFAULT_JOY_DEADBAND; + + /* Remove obsolete values */ + preferences.EraseValue(CFG_OPTIONS_STICKS); + preferences.EraseValue("Sticks2"); + preferences.EraseValue("JoystickAxes1"); + preferences.EraseValue("JoystickAxes2"); + preferences.EraseValue("Stick1ToKeysDeadBand"); + preferences.EraseValue("Stick2ToKeysDeadBand"); } /****************************************************************************/ void JoystickHandler::WritePreferences(Preferences& preferences) { - preferences.SetDWORDValue(CFG_OPTIONS_STICKS, m_MenuIdSticks[0]); - preferences.SetDWORDValue(CFG_OPTIONS_STICKS2, m_MenuIdSticks[1]); - preferences.SetDWORDValue("JoystickAxes1", m_JoystickConfig[0].PCAxes); - preferences.SetDWORDValue("JoystickAxes2", m_JoystickConfig[1].PCAxes); + for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) + { + BBCJoystickConfig& config = m_JoystickConfig[bbcIdx]; + SetNthBoolValue(preferences, CFG_OPTIONS_STICK_ANALOG, bbcIdx + 1, config.AnalogMousestick); + SetNthBoolValue(preferences, CFG_OPTIONS_STICK_DIGITAL, bbcIdx + 1, config.DigitalMousestick); + SetNthDWORDValue(preferences, CFG_OPTIONS_STICK_PCSTICK, bbcIdx + 1, config.PCStick); + SetNthDWORDValue(preferences, CFG_OPTIONS_STICK_PCAXES, bbcIdx + 1, config.PCAxes); + } preferences.SetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys); preferences.SetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, m_AutoloadJoystickMap); - preferences.SetDWORDValue(CFG_OPTIONS_STICK_DEADBAND, m_Deadband); + preferences.SetDWORDValue(CFG_OPTIONS_STICKS_DEADBAND, m_Deadband); } diff --git a/Src/JoystickHandler.h b/Src/JoystickHandler.h index b512397e..7cf66b21 100644 --- a/Src/JoystickHandler.h +++ b/Src/JoystickHandler.h @@ -162,6 +162,10 @@ class JoystickHandler bool WriteJoyMap(const char *filename, JoyMap *joymap); /* Preferences */ + bool GetNthBoolValue(Preferences& preferences, const char* format, int idx, bool& value); + bool GetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD& value); + void SetNthBoolValue(Preferences& preferences, const char* format, int idx, bool value); + void SetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD value); void ReadPreferences(Preferences& preferences); void WritePreferences(Preferences& preferences); }; diff --git a/Src/beebwinprefs.cpp b/Src/beebwinprefs.cpp index c5e162d6..0abd7f39 100644 --- a/Src/beebwinprefs.cpp +++ b/Src/beebwinprefs.cpp @@ -717,6 +717,8 @@ void BeebWin::SavePreferences(bool saveAll) m_Preferences.SetDWORDValue("BitmapCaptureResolution", m_MenuIdCaptureResolution); m_Preferences.SetDWORDValue("BitmapCaptureFormat", m_MenuIdCaptureFormat); + m_Joysticks.WritePreferences(m_Preferences); + RECT rect; GetWindowRect(m_hWnd,&rect); m_Preferences.SetBinaryValue("WindowPos", &rect, sizeof(rect)); From c652921e30b48ad3104de107ffb0b5b50f93feff Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Wed, 24 Feb 2021 10:24:25 +0100 Subject: [PATCH 25/38] Save joystick order in preferences file This is somewhat experimental and may be changed or removed in the future --- .gitignore | 1 + Src/BeebEm.rc | 4 +- Src/JoystickHandler.cpp | 354 +++++++++++++++++++++++++++++++--------- Src/JoystickHandler.h | 72 +++++++- Src/beebwinprefs.cpp | 2 + Src/preferences.cpp | 25 +++ Src/preferences.h | 2 + 7 files changed, 377 insertions(+), 83 deletions(-) diff --git a/.gitignore b/.gitignore index eb4ae4ba..2b9db9ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.user +.vs Src/.vs Src/BeebEm.aps Src/BeebEm.VC.db diff --git a/Src/BeebEm.rc b/Src/BeebEm.rc index 14aa7bbb..f4fae1f5 100644 --- a/Src/BeebEm.rc +++ b/Src/BeebEm.rc @@ -721,7 +721,7 @@ BEGIN END POPUP "Second Joystick" BEGIN - MENUITEM "First PC Joystick", IDM_JOY2_PCJOY1 + MENUITEM "First PC &Joystick", IDM_JOY2_PCJOY1 MENUITEM "Second PC Joystick", IDM_JOY2_PCJOY2 MENUITEM "&Analogue Mousestick", IDM_JOY2_ANALOGUE_MOUSESTICK MENUITEM "&Digital Mousestick", IDM_JOY2_DIGITAL_MOUSESTICK @@ -740,7 +740,7 @@ BEGIN MENUITEM "Load Joystick Mapping...", IDM_LOADJOYMAP MENUITEM "Save Joystick Mapping...", IDM_SAVEJOYMAP END - MENUITEM "Initialise Joysticks", IDM_INIT_JOYSTICK + MENUITEM "Rescan Joysticks", IDM_INIT_JOYSTICK MENUITEM SEPARATOR MENUITEM "&Freeze when inactive", IDM_FREEZEINACTIVE MENUITEM "&Hide Cursor", IDM_HIDECURSOR diff --git a/Src/JoystickHandler.cpp b/Src/JoystickHandler.cpp index a564ea5f..b2ce1543 100644 --- a/Src/JoystickHandler.cpp +++ b/Src/JoystickHandler.cpp @@ -32,12 +32,59 @@ Boston, MA 02110-1301, USA. #include "filedialog.h" #include "SelectKeyDialog.h" +#include +#include + #define JOYMAP_TOKEN "*** BeebEm Joystick Map ***" #define DEFAULT_JOY_DEADBAND 4096 /*****************************************************************************/ -HWND JoystickHandler::GetHwnd() { return m_BeebWin->m_hWnd; } +std::string JoystickOrderEntry::to_string() +{ + char buf[10]; + sprintf_s(buf, "%04x:%04x", mId(), pId()); + std::string result(buf); +#if 0 // Joystick names from joyGetDevCaps are useless + if (Name.length() != 0) + result += " # " + Name; +#endif + return result; +} + +/*****************************************************************************/ +bool JoystickOrderEntry::from_string(const std::string& str) +{ + Name = ""; + + auto split = str.find('#'); + if (split != std::string::npos) + { + ++split; + while (split < str.length() && std::isspace(str[split])) + ++split; + Name = str.substr(split); + } + + return sscanf(str.c_str(), "%x:%x", &mId(), &pId()) == 2; +} + +/*****************************************************************************/ +std::string JoystickDev::DisplayString() +{ + if (!Configured) + return std::string{}; + + std::string name = Caps.szPname; + if (Instance != 1) + name += " #" + std::to_string(Instance); + return name; +} + +/*****************************************************************************/ +HWND JoystickHandler::GetHWnd() { return m_BeebWin->m_hWnd; } + +HMENU JoystickHandler::GetHMenu() { return m_BeebWin->m_hMenu; } int JoystickHandler::GetXWinSize() { return m_BeebWin->m_XWinSize; } @@ -47,6 +94,16 @@ void JoystickHandler::CheckMenuItem(UINT id, bool checked) { m_BeebWin->CheckMen void JoystickHandler::EnableMenuItem(UINT id, bool enabled) { m_BeebWin->EnableMenuItem(id, enabled); } +void JoystickHandler::SetMenuItemText(UINT id, const std::string& text) +{ + MENUITEMINFO mii{ 0 }; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_STRING; + mii.fType = MFT_STRING; + mii.dwTypeData = const_cast(text.c_str()); + SetMenuItemInfo(GetHMenu(), id, FALSE, &mii); +} + /*****************************************************************************/ /* All this stuff will go away when joystick configuration is done via dialog */ @@ -101,6 +158,7 @@ int JoystickHandler::MenuIdToAxes(int bbcIdx, UINT menuId) if (menuId == JoystickMenuIds[bbcIdx].Axes[axesIdx]) return axesIdx; } + return 0; } /****************************************************************************/ @@ -118,8 +176,10 @@ void JoystickHandler::UpdateJoystickConfig(int bbcIdx) m_JoystickConfig[bbcIdx].Enabled = (m_MenuIdAxes[bbcIdx] != 0); m_JoystickConfig[bbcIdx].PCStick = MenuIdToStick(bbcIdx, m_MenuIdSticks[bbcIdx]); m_JoystickConfig[bbcIdx].PCAxes = MenuIdToAxes(bbcIdx, m_MenuIdAxes[bbcIdx]); - m_JoystickConfig[bbcIdx].AnalogMousestick = (m_MenuIdAxes[bbcIdx] == JoystickMenuIds[bbcIdx].AnalogMousestick); - m_JoystickConfig[bbcIdx].DigitalMousestick = (m_MenuIdAxes[bbcIdx] == JoystickMenuIds[bbcIdx].DigitalMousestick); + m_JoystickConfig[bbcIdx].AnalogMousestick = + (static_cast(m_MenuIdAxes[bbcIdx]) == JoystickMenuIds[bbcIdx].AnalogMousestick); + m_JoystickConfig[bbcIdx].DigitalMousestick = + (static_cast(m_MenuIdAxes[bbcIdx]) == JoystickMenuIds[bbcIdx].DigitalMousestick); } /****************************************************************************/ @@ -192,11 +252,29 @@ void JoystickHandler::UpdateJoystickMenu() for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) { bool EnableAxes = false; +#if 0 // This needs rework (proper joystick name from DirectInput) + static const char* const menuItems[2] = { "First PC &Joystick", "Second PC Joystick" }; + + for (int pcIdx = 0; pcIdx < _countof(JoystickMenuIdsType::Joysticks); ++pcIdx) + { + auto* dev = m_PCJoystickState[pcIdx].Dev; + if (dev) + { + SetMenuItemText(JoystickMenuIds[bbcIdx].Joysticks[pcIdx], dev->DisplayString()); + } + else + { + SetMenuItemText(JoystickMenuIds[bbcIdx].Joysticks[pcIdx], menuItems[pcIdx]); + } + } +#endif + for (int pcIdx = 0; pcIdx < NUM_PC_JOYSTICKS; ++pcIdx) { if (IsPCJoystickAssigned(pcIdx, bbcIdx)) EnableAxes = true; } + for (int axesIdx = 0; axesIdx < _countof(JoystickMenuIdsType::Axes); ++axesIdx) EnableMenuItem(JoystickMenuIds[bbcIdx].Axes[axesIdx], EnableAxes); } @@ -222,7 +300,7 @@ void JoystickHandler::ProcessMenuCommand(int bbcIdx, UINT MenuId) } } - if (MenuId == m_MenuIdSticks[bbcIdx]) + if (MenuId == static_cast(m_MenuIdSticks[bbcIdx])) { /* Joystick switched off completely */ m_MenuIdSticks[bbcIdx] = 0; @@ -274,6 +352,117 @@ void JoystickHandler::ResetJoystick(void) } +/****************************************************************************/ +void JoystickHandler::ScanJoysticks(void) +{ + JOYINFOEX joyInfoEx; + std::map> listById; + + // Clear PC joystick list + for (PCJoystickState& state : m_PCJoystickState) + { + state.Captured = false; + state.Dev = nullptr; + state.JoyIndex = -1; + } + + // Get all configured and present joysticks + for (int devIdx = 0; devIdx < MAX_JOYSTICK_DEVS; ++devIdx) + { + JoystickDev& dev = m_JoystickDevs[devIdx]; + dev.Configured = false; + dev.Present = false; + dev.Instance = 0; + dev.Order = -1; + dev.JoyIndex = devIdx; + + memset(&joyInfoEx, 0, sizeof(joyInfoEx)); + joyInfoEx.dwSize = sizeof(joyInfoEx); + joyInfoEx.dwFlags = JOY_RETURNBUTTONS; + + DWORD Result = joyGetDevCaps(devIdx, &dev.Caps, sizeof(JOYCAPS)); + if (Result == JOYERR_NOERROR) + { + dev.Configured = true; + + Result = joyGetPosEx(devIdx, &joyInfoEx); + if (Result == JOYERR_NOERROR) + { + dev.Present = true; + listById[dev.Id()].push_back(&dev); + dev.Instance = listById[dev.Id()].size(); + } + } + } + + int joyIdx = 0; + int order = 0; + // Start with joysticks in the order list + for (JoystickOrderEntry& entry : m_JoystickOrder) + { + std::list& list = listById[entry]; + if (list.size() != 0) + { + JoystickDev& dev = *(list.front()); + list.pop_front(); + dev.Order = order++; + entry.JoyIndex = dev.JoyIndex; + if (dev.Present && joyIdx < NUM_PC_JOYSTICKS) + { + m_PCJoystickState[joyIdx].Dev = &dev; + m_PCJoystickState[joyIdx].JoyIndex = dev.JoyIndex; + ++joyIdx; + } + } + else + { + entry.JoyIndex = -1; + } + } + + std::list newJoysticks; + // Add joysticks not in the order list + for (JoystickDev& dev : m_JoystickDevs) + { + if (joyIdx >= NUM_PC_JOYSTICKS) + break; + + if (dev.Present && dev.Order == -1) + { + m_PCJoystickState[joyIdx].Dev = &dev; + m_PCJoystickState[joyIdx].JoyIndex = dev.JoyIndex; + ++joyIdx; + newJoysticks.push_back(&dev); + } + } + + // If joystick order list is too long, remove some unconnected entries + // Remove entries from the beginning or from the end, or some other order? + int to_remove = newJoysticks.size() + m_JoystickOrder.size() - MAX_JOYSTICK_ORDER; + int idx = m_JoystickOrder.size() - 1; + while (to_remove > 0 && idx >= 0) + { + if (m_JoystickOrder[idx].JoyIndex == -1) + { + m_JoystickOrder.erase(m_JoystickOrder.begin() + idx); + --to_remove; + } + --idx; + } + + // Add new joystick at the end of order list + for (JoystickDev* dev : newJoysticks) + m_JoystickOrder.emplace_back(dev->Id(), dev->DisplayString(), dev->JoyIndex); + + // Update order in joystick list + for (idx = 0; idx < static_cast(m_JoystickOrder.size()); ++idx) + { + JoystickOrderEntry& entry = m_JoystickOrder[idx]; + if (entry.JoyIndex != -1) + m_JoystickDevs[entry.JoyIndex].Order = idx; + } +} + /****************************************************************************/ bool JoystickHandler::InitJoystick(bool verbose) { @@ -281,19 +470,19 @@ bool JoystickHandler::InitJoystick(bool verbose) ResetJoystick(); - // TODO: Joystick ordering here + ScanJoysticks(); + if (IsPCJoystickOn(0) || IsPCJoystickOn(1) || m_JoystickToKeys) { for (int pcIdx = 0; pcIdx < NUM_PC_JOYSTICKS; ++pcIdx) { - Success = CaptureJoystick(pcIdx, verbose); - if (!Success) - break; + if (IsPCJoystickOn(pcIdx) || m_JoystickToKeys) + Success = CaptureJoystick(pcIdx, verbose); } if (!m_JoystickTimerRunning) { - SetTimer(GetHwnd(), 3, 20, NULL); + SetTimer(GetHWnd(), 3, 20, NULL); m_JoystickTimerRunning = true; } } @@ -301,7 +490,7 @@ bool JoystickHandler::InitJoystick(bool verbose) { if (m_JoystickTimerRunning) { - KillTimer(GetHwnd(), 3); + KillTimer(GetHWnd(), 3); m_JoystickTimerRunning = false; } } @@ -312,60 +501,21 @@ bool JoystickHandler::InitJoystick(bool verbose) } /****************************************************************************/ -// TODO: Change it all - scan all joysticks first and then order and assign bool JoystickHandler::CaptureJoystick(int Index, bool verbose) { bool success = false; - DWORD JoyIndex; - DWORD Result; - JOYINFOEX joyInfoEx; - UINT numDevs = joyGetNumDevs(); - - // Scan for first present joystick index. It doesn't have to be - // consecutive number. - if (Index == 0) - JoyIndex = 0; - else - JoyIndex = m_PCJoystickState[Index - 1].JoyIndex + 1; - - // Find first joystick that is known AND connected - Result = JOYERR_UNPLUGGED; - while (Result != JOYERR_NOERROR && JoyIndex != numDevs) - { - memset(&joyInfoEx, 0, sizeof(joyInfoEx)); - joyInfoEx.dwSize = sizeof(joyInfoEx); - joyInfoEx.dwFlags = JOY_RETURNBUTTONS; - - Result = joyGetDevCaps(JoyIndex, &m_PCJoystickState[Index].Caps, sizeof(JOYCAPS)); - if (Result == JOYERR_NOERROR) - Result = joyGetPosEx(JoyIndex, &joyInfoEx); - if (Result != JOYERR_NOERROR) - ++JoyIndex; - } - - if (Result == ERROR_SUCCESS) + if (m_PCJoystickState[Index].Dev) { m_PCJoystickState[Index].Captured = true; - m_PCJoystickState[Index].JoyIndex = JoyIndex; success = true; } else if (verbose) { - if (Result == ERROR_DEVICE_NOT_CONNECTED || Result == JOYERR_UNPLUGGED) - { - char str[100]; - sprintf(str, "Joystick %d is not plugged in", Index + 1); + char str[100]; + sprintf(str, "Failed to initialise joystick %d", Index + 1); - MessageBox(GetHwnd(), str, WindowTitle, MB_OK | MB_ICONWARNING); - } - else - { - char str[100]; - sprintf(str, "Failed to initialise joystick %d", Index + 1); - - MessageBox(GetHwnd(), str, WindowTitle, MB_OK | MB_ICONWARNING); - } + MessageBox(GetHWnd(), str, WindowTitle, MB_OK | MB_ICONWARNING); } return success; @@ -586,9 +736,9 @@ void JoystickHandler::TranslateJoystick(int joyId) 0, 65535, 0, 65535, 0, 65535 }; bool success = false; - JOYINFOEX joyInfoEx; + JOYINFOEX joyInfoEx{}; - const JOYCAPS* joyCaps = &m_PCJoystickState[joyId].Caps; + const JOYCAPS* joyCaps = &m_PCJoystickState[joyId].Dev->Caps; DWORD joyIndex = m_PCJoystickState[joyId].JoyIndex; joyInfoEx.dwSize = sizeof(joyInfoEx); @@ -605,9 +755,12 @@ void JoystickHandler::TranslateJoystick(int joyId) joyInfoEx.dwXpos = joyInfoEx.dwYpos = joyInfoEx.dwZpos = 32768; joyInfoEx.dwRpos = joyInfoEx.dwUpos = joyInfoEx.dwVpos = 32768; joyInfoEx.dwPOV = JOY_POVCENTERED; - // Reset 'captured' flag and update menu entry - m_PCJoystickState[joyId].Captured = false; - UpdateJoystickMenu(); + if (m_PCJoystickState[joyId].Captured) + { + // Reset 'captured' flag and update menu entry + m_PCJoystickState[joyId].Captured = false; + UpdateJoystickMenu(); + } } // PC joystick to BBC joystick @@ -717,7 +870,7 @@ void JoystickHandler::CheckForJoystickMap(const char *path) /****************************************************************************/ void JoystickHandler::ResetJoystickMap() { - if (MessageBox(GetHwnd(), "Clear joystick to keyboard mapping table?", WindowTitle, MB_YESNO | MB_ICONQUESTION) != IDYES) + if (MessageBox(GetHWnd(), "Clear joystick to keyboard mapping table?", WindowTitle, MB_YESNO | MB_ICONQUESTION) != IDYES) return; ResetJoyMap(&JoystickMap); @@ -793,7 +946,7 @@ bool JoystickHandler::ReadJoyMap(const char *filename, JoyMap *joymap) char errstr[500]; sprintf(errstr, "Invalid line in joystick mapping file:\n %s\n line %d\n", filename, line); - MessageBox(GetHwnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); + MessageBox(GetHWnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); success = false; break; } @@ -805,7 +958,7 @@ bool JoystickHandler::ReadJoyMap(const char *filename, JoyMap *joymap) char errstr[500]; sprintf(errstr, "Invalid input name in joystick mapping file:\n %s\n line %d\n", filename, line); - MessageBox(GetHwnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); + MessageBox(GetHWnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); success = false; break; } @@ -827,7 +980,7 @@ bool JoystickHandler::ReadJoyMap(const char *filename, JoyMap *joymap) char errstr[500]; sprintf(errstr, "Invalid key name in joystick mapping file:\n %s\n line %d\n", filename, line); - MessageBox(GetHwnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); + MessageBox(GetHWnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); success = false; break; } @@ -857,7 +1010,7 @@ bool JoystickHandler::ReadJoyMap(const char *filename, JoyMap *joymap) char errstr[500]; sprintf(errstr, "Invalid key name in joystick mapping file:\n %s\n line %d\n", filename, line); - MessageBox(GetHwnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); + MessageBox(GetHWnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); success = false; break; } @@ -898,7 +1051,7 @@ void JoystickHandler::LoadJoystickMap() strcpy(DefaultPath, m_BeebWin->GetUserDataPath()); } - FileDialog fileDialog(GetHwnd(), FileName, sizeof(FileName), DefaultPath, filter); + FileDialog fileDialog(GetHWnd(), FileName, sizeof(FileName), DefaultPath, filter); if (m_JoystickMapPath[0] == '\0' && !m_AutoloadJoystickMap) { @@ -998,7 +1151,7 @@ void JoystickHandler::SaveJoystickMap() strcpy(DefaultPath, m_BeebWin->GetUserDataPath()); } - FileDialog fileDialog(GetHwnd(), FileName, sizeof(FileName), DefaultPath, filter); + FileDialog fileDialog(GetHWnd(), FileName, sizeof(FileName), DefaultPath, filter); if (m_JoystickMapPath[0] == '\0' && !m_AutoloadJoystickMap) { @@ -1032,6 +1185,8 @@ static const char *CFG_OPTIONS_STICKS_TO_KEYS = "SticksToKeys"; static const char *CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP = "AutoloadJoystickMap"; static const char *CFG_OPTIONS_STICKS_DEADBAND = "SticksToKeysDeadBand"; +static const char *CFG_OPTIONS_JOYSTICK_ORDER = "JoystickOrder%d"; + /****************************************************************************/ bool JoystickHandler::GetNthBoolValue(Preferences& preferences, const char* format, int idx, bool& value) { @@ -1048,6 +1203,14 @@ bool JoystickHandler::GetNthDWORDValue(Preferences& preferences, const char* for return preferences.GetDWORDValue(option_str, value); } +/****************************************************************************/ +bool JoystickHandler::GetNthStringValue(Preferences& preferences, const char* format, int idx, std::string& value) +{ + char option_str[256]; + sprintf_s(option_str, format, idx); + return preferences.GetStringValue(option_str, value); +} + /****************************************************************************/ void JoystickHandler::SetNthBoolValue(Preferences& preferences, const char* format, int idx, bool value) { @@ -1064,11 +1227,26 @@ void JoystickHandler::SetNthDWORDValue(Preferences& preferences, const char* for preferences.SetDWORDValue(option_str, value); } +/****************************************************************************/ +void JoystickHandler::SetNthStringValue(Preferences& preferences, const char* format, int idx, const std::string& value) +{ + char option_str[256]; + sprintf_s(option_str, format, idx); + preferences.SetStringValue(option_str, value); +} + +/****************************************************************************/ +void JoystickHandler::EraseNthValue(Preferences& preferences, const char* format, int idx) +{ + char option_str[256]; + sprintf_s(option_str, format, idx); + preferences.EraseValue(option_str); +} + /****************************************************************************/ void JoystickHandler::ReadPreferences(Preferences& preferences) { DWORD dword; - unsigned char b; bool flag; /* Clear joystick configuration */ @@ -1139,13 +1317,18 @@ void JoystickHandler::ReadPreferences(Preferences& preferences) else m_Deadband = DEFAULT_JOY_DEADBAND; - /* Remove obsolete values */ - preferences.EraseValue(CFG_OPTIONS_STICKS); - preferences.EraseValue("Sticks2"); - preferences.EraseValue("JoystickAxes1"); - preferences.EraseValue("JoystickAxes2"); - preferences.EraseValue("Stick1ToKeysDeadBand"); - preferences.EraseValue("Stick2ToKeysDeadBand"); + m_JoystickOrder.clear(); + for (int idx = 0; idx < MAX_JOYSTICK_ORDER; ++idx) + { + std::string value; + JoystickOrderEntry entry; + if (!GetNthStringValue(preferences, CFG_OPTIONS_JOYSTICK_ORDER, idx + 1, value) + || !entry.from_string(value)) + break; + m_JoystickOrder.push_back(entry); + + } + } /****************************************************************************/ @@ -1163,4 +1346,29 @@ void JoystickHandler::WritePreferences(Preferences& preferences) preferences.SetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys); preferences.SetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, m_AutoloadJoystickMap); preferences.SetDWORDValue(CFG_OPTIONS_STICKS_DEADBAND, m_Deadband); + + /* Remove obsolete values */ + preferences.EraseValue(CFG_OPTIONS_STICKS); + preferences.EraseValue("Sticks2"); + preferences.EraseValue("JoystickAxes1"); + preferences.EraseValue("JoystickAxes2"); + preferences.EraseValue("Stick1ToKeysDeadBand"); + preferences.EraseValue("Stick2ToKeysDeadBand"); +} + +/****************************************************************************/ +void JoystickHandler::WriteJoystickOrder(Preferences& preferences) +{ + for (UINT idx = 0; idx < MAX_JOYSTICK_ORDER; ++idx) + { + if (idx < m_JoystickOrder.size()) + { + SetNthStringValue(preferences, CFG_OPTIONS_JOYSTICK_ORDER, idx + 1, + m_JoystickOrder[idx].to_string()); + } + else + { + EraseNthValue(preferences, CFG_OPTIONS_JOYSTICK_ORDER, idx + 1); + } + } } diff --git a/Src/JoystickHandler.h b/Src/JoystickHandler.h index 7cf66b21..a5d5977f 100644 --- a/Src/JoystickHandler.h +++ b/Src/JoystickHandler.h @@ -32,7 +32,14 @@ Boston, MA 02110-1301, USA. #include "beebemrc.h" #include "keymapping.h" +#include + +/* Max number of joysticks in joystickapi */ +#define MAX_JOYSTICK_DEVS 16 +/* Max number of joysticks to capture */ #define NUM_PC_JOYSTICKS 4 +/* Max number of entries on joystick order list */ +#define MAX_JOYSTICK_ORDER 16 #define JOYSTICK_MAX_AXES 16 #define JOYSTICK_MAX_BTNS 16 @@ -66,16 +73,56 @@ typedef KeyPair JoyMap[BEEB_VKEY_JOY_COUNT]; class BeebWin; -struct PCJoystickState { +struct JoystickId : std::pair +{ + using std::pair::pair; + + // Manufacturer ID aka Vendor ID + int& mId() { return first; } + // Product ID + int& pId() { return second; } +}; + +struct JoystickOrderEntry : JoystickId +{ + std::string Name{}; + int JoyIndex{ -1 }; + + JoystickOrderEntry() = default; + JoystickOrderEntry(JoystickId id, const std::string& name, int joyIndex) : + JoystickId(id), Name(name), JoyIndex(joyIndex) {} + JoystickOrderEntry(int mid, int pid, const std::string& name) : + JoystickId(mid, pid), Name(name) {} + + std::string to_string(); + bool from_string(const std::string&); +}; + +struct JoystickDev +{ JOYCAPS Caps{}; - unsigned int JoyIndex{ 0 }; - bool Captured{ false }; - unsigned int PrevAxes{ 0 }; - unsigned int PrevBtns{ 0 }; - bool JoystickToKeysActive{ false }; + int Instance{ 0 }; + int Order{ -1 }; + int JoyIndex{ -1 }; + bool Configured{ false }; + bool Present{ false }; + + JoystickId Id() { return JoystickId{ Caps.wMid, Caps.wPid }; } + std::string DisplayString(); }; -struct BBCJoystickConfig { +struct PCJoystickState +{ + JoystickDev* Dev{ nullptr }; + int JoyIndex{ -1 }; + bool Captured{ false }; + unsigned int PrevAxes{ 0 }; + unsigned int PrevBtns{ 0 }; + bool JoystickToKeysActive{ false }; +}; + +struct BBCJoystickConfig +{ bool Enabled{ false }; int PCStick{ 0 }; int PCAxes{ 0 }; @@ -87,6 +134,7 @@ class JoystickHandler { BeebWin* m_BeebWin; bool m_JoystickTimerRunning{ false }; + JoystickDev m_JoystickDevs[MAX_JOYSTICK_DEVS]; PCJoystickState m_PCJoystickState[NUM_PC_JOYSTICKS]; BBCJoystickConfig m_JoystickConfig[NUM_BBC_JOYSTICKS]; int m_MenuIdSticks[NUM_BBC_JOYSTICKS]{}; @@ -96,6 +144,7 @@ class JoystickHandler bool m_AutoloadJoystickMap{ false }; HWND m_JoystickTarget{ nullptr }; char m_JoystickMapPath[_MAX_PATH]{}; + std::vector m_JoystickOrder; public: JoystickHandler(BeebWin* beebWin) : m_BeebWin{ beebWin } {} @@ -107,11 +156,13 @@ class JoystickHandler void SetJoystickTarget(HWND target) { m_JoystickTarget = target; } /* BeebWin access */ - HWND GetHwnd(); + HWND GetHWnd(); + HMENU GetHMenu(); int GetXWinSize(); int GetYWinSize(); void CheckMenuItem(UINT id, bool checked); void EnableMenuItem(UINT id, bool enabled); + void SetMenuItemText(UINT id, const std::string& text); /* Menu handling - will soon be gone */ int MenuIdToStick(int bbcIdx, UINT menuId); @@ -129,6 +180,7 @@ class JoystickHandler void ToggleAutoloadJoystickMap(); /* Initialization */ + void ScanJoysticks(void); void ResetJoystick(void); bool InitJoystick(bool verbose = false); bool CaptureJoystick(int Index, bool verbose); @@ -164,10 +216,14 @@ class JoystickHandler /* Preferences */ bool GetNthBoolValue(Preferences& preferences, const char* format, int idx, bool& value); bool GetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD& value); + bool GetNthStringValue(Preferences& preferences, const char* format, int idx, std::string& value); void SetNthBoolValue(Preferences& preferences, const char* format, int idx, bool value); void SetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD value); + void SetNthStringValue(Preferences& preferences, const char* format, int idx, const std::string& value); + void EraseNthValue(Preferences& preferences, const char* format, int idx); void ReadPreferences(Preferences& preferences); void WritePreferences(Preferences& preferences); + void WriteJoystickOrder(Preferences& preferences); }; #endif diff --git a/Src/beebwinprefs.cpp b/Src/beebwinprefs.cpp index 0abd7f39..dee6b5d1 100644 --- a/Src/beebwinprefs.cpp +++ b/Src/beebwinprefs.cpp @@ -736,6 +736,8 @@ void BeebWin::SavePreferences(bool saveAll) m_Preferences.SetBoolValue("WriteInstructionCounts", m_WriteInstructionCounts); + m_Joysticks.WriteJoystickOrder(m_Preferences); + if (m_Preferences.Save(m_PrefsFile) == Preferences::Result::Success) { m_AutoSavePrefsChanged = false; } diff --git a/Src/preferences.cpp b/Src/preferences.cpp index 0e7dd78a..e4ce734a 100644 --- a/Src/preferences.cpp +++ b/Src/preferences.cpp @@ -158,6 +158,24 @@ bool Preferences::GetStringValue(const char *id, char *str) //----------------------------------------------------------------------------- +bool Preferences::GetStringValue(const char *id, std::string& str) +{ + bool found = true; + + PrefsMap::const_iterator i = m_Prefs.find(id); + + if (i != m_Prefs.end()) { + str = i->second; + } + else { + found = false; + } + + return found; +} + +//----------------------------------------------------------------------------- + void Preferences::SetStringValue(const char *id, const char *str) { m_Prefs[id] = str; @@ -165,6 +183,13 @@ void Preferences::SetStringValue(const char *id, const char *str) //----------------------------------------------------------------------------- +void Preferences::SetStringValue(const char *id, const std::string& str) +{ + m_Prefs[id] = str; +} + +//----------------------------------------------------------------------------- + bool Preferences::GetDWORDValue(const char *id, DWORD &dw) { bool found = true; diff --git a/Src/preferences.h b/Src/preferences.h index d8342174..ae16a51f 100644 --- a/Src/preferences.h +++ b/Src/preferences.h @@ -46,7 +46,9 @@ class Preferences { bool GetBinaryValue(const char *id, void *bin, size_t binsize); void SetBinaryValue(const char *id, const void *bin, size_t binsize); bool GetStringValue(const char *id, char *str); + bool GetStringValue(const char *id, std::string& str); void SetStringValue(const char *id, const char *str); + void SetStringValue(const char *id, const std::string& str); bool GetDWORDValue(const char *id, DWORD &dw); void SetDWORDValue(const char *id, DWORD dw); bool GetBoolValue(const char *id, bool &b); From d764172c655190d6ad2e37836582499901ccc514 Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Wed, 24 Feb 2021 11:20:11 +0100 Subject: [PATCH 26/38] Moved JoystickHandler details out of header file, left only interface --- Src/BeebEm.vcxproj | 1 - Src/BeebEm.vcxproj.filters | 3 - Src/JoystickHandler.cpp | 309 ++++++++++++++++++++++++++++++------- Src/JoystickHandler.h | 136 ++-------------- Src/beebwin.h | 10 +- Src/keymapping.h | 40 ----- 6 files changed, 277 insertions(+), 222 deletions(-) delete mode 100644 Src/keymapping.h diff --git a/Src/BeebEm.vcxproj b/Src/BeebEm.vcxproj index 66fc7c88..857e2d6a 100644 --- a/Src/BeebEm.vcxproj +++ b/Src/BeebEm.vcxproj @@ -344,7 +344,6 @@ - diff --git a/Src/BeebEm.vcxproj.filters b/Src/BeebEm.vcxproj.filters index 0a71d39c..6b221c45 100644 --- a/Src/BeebEm.vcxproj.filters +++ b/Src/BeebEm.vcxproj.filters @@ -500,9 +500,6 @@ Header Files - - Header Files - diff --git a/Src/JoystickHandler.cpp b/Src/JoystickHandler.cpp index b2ce1543..10e14c02 100644 --- a/Src/JoystickHandler.cpp +++ b/Src/JoystickHandler.cpp @@ -31,14 +31,172 @@ Boston, MA 02110-1301, USA. #include "sysvia.h" #include "filedialog.h" #include "SelectKeyDialog.h" +#include "atodconv.h" #include #include +#include #define JOYMAP_TOKEN "*** BeebEm Joystick Map ***" #define DEFAULT_JOY_DEADBAND 4096 +struct JoystickId : std::pair +{ + using std::pair::pair; + + // Manufacturer ID aka Vendor ID + int& mId() { return first; } + // Product ID + int& pId() { return second; } +}; + +struct JoystickOrderEntry : JoystickId +{ + std::string Name{}; + int JoyIndex{ -1 }; + + JoystickOrderEntry() = default; + JoystickOrderEntry(JoystickId id, const std::string& name, int joyIndex) : + JoystickId(id), Name(name), JoyIndex(joyIndex) {} + JoystickOrderEntry(int mid, int pid, const std::string& name) : + JoystickId(mid, pid), Name(name) {} + + std::string to_string(); + bool from_string(const std::string&); +}; + +struct JoystickDev +{ + JOYCAPS Caps{}; + int Instance{ 0 }; + int Order{ -1 }; + int JoyIndex{ -1 }; + bool Configured{ false }; + bool Present{ false }; + + JoystickId Id() { return JoystickId{ Caps.wMid, Caps.wPid }; } + std::string DisplayString(); +}; + +struct PCJoystickState +{ + JoystickDev* Dev{ nullptr }; + int JoyIndex{ -1 }; + bool Captured{ false }; + unsigned int PrevAxes{ 0 }; + unsigned int PrevBtns{ 0 }; + bool JoystickToKeysActive{ false }; +}; + +struct BBCJoystickConfig +{ + bool Enabled{ false }; + int PCStick{ 0 }; + int PCAxes{ 0 }; + bool AnalogMousestick{ false }; + bool DigitalMousestick{ false }; +}; + +class JoystickHandlerDetails +{ + BeebWin* m_BeebWin; + + bool m_JoystickTimerRunning{ false }; + JoystickDev m_JoystickDevs[MAX_JOYSTICK_DEVS]; + PCJoystickState m_PCJoystickState[NUM_PC_JOYSTICKS]; + BBCJoystickConfig m_JoystickConfig[NUM_BBC_JOYSTICKS]; + int m_MenuIdSticks[NUM_BBC_JOYSTICKS]{}; + int m_MenuIdAxes[NUM_BBC_JOYSTICKS]{}; + int m_Deadband; + bool m_JoystickToKeys{ false }; + bool m_AutoloadJoystickMap{ false }; + HWND m_JoystickTarget{ nullptr }; + char m_JoystickMapPath[_MAX_PATH]{}; + std::vector m_JoystickOrder; + +public: + JoystickHandlerDetails(BeebWin* beebWin) : m_BeebWin{ beebWin } {} + + /* Accessors */ + int GetMenuIdSticks(int bbcIdx) { return m_MenuIdSticks[bbcIdx]; } + bool GetJoystickToKeys() { return m_JoystickToKeys; } + void SetJoystickToKeys(bool enabled) { m_JoystickToKeys = enabled; } + void SetJoystickTarget(HWND target) { m_JoystickTarget = target; } + + /* BeebWin access */ + HWND GetHWnd(); + HMENU GetHMenu(); + int GetXWinSize(); + int GetYWinSize(); + void CheckMenuItem(UINT id, bool checked); + void EnableMenuItem(UINT id, bool enabled); + void SetMenuItemText(UINT id, const std::string& text); + + /* Menu handling - will soon be gone */ + int MenuIdToStick(int bbcIdx, UINT menuId); + UINT StickToMenuId(int bbcIdx, int pcStick); + int MenuIdToAxes(int bbcIdx, UINT menuId); + UINT AxesToMenuId(int bbcIdx, int pcAxes); + void UpdateJoystickConfig(int bbcIdx); + bool IsPCJoystickAssigned(int pcIdx, int bbcIdx); + bool IsPCJoystickOn(int pcIdx); + void InitMenu(void); + void UpdateJoystickMenu(void); + void ProcessMenuCommand(int bbcIdx, UINT menuId); + void ProcessAxesMenuCommand(int bbcIdx, UINT menuId); + void ToggleJoystickToKeys(); + void ToggleAutoloadJoystickMap(); + + /* Initialization */ + void ScanJoysticks(void); + void ResetJoystick(void); + bool InitJoystick(bool verbose = false); + bool CaptureJoystick(int Index, bool verbose); + + /* BBC Analog */ + void SetJoystickButton(int index, bool value); + void ScaleJoystick(int index, unsigned int x, unsigned int y, + unsigned int minX, unsigned int minY, + unsigned int maxX, unsigned int maxY); + + /* Mousestick */ + void SetMousestickButton(int index, bool button); + void ScaleMousestick(unsigned int x, unsigned int y); + + /* Joystick to keyboard */ + unsigned int GetJoystickAxes(const JOYCAPS& caps, int deadband, const JOYINFOEX& joyInfoEx); + void TranslateAxes(int joyId, unsigned int axesState); + void TranslateJoystickButtons(int joyId, unsigned int buttons); + void TranslateOrSendKey(int vkey, bool keyUp); + void TranslateJoystick(int joyId); + + /* Timer handler */ + void UpdateJoysticks(void); + + /* Joystick to keyboard mapping */ + void CheckForJoystickMap(const char* path); + void ResetJoystickMap(void); + void ResetJoyMap(JoyMap* joymap); + void ResetJoyMapToDefaultUser(void); + bool ReadJoyMap(const char* filename, JoyMap* joymap); + void LoadJoystickMap(void); + void SaveJoystickMap(void); + bool WriteJoyMap(const char* filename, JoyMap* joymap); + + /* Preferences */ + bool GetNthBoolValue(Preferences& preferences, const char* format, int idx, bool& value); + bool GetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD& value); + bool GetNthStringValue(Preferences& preferences, const char* format, int idx, std::string& value); + void SetNthBoolValue(Preferences& preferences, const char* format, int idx, bool value); + void SetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD value); + void SetNthStringValue(Preferences& preferences, const char* format, int idx, const std::string& value); + void EraseNthValue(Preferences& preferences, const char* format, int idx); + void ReadPreferences(Preferences& preferences); + void WritePreferences(Preferences& preferences); + void WriteJoystickOrder(Preferences& preferences); +}; + /*****************************************************************************/ std::string JoystickOrderEntry::to_string() { @@ -82,19 +240,19 @@ std::string JoystickDev::DisplayString() } /*****************************************************************************/ -HWND JoystickHandler::GetHWnd() { return m_BeebWin->m_hWnd; } +HWND JoystickHandlerDetails::GetHWnd() { return m_BeebWin->m_hWnd; } -HMENU JoystickHandler::GetHMenu() { return m_BeebWin->m_hMenu; } +HMENU JoystickHandlerDetails::GetHMenu() { return m_BeebWin->m_hMenu; } -int JoystickHandler::GetXWinSize() { return m_BeebWin->m_XWinSize; } +int JoystickHandlerDetails::GetXWinSize() { return m_BeebWin->m_XWinSize; } -int JoystickHandler::GetYWinSize() { return m_BeebWin->m_YWinSize; } +int JoystickHandlerDetails::GetYWinSize() { return m_BeebWin->m_YWinSize; } -void JoystickHandler::CheckMenuItem(UINT id, bool checked) { m_BeebWin->CheckMenuItem(id, checked); } +void JoystickHandlerDetails::CheckMenuItem(UINT id, bool checked) { m_BeebWin->CheckMenuItem(id, checked); } -void JoystickHandler::EnableMenuItem(UINT id, bool enabled) { m_BeebWin->EnableMenuItem(id, enabled); } +void JoystickHandlerDetails::EnableMenuItem(UINT id, bool enabled) { m_BeebWin->EnableMenuItem(id, enabled); } -void JoystickHandler::SetMenuItemText(UINT id, const std::string& text) +void JoystickHandlerDetails::SetMenuItemText(UINT id, const std::string& text) { MENUITEMINFO mii{ 0 }; mii.cbSize = sizeof(mii); @@ -132,7 +290,7 @@ constexpr JoystickMenuIdsType JoystickMenuIds[NUM_BBC_JOYSTICKS] { /****************************************************************************/ -int JoystickHandler::MenuIdToStick(int bbcIdx, UINT menuId) +int JoystickHandlerDetails::MenuIdToStick(int bbcIdx, UINT menuId) { for (int pcIdx = 0; pcIdx < _countof(JoystickMenuIdsType::Joysticks); ++pcIdx) { @@ -143,7 +301,7 @@ int JoystickHandler::MenuIdToStick(int bbcIdx, UINT menuId) } /****************************************************************************/ -UINT JoystickHandler::StickToMenuId(int bbcIdx, int pcStick) +UINT JoystickHandlerDetails::StickToMenuId(int bbcIdx, int pcStick) { if (pcStick > 0 && pcStick - 1 < _countof(JoystickMenuIdsType::Joysticks)) return JoystickMenuIds[bbcIdx].Joysticks[pcStick - 1]; @@ -151,7 +309,7 @@ UINT JoystickHandler::StickToMenuId(int bbcIdx, int pcStick) } /****************************************************************************/ -int JoystickHandler::MenuIdToAxes(int bbcIdx, UINT menuId) +int JoystickHandlerDetails::MenuIdToAxes(int bbcIdx, UINT menuId) { for (int axesIdx = 0; axesIdx < _countof(JoystickMenuIdsType::Axes); ++axesIdx) { @@ -162,7 +320,7 @@ int JoystickHandler::MenuIdToAxes(int bbcIdx, UINT menuId) } /****************************************************************************/ -UINT JoystickHandler::AxesToMenuId(int bbcIdx, int pcAxes) +UINT JoystickHandlerDetails::AxesToMenuId(int bbcIdx, int pcAxes) { if (pcAxes >= 0 && pcAxes < _countof(JoystickMenuIdsType::Axes)) return JoystickMenuIds[bbcIdx].Axes[pcAxes]; @@ -171,7 +329,7 @@ UINT JoystickHandler::AxesToMenuId(int bbcIdx, int pcAxes) /****************************************************************************/ // Update joystick configuration from checked menu items -void JoystickHandler::UpdateJoystickConfig(int bbcIdx) +void JoystickHandlerDetails::UpdateJoystickConfig(int bbcIdx) { m_JoystickConfig[bbcIdx].Enabled = (m_MenuIdAxes[bbcIdx] != 0); m_JoystickConfig[bbcIdx].PCStick = MenuIdToStick(bbcIdx, m_MenuIdSticks[bbcIdx]); @@ -184,7 +342,7 @@ void JoystickHandler::UpdateJoystickConfig(int bbcIdx) /****************************************************************************/ // Check if this PC joystick is assigned to this BBC joystick -bool JoystickHandler::IsPCJoystickAssigned(int pcIdx, int bbcIdx) +bool JoystickHandlerDetails::IsPCJoystickAssigned(int pcIdx, int bbcIdx) { return m_JoystickConfig[bbcIdx].PCStick == pcIdx + 1; } @@ -192,7 +350,7 @@ bool JoystickHandler::IsPCJoystickAssigned(int pcIdx, int bbcIdx) /****************************************************************************/ // Check if PC joystick is assigned to any BBC joystick // TODO: Update m_PCStickForJoystick early and use it here -bool JoystickHandler::IsPCJoystickOn(int pcIdx) +bool JoystickHandlerDetails::IsPCJoystickOn(int pcIdx) { if (pcIdx >= _countof(JoystickMenuIdsType::Joysticks)) return false; @@ -207,7 +365,7 @@ bool JoystickHandler::IsPCJoystickOn(int pcIdx) } /*****************************************************************************/ -void JoystickHandler::InitMenu() +void JoystickHandlerDetails::InitMenu() { for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) { @@ -233,7 +391,7 @@ void JoystickHandler::InitMenu() } /****************************************************************************/ -void JoystickHandler::UpdateJoystickMenu() +void JoystickHandlerDetails::UpdateJoystickMenu() { bool EnableInit = false; @@ -281,7 +439,7 @@ void JoystickHandler::UpdateJoystickMenu() } /****************************************************************************/ -void JoystickHandler::ProcessMenuCommand(int bbcIdx, UINT MenuId) +void JoystickHandlerDetails::ProcessMenuCommand(int bbcIdx, UINT MenuId) { /* Disable current selection */ if (m_MenuIdSticks[bbcIdx] != 0) @@ -318,7 +476,7 @@ void JoystickHandler::ProcessMenuCommand(int bbcIdx, UINT MenuId) } /****************************************************************************/ -void JoystickHandler::ProcessAxesMenuCommand(int bbcIdx, UINT MenuId) +void JoystickHandlerDetails::ProcessAxesMenuCommand(int bbcIdx, UINT MenuId) { CheckMenuItem(m_MenuIdAxes[bbcIdx], false); m_MenuIdAxes[bbcIdx] = MenuId; @@ -327,7 +485,7 @@ void JoystickHandler::ProcessAxesMenuCommand(int bbcIdx, UINT MenuId) } /****************************************************************************/ -void JoystickHandler::ToggleJoystickToKeys(void) +void JoystickHandlerDetails::ToggleJoystickToKeys(void) { m_JoystickToKeys = !m_JoystickToKeys; InitJoystick(false); @@ -335,14 +493,14 @@ void JoystickHandler::ToggleJoystickToKeys(void) } /****************************************************************************/ -void JoystickHandler::ToggleAutoloadJoystickMap(void) +void JoystickHandlerDetails::ToggleAutoloadJoystickMap(void) { m_AutoloadJoystickMap = !m_AutoloadJoystickMap; CheckMenuItem(IDM_AUTOLOADJOYMAP, m_AutoloadJoystickMap); } /****************************************************************************/ -void JoystickHandler::ResetJoystick(void) +void JoystickHandlerDetails::ResetJoystick(void) { for (int pcIdx = 0; pcIdx < NUM_PC_JOYSTICKS; ++pcIdx) { @@ -353,7 +511,7 @@ void JoystickHandler::ResetJoystick(void) } /****************************************************************************/ -void JoystickHandler::ScanJoysticks(void) +void JoystickHandlerDetails::ScanJoysticks(void) { JOYINFOEX joyInfoEx; std::map> listById; @@ -464,7 +622,7 @@ void JoystickHandler::ScanJoysticks(void) } /****************************************************************************/ -bool JoystickHandler::InitJoystick(bool verbose) +bool JoystickHandlerDetails::InitJoystick(bool verbose) { bool Success = true; @@ -501,7 +659,7 @@ bool JoystickHandler::InitJoystick(bool verbose) } /****************************************************************************/ -bool JoystickHandler::CaptureJoystick(int Index, bool verbose) +bool JoystickHandlerDetails::CaptureJoystick(int Index, bool verbose) { bool success = false; @@ -522,13 +680,13 @@ bool JoystickHandler::CaptureJoystick(int Index, bool verbose) } /****************************************************************************/ -void JoystickHandler::SetJoystickButton(int index, bool value) +void JoystickHandlerDetails::SetJoystickButton(int index, bool value) { JoystickButton[index] = value; } /****************************************************************************/ -void JoystickHandler::ScaleJoystick(int index, unsigned int x, unsigned int y, +void JoystickHandlerDetails::ScaleJoystick(int index, unsigned int x, unsigned int y, unsigned int minX, unsigned int minY, unsigned int maxX, unsigned int maxY) { /* Scale and reverse the readings */ @@ -539,7 +697,7 @@ void JoystickHandler::ScaleJoystick(int index, unsigned int x, unsigned int y, } /****************************************************************************/ -void JoystickHandler::SetMousestickButton(int index, bool value) +void JoystickHandlerDetails::SetMousestickButton(int index, bool value) { if (index == 0) { @@ -566,7 +724,7 @@ void JoystickHandler::SetMousestickButton(int index, bool value) } /****************************************************************************/ -void JoystickHandler::ScaleMousestick(unsigned int x, unsigned int y) +void JoystickHandlerDetails::ScaleMousestick(unsigned int x, unsigned int y) { for (int index = 0; index < 2; ++index) { @@ -612,7 +770,7 @@ void JoystickHandler::ScaleMousestick(unsigned int x, unsigned int y) } /****************************************************************************/ -unsigned int JoystickHandler::GetJoystickAxes(const JOYCAPS& caps, int deadband, const JOYINFOEX& joyInfoEx) +unsigned int JoystickHandlerDetails::GetJoystickAxes(const JOYCAPS& caps, int deadband, const JOYINFOEX& joyInfoEx) { using Info = JOYINFOEX; using Caps = JOYCAPS; @@ -661,7 +819,7 @@ unsigned int JoystickHandler::GetJoystickAxes(const JOYCAPS& caps, int deadband, /****************************************************************************/ // Translate joystick position changes to key up or down message -void JoystickHandler::TranslateAxes(int joyId, unsigned int axesState) +void JoystickHandlerDetails::TranslateAxes(int joyId, unsigned int axesState) { int vkeys = BEEB_VKEY_JOY_START + joyId * (JOYSTICK_MAX_AXES + JOYSTICK_MAX_BTNS); unsigned int& prevAxes = m_PCJoystickState[joyId].PrevAxes; @@ -684,7 +842,7 @@ void JoystickHandler::TranslateAxes(int joyId, unsigned int axesState) } /****************************************************************************/ -void JoystickHandler::TranslateJoystickButtons(int joyId, unsigned int buttons) +void JoystickHandlerDetails::TranslateJoystickButtons(int joyId, unsigned int buttons) { const int BUTTON_COUNT = 32; @@ -711,7 +869,7 @@ void JoystickHandler::TranslateJoystickButtons(int joyId, unsigned int buttons) } /****************************************************************************/ -void JoystickHandler::TranslateOrSendKey(int vkey, bool keyUp) +void JoystickHandlerDetails::TranslateOrSendKey(int vkey, bool keyUp) { if (m_JoystickTarget == nullptr) { @@ -727,7 +885,7 @@ void JoystickHandler::TranslateOrSendKey(int vkey, bool keyUp) } /****************************************************************************/ -void JoystickHandler::TranslateJoystick(int joyId) +void JoystickHandlerDetails::TranslateJoystick(int joyId) { static const JOYCAPS dummyJoyCaps = { 0, 0, "", @@ -827,7 +985,7 @@ void JoystickHandler::TranslateJoystick(int joyId) } /*****************************************************************************/ -void JoystickHandler::UpdateJoysticks() +void JoystickHandlerDetails::UpdateJoysticks() { for (int idx = 0; idx < NUM_PC_JOYSTICKS; ++idx) { @@ -838,7 +996,7 @@ void JoystickHandler::UpdateJoysticks() /*****************************************************************************/ // Look for file specific joystick map -void JoystickHandler::CheckForJoystickMap(const char *path) +void JoystickHandlerDetails::CheckForJoystickMap(const char *path) { char file[_MAX_PATH]; char drive[_MAX_DRIVE]; @@ -868,7 +1026,7 @@ void JoystickHandler::CheckForJoystickMap(const char *path) } /****************************************************************************/ -void JoystickHandler::ResetJoystickMap() +void JoystickHandlerDetails::ResetJoystickMap() { if (MessageBox(GetHWnd(), "Clear joystick to keyboard mapping table?", WindowTitle, MB_YESNO | MB_ICONQUESTION) != IDYES) return; @@ -877,7 +1035,7 @@ void JoystickHandler::ResetJoystickMap() } /****************************************************************************/ -void JoystickHandler::ResetJoyMap(JoyMap* joymap) +void JoystickHandlerDetails::ResetJoyMap(JoyMap* joymap) { // Initialize all input to unassigned for (auto& mapping : *joymap) @@ -890,7 +1048,7 @@ void JoystickHandler::ResetJoyMap(JoyMap* joymap) } /****************************************************************************/ -void JoystickHandler::ResetJoyMapToDefaultUser(void) +void JoystickHandlerDetails::ResetJoyMapToDefaultUser(void) { char keymap[_MAX_PATH]; strcpy(keymap, "DefaultUser.jmap"); @@ -913,7 +1071,7 @@ static void makeupper(char* str) } /****************************************************************************/ -bool JoystickHandler::ReadJoyMap(const char *filename, JoyMap *joymap) +bool JoystickHandlerDetails::ReadJoyMap(const char *filename, JoyMap *joymap) { bool success = true; FILE *infile = m_BeebWin->OpenReadFile(filename, "key map", JOYMAP_TOKEN "\n"); @@ -1033,7 +1191,7 @@ bool JoystickHandler::ReadJoyMap(const char *filename, JoyMap *joymap) } /****************************************************************************/ -void JoystickHandler::LoadJoystickMap() +void JoystickHandlerDetails::LoadJoystickMap() { char DefaultPath[_MAX_PATH]; char FileName[_MAX_PATH]; @@ -1070,7 +1228,7 @@ void JoystickHandler::LoadJoystickMap() } /****************************************************************************/ -bool JoystickHandler::WriteJoyMap(const char *filename, JoyMap *joymap) +bool JoystickHandlerDetails::WriteJoyMap(const char *filename, JoyMap *joymap) { FILE* outfile = m_BeebWin->OpenWriteFile(filename, "joystick map"); @@ -1132,7 +1290,7 @@ static bool hasFileExt(const char* fileName, const char* fileExt) } /****************************************************************************/ -void JoystickHandler::SaveJoystickMap() +void JoystickHandlerDetails::SaveJoystickMap() { char DefaultPath[_MAX_PATH]; // Add space for .jmap exstension @@ -1188,7 +1346,7 @@ static const char *CFG_OPTIONS_STICKS_DEADBAND = "SticksToKeysDeadBand"; static const char *CFG_OPTIONS_JOYSTICK_ORDER = "JoystickOrder%d"; /****************************************************************************/ -bool JoystickHandler::GetNthBoolValue(Preferences& preferences, const char* format, int idx, bool& value) +bool JoystickHandlerDetails::GetNthBoolValue(Preferences& preferences, const char* format, int idx, bool& value) { char option_str[256]; sprintf_s(option_str, format, idx); @@ -1196,7 +1354,7 @@ bool JoystickHandler::GetNthBoolValue(Preferences& preferences, const char* form } /****************************************************************************/ -bool JoystickHandler::GetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD& value) +bool JoystickHandlerDetails::GetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD& value) { char option_str[256]; sprintf_s(option_str, format, idx); @@ -1204,7 +1362,7 @@ bool JoystickHandler::GetNthDWORDValue(Preferences& preferences, const char* for } /****************************************************************************/ -bool JoystickHandler::GetNthStringValue(Preferences& preferences, const char* format, int idx, std::string& value) +bool JoystickHandlerDetails::GetNthStringValue(Preferences& preferences, const char* format, int idx, std::string& value) { char option_str[256]; sprintf_s(option_str, format, idx); @@ -1212,7 +1370,7 @@ bool JoystickHandler::GetNthStringValue(Preferences& preferences, const char* fo } /****************************************************************************/ -void JoystickHandler::SetNthBoolValue(Preferences& preferences, const char* format, int idx, bool value) +void JoystickHandlerDetails::SetNthBoolValue(Preferences& preferences, const char* format, int idx, bool value) { char option_str[256]; sprintf_s(option_str, format, idx); @@ -1220,7 +1378,7 @@ void JoystickHandler::SetNthBoolValue(Preferences& preferences, const char* form } /****************************************************************************/ -void JoystickHandler::SetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD value) +void JoystickHandlerDetails::SetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD value) { char option_str[256]; sprintf_s(option_str, format, idx); @@ -1228,7 +1386,7 @@ void JoystickHandler::SetNthDWORDValue(Preferences& preferences, const char* for } /****************************************************************************/ -void JoystickHandler::SetNthStringValue(Preferences& preferences, const char* format, int idx, const std::string& value) +void JoystickHandlerDetails::SetNthStringValue(Preferences& preferences, const char* format, int idx, const std::string& value) { char option_str[256]; sprintf_s(option_str, format, idx); @@ -1236,7 +1394,7 @@ void JoystickHandler::SetNthStringValue(Preferences& preferences, const char* fo } /****************************************************************************/ -void JoystickHandler::EraseNthValue(Preferences& preferences, const char* format, int idx) +void JoystickHandlerDetails::EraseNthValue(Preferences& preferences, const char* format, int idx) { char option_str[256]; sprintf_s(option_str, format, idx); @@ -1244,7 +1402,7 @@ void JoystickHandler::EraseNthValue(Preferences& preferences, const char* format } /****************************************************************************/ -void JoystickHandler::ReadPreferences(Preferences& preferences) +void JoystickHandlerDetails::ReadPreferences(Preferences& preferences) { DWORD dword; bool flag; @@ -1332,7 +1490,7 @@ void JoystickHandler::ReadPreferences(Preferences& preferences) } /****************************************************************************/ -void JoystickHandler::WritePreferences(Preferences& preferences) +void JoystickHandlerDetails::WritePreferences(Preferences& preferences) { for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) { @@ -1357,7 +1515,7 @@ void JoystickHandler::WritePreferences(Preferences& preferences) } /****************************************************************************/ -void JoystickHandler::WriteJoystickOrder(Preferences& preferences) +void JoystickHandlerDetails::WriteJoystickOrder(Preferences& preferences) { for (UINT idx = 0; idx < MAX_JOYSTICK_ORDER; ++idx) { @@ -1372,3 +1530,50 @@ void JoystickHandler::WriteJoystickOrder(Preferences& preferences) } } } + +/****************************************************************************/ +JoystickHandler::JoystickHandler(BeebWin* beebWin) : m_Details{ std::make_unique(beebWin) } {} + +JoystickHandler::~JoystickHandler() {} + +int JoystickHandler::GetMenuIdSticks(int bbcIdx) { return m_Details->GetMenuIdSticks(bbcIdx); } + +bool JoystickHandler::GetJoystickToKeys() { return m_Details->GetJoystickToKeys(); } + +void JoystickHandler::SetJoystickToKeys(bool enabled) { m_Details->SetJoystickToKeys(enabled); } + +void JoystickHandler::SetJoystickTarget(HWND target) { m_Details->SetJoystickTarget(target); } + +void JoystickHandler::InitMenu(void) { m_Details->InitMenu(); } + +void JoystickHandler::ProcessMenuCommand(int bbcIdx, UINT menuId) { m_Details->ProcessMenuCommand(bbcIdx, menuId); } + +void JoystickHandler::ProcessAxesMenuCommand(int bbcIdx, UINT menuId) { m_Details->ProcessAxesMenuCommand(bbcIdx, menuId); } + +void JoystickHandler::ToggleJoystickToKeys() { m_Details->ToggleJoystickToKeys(); } + +void JoystickHandler::ToggleAutoloadJoystickMap() { m_Details->ToggleAutoloadJoystickMap(); } + +bool JoystickHandler::InitJoystick(bool verbose) { return m_Details->InitJoystick(verbose); } + +void JoystickHandler::SetMousestickButton(int index, bool button) { m_Details->SetMousestickButton(index, button); } + +void JoystickHandler::ScaleMousestick(unsigned int x, unsigned int y) { m_Details->ScaleMousestick(x, y); } + +void JoystickHandler::UpdateJoysticks(void) { m_Details->UpdateJoysticks(); } + +void JoystickHandler::CheckForJoystickMap(const char* path) { m_Details->CheckForJoystickMap(path); } + +void JoystickHandler::ResetJoystickMap(void) { m_Details->ResetJoystickMap(); } + +void JoystickHandler::ResetJoyMapToDefaultUser(void) { m_Details->ResetJoyMapToDefaultUser(); } + +void JoystickHandler::LoadJoystickMap(void) { m_Details->LoadJoystickMap(); } + +void JoystickHandler::SaveJoystickMap(void) { m_Details->SaveJoystickMap(); } + +void JoystickHandler::ReadPreferences(Preferences& preferences) { m_Details->ReadPreferences(preferences); } + +void JoystickHandler::WritePreferences(Preferences& preferences) { m_Details->WritePreferences(preferences); } + +void JoystickHandler::WriteJoystickOrder(Preferences& preferences) { m_Details->WriteJoystickOrder(preferences); } diff --git a/Src/JoystickHandler.h b/Src/JoystickHandler.h index a5d5977f..13e9c8b5 100644 --- a/Src/JoystickHandler.h +++ b/Src/JoystickHandler.h @@ -28,11 +28,7 @@ Boston, MA 02110-1301, USA. #ifndef BEEBWINJOYSTICK_H #define BEEBWINJOYSTICK_H -#include "atodconv.h" -#include "beebemrc.h" -#include "keymapping.h" - -#include +#include /* Max number of joysticks in joystickapi */ #define MAX_JOYSTICK_DEVS 16 @@ -69,158 +65,48 @@ Boston, MA 02110-1301, USA. #define BEEB_VKEY_COUNT BEEB_VKEY_JOY_END -typedef KeyPair JoyMap[BEEB_VKEY_JOY_COUNT]; - class BeebWin; - -struct JoystickId : std::pair -{ - using std::pair::pair; - - // Manufacturer ID aka Vendor ID - int& mId() { return first; } - // Product ID - int& pId() { return second; } -}; - -struct JoystickOrderEntry : JoystickId -{ - std::string Name{}; - int JoyIndex{ -1 }; - - JoystickOrderEntry() = default; - JoystickOrderEntry(JoystickId id, const std::string& name, int joyIndex) : - JoystickId(id), Name(name), JoyIndex(joyIndex) {} - JoystickOrderEntry(int mid, int pid, const std::string& name) : - JoystickId(mid, pid), Name(name) {} - - std::string to_string(); - bool from_string(const std::string&); -}; - -struct JoystickDev -{ - JOYCAPS Caps{}; - int Instance{ 0 }; - int Order{ -1 }; - int JoyIndex{ -1 }; - bool Configured{ false }; - bool Present{ false }; - - JoystickId Id() { return JoystickId{ Caps.wMid, Caps.wPid }; } - std::string DisplayString(); -}; - -struct PCJoystickState -{ - JoystickDev* Dev{ nullptr }; - int JoyIndex{ -1 }; - bool Captured{ false }; - unsigned int PrevAxes{ 0 }; - unsigned int PrevBtns{ 0 }; - bool JoystickToKeysActive{ false }; -}; - -struct BBCJoystickConfig -{ - bool Enabled{ false }; - int PCStick{ 0 }; - int PCAxes{ 0 }; - bool AnalogMousestick{ false }; - bool DigitalMousestick{ false }; -}; +class JoystickHandlerDetails; class JoystickHandler { - BeebWin* m_BeebWin; - bool m_JoystickTimerRunning{ false }; - JoystickDev m_JoystickDevs[MAX_JOYSTICK_DEVS]; - PCJoystickState m_PCJoystickState[NUM_PC_JOYSTICKS]; - BBCJoystickConfig m_JoystickConfig[NUM_BBC_JOYSTICKS]; - int m_MenuIdSticks[NUM_BBC_JOYSTICKS]{}; - int m_MenuIdAxes[NUM_BBC_JOYSTICKS]{}; - int m_Deadband; - bool m_JoystickToKeys{ false }; - bool m_AutoloadJoystickMap{ false }; - HWND m_JoystickTarget{ nullptr }; - char m_JoystickMapPath[_MAX_PATH]{}; - std::vector m_JoystickOrder; + std::unique_ptr m_Details; public: - JoystickHandler(BeebWin* beebWin) : m_BeebWin{ beebWin } {} + JoystickHandler(BeebWin* beebWin); + ~JoystickHandler(); /* Accessors */ - int GetMenuIdSticks(int bbcIdx) { return m_MenuIdSticks[bbcIdx]; } - bool GetJoystickToKeys() { return m_JoystickToKeys; } - void SetJoystickToKeys(bool enabled) { m_JoystickToKeys = enabled; } - void SetJoystickTarget(HWND target) { m_JoystickTarget = target; } - - /* BeebWin access */ - HWND GetHWnd(); - HMENU GetHMenu(); - int GetXWinSize(); - int GetYWinSize(); - void CheckMenuItem(UINT id, bool checked); - void EnableMenuItem(UINT id, bool enabled); - void SetMenuItemText(UINT id, const std::string& text); + int GetMenuIdSticks(int bbcIdx); + bool GetJoystickToKeys(); + void SetJoystickToKeys(bool enabled); + void SetJoystickTarget(HWND target); /* Menu handling - will soon be gone */ - int MenuIdToStick(int bbcIdx, UINT menuId); - UINT StickToMenuId(int bbcIdx, int pcStick); - int MenuIdToAxes(int bbcIdx, UINT menuId); - UINT AxesToMenuId(int bbcIdx, int pcAxes); - void UpdateJoystickConfig(int bbcIdx); - bool IsPCJoystickAssigned(int pcIdx, int bbcIdx); - bool IsPCJoystickOn(int pcIdx); void InitMenu(void); - void UpdateJoystickMenu(void); void ProcessMenuCommand(int bbcIdx, UINT menuId); void ProcessAxesMenuCommand(int bbcIdx, UINT menuId); void ToggleJoystickToKeys(); void ToggleAutoloadJoystickMap(); /* Initialization */ - void ScanJoysticks(void); - void ResetJoystick(void); bool InitJoystick(bool verbose = false); - bool CaptureJoystick(int Index, bool verbose); - - /* BBC Analog */ - void SetJoystickButton(int index, bool value); - void ScaleJoystick(int index, unsigned int x, unsigned int y, - unsigned int minX, unsigned int minY, - unsigned int maxX, unsigned int maxY); /* Mousestick */ void SetMousestickButton(int index, bool button); void ScaleMousestick(unsigned int x, unsigned int y); - /* Joystick to keyboard */ - unsigned int GetJoystickAxes(const JOYCAPS& caps, int deadband, const JOYINFOEX& joyInfoEx); - void TranslateAxes(int joyId, unsigned int axesState); - void TranslateJoystickButtons(int joyId, unsigned int buttons); - void TranslateOrSendKey(int vkey, bool keyUp); - void TranslateJoystick(int joyId); + /* Timer handler */ void UpdateJoysticks(void); /* Joystick to keyboard mapping */ - void CheckForJoystickMap(const char *path); + void CheckForJoystickMap(const char* path); void ResetJoystickMap(void); - void ResetJoyMap(JoyMap* joymap); void ResetJoyMapToDefaultUser(void); - bool ReadJoyMap(const char *filename, JoyMap *joymap); void LoadJoystickMap(void); void SaveJoystickMap(void); - bool WriteJoyMap(const char *filename, JoyMap *joymap); /* Preferences */ - bool GetNthBoolValue(Preferences& preferences, const char* format, int idx, bool& value); - bool GetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD& value); - bool GetNthStringValue(Preferences& preferences, const char* format, int idx, std::string& value); - void SetNthBoolValue(Preferences& preferences, const char* format, int idx, bool value); - void SetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD value); - void SetNthStringValue(Preferences& preferences, const char* format, int idx, const std::string& value); - void EraseNthValue(Preferences& preferences, const char* format, int idx); void ReadPreferences(Preferences& preferences); void WritePreferences(Preferences& preferences); void WriteJoystickOrder(Preferences& preferences); diff --git a/Src/beebwin.h b/Src/beebwin.h index 1225ed0e..4dc1d548 100644 --- a/Src/beebwin.h +++ b/Src/beebwin.h @@ -43,7 +43,6 @@ Boston, MA 02110-1301, USA. #include "port.h" #include "preferences.h" #include "video.h" -#include "keymapping.h" #include "JoystickHandler.h" /* Used in message boxes */ @@ -53,6 +52,15 @@ Boston, MA 02110-1301, USA. #define CFG_KEYBOARD_LAYOUT "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout" #define CFG_SCANCODE_MAP "Scancode Map" +struct KeyMapping { + int row; // Beeb row + int col; // Beeb col + bool shift; // Beeb shift state +}; + +typedef KeyMapping KeyPair[2]; +typedef KeyPair KeyMap[256]; // Indices are: [Virt key][shift state] +typedef KeyPair JoyMap[BEEB_VKEY_JOY_COUNT]; #define UNASSIGNED_ROW -9 diff --git a/Src/keymapping.h b/Src/keymapping.h deleted file mode 100644 index 0b9b26a4..00000000 --- a/Src/keymapping.h +++ /dev/null @@ -1,40 +0,0 @@ -/**************************************************************** -BeebEm - BBC Micro and Master 128 Emulator -Copyright (C) 1994 Nigel Magnay -Copyright (C) 1997 Mike Wyatt -Copyright (C) 1998 Robert Schmidt -Copyright (C) 2001 Richard Gellman -Copyright (C) 2004 Ken Lowe -Copyright (C) 2004 Rob O'Donnell -Copyright (C) 2005 Jon Welch -Copyright (C) 2021 Chris Needham, Tadeusz Kijkowski - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -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. - -You should have received a copy of the GNU General Public -License along with this program; if not, write to the Free -Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -Boston, MA 02110-1301, USA. -****************************************************************/ - -#ifndef BEEBEM_KEYMAPPING_H -#define BEEBEM_KEYMAPPING_H - -struct KeyMapping { - int row; // Beeb row - int col; // Beeb col - bool shift; // Beeb shift state -}; - -typedef KeyMapping KeyPair[2]; -typedef KeyPair KeyMap[256]; // Indices are: [Virt key][shift state] - -#endif From e8db25e3ab79f601982924fea88db6e236a3fa74 Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Wed, 24 Feb 2021 19:47:48 +0100 Subject: [PATCH 27/38] Added Reset Mapping to dialog and fixed Shift keys colours --- Src/BeebEm.rc | 3 ++- Src/JoystickHandler.cpp | 8 ++++---- Src/beebemrc.h | 3 ++- Src/userkybd.cpp | 45 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/Src/BeebEm.rc b/Src/BeebEm.rc index f4fae1f5..fc72239c 100644 --- a/Src/BeebEm.rc +++ b/Src/BeebEm.rc @@ -108,7 +108,8 @@ BEGIN CONTROL "_ £",IDK_UNDERSCORE,"Button",BS_OWNERDRAW | WS_TABSTOP,269,33,17,14 CONTROL "^ ~",IDK_CARET,"Button",BS_OWNERDRAW | WS_TABSTOP,243,19,17,14 CONTROL "SFT LK",IDK_SHIFT_LOCK,"Button",BS_OWNERDRAW | WS_TABSTOP,9,62,29,14 - CONTROL "Unassign",IDK_UNASSIGN,"Button",BS_OWNERDRAW | WS_TABSTOP,9,101,36,14 + CONTROL "Unassign",IDK_UNASSIGN,"Button",BS_OWNERDRAW | WS_TABSTOP,9,86,60,14 + CONTROL "Reset Mapping",IDK_RESET_MAPPING,"Button",WS_TABSTOP,9,101,60,14 END IDD_KEYBOARD_LINKS DIALOG 0, 0, 160, 66 diff --git a/Src/JoystickHandler.cpp b/Src/JoystickHandler.cpp index 10e14c02..415fc3e3 100644 --- a/Src/JoystickHandler.cpp +++ b/Src/JoystickHandler.cpp @@ -597,15 +597,15 @@ void JoystickHandlerDetails::ScanJoysticks(void) // If joystick order list is too long, remove some unconnected entries // Remove entries from the beginning or from the end, or some other order? int to_remove = newJoysticks.size() + m_JoystickOrder.size() - MAX_JOYSTICK_ORDER; - int idx = m_JoystickOrder.size() - 1; - while (to_remove > 0 && idx >= 0) + unsigned int idx = 0; + while (to_remove > 0 && idx < m_JoystickOrder.size()) { if (m_JoystickOrder[idx].JoyIndex == -1) { m_JoystickOrder.erase(m_JoystickOrder.begin() + idx); --to_remove; } - --idx; + ++idx; } // Add new joystick at the end of order list @@ -668,7 +668,7 @@ bool JoystickHandlerDetails::CaptureJoystick(int Index, bool verbose) m_PCJoystickState[Index].Captured = true; success = true; } - else if (verbose) + else if (verbose && (IsPCJoystickOn(Index) || m_JoystickToKeys && Index == 0)) { char str[100]; sprintf(str, "Failed to initialise joystick %d", Index + 1); diff --git a/Src/beebemrc.h b/Src/beebemrc.h index 03d37980..05b9a430 100644 --- a/Src/beebemrc.h +++ b/Src/beebemrc.h @@ -188,6 +188,7 @@ Boston, MA 02110-1301, USA. #define IDC_DEBUGTELETEXTBRK 1089 #define IDC_ASSIGNED_KEYS 1090 #define IDC_ASSIGNED_KEYS_LBL 1091 +#define IDK_RESET_MAPPING 1092 #define IDM_ABOUT 40001 #define IDM_DISC 40002 #define IDM_LOADDISC0 40002 @@ -462,7 +463,7 @@ Boston, MA 02110-1301, USA. #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 118 #define _APS_NEXT_COMMAND_VALUE 40319 -#define _APS_NEXT_CONTROL_VALUE 1092 +#define _APS_NEXT_CONTROL_VALUE 1093 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/Src/userkybd.cpp b/Src/userkybd.cpp index 89dfa6ff..6353092b 100644 --- a/Src/userkybd.cpp +++ b/Src/userkybd.cpp @@ -47,6 +47,7 @@ static COLORREF GetKeyColour(UINT ctrlID); static std::string GetKeysUsed(); static void FillAssignedKeysCount(); static void UpdateAssignedKeysCount(int row, int col, int change, bool redrawColour = false); +static void RedrawAllKeys(); static int GetBBCKeyIndex(const BBCKey* key); // Colour used to highlight the selected key. @@ -392,6 +393,10 @@ static INT_PTR CALLBACK UserKeyboardDlgProc(HWND hwnd, { SetWindowText(hwnd, "Joystick To Keyboard Mapping"); } + else + { + ShowWindow(GetDlgItem(hwnd, IDK_RESET_MAPPING), SW_HIDE); + } return FALSE; case WM_COMMAND: @@ -406,6 +411,12 @@ static INT_PTR CALLBACK UserKeyboardDlgProc(HWND hwnd, PostMessage(hwndMain, WM_USER_KEYBOARD_DIALOG_CLOSED, 0, 0); break; + case IDK_RESET_MAPPING: + SendMessage(hwndMain, WM_COMMAND, IDM_RESETJOYMAP, 0); + FillAssignedKeysCount(); + RedrawAllKeys(); + break; + default: SelectKeyMapping(hwnd, (UINT)wParam, (HWND)lParam); break; @@ -454,6 +465,17 @@ static INT_PTR CALLBACK UserKeyboardDlgProc(HWND hwnd, // Show the key as not depressed, i.e., normal. SetKeyColour(GetKeyColour(ctrlID)); + + if (ctrlID == IDK_SHIFT_L) + { + HWND rShiftCtrl = GetDlgItem(hwndUserKeyboard, IDK_SHIFT_R); + SetKeyColour(GetKeyColour(IDK_SHIFT_R), rShiftCtrl); + } + else if (ctrlID == IDK_SHIFT_R) + { + HWND lShiftCtrl = GetDlgItem(hwndUserKeyboard, IDK_SHIFT_L); + SetKeyColour(GetKeyColour(IDK_SHIFT_L), lShiftCtrl); + } } return TRUE; @@ -718,5 +740,28 @@ static void UpdateAssignedKeysCount(int row, int col, int change, bool redrawCol HWND keyCtrl = GetDlgItem(hwndUserKeyboard, key->ctrlId); SetKeyColour(GetKeyColour(key->ctrlId), keyCtrl); } + /* If it's shift, update the other one */ + if (key->column == 0 && key->row == 0) + { + key = GetBBCKeyByResId((IDK_SHIFT_L + IDK_SHIFT_R) - key->ctrlId); + index = GetBBCKeyIndex(key); + assignedKeysCount[index] += change; + if (redrawColour && key->ctrlId != selectedCtrlID) + { + HWND keyCtrl = GetDlgItem(hwndUserKeyboard, key->ctrlId); + SetKeyColour(GetKeyColour(key->ctrlId), keyCtrl); + } + } } } + +/****************************************************************************/ + +static void RedrawAllKeys() +{ + for (auto& key : BBCKeys) + { + HWND keyCtrl = GetDlgItem(hwndUserKeyboard, key.ctrlId); + SetKeyColour(GetKeyColour(key.ctrlId), keyCtrl); + } +} \ No newline at end of file From 8189cf581e741234ad1f4ff7e11825e8b2929a57 Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Wed, 24 Feb 2021 20:44:05 +0100 Subject: [PATCH 28/38] Added JoystickGain parameter (no UI for it) --- Src/JoystickHandler.cpp | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/Src/JoystickHandler.cpp b/Src/JoystickHandler.cpp index 415fc3e3..4a9039a8 100644 --- a/Src/JoystickHandler.cpp +++ b/Src/JoystickHandler.cpp @@ -36,6 +36,11 @@ Boston, MA 02110-1301, USA. #include #include #include +#include + +// Unhide std::min, std::max +#undef min +#undef max #define JOYMAP_TOKEN "*** BeebEm Joystick Map ***" @@ -109,6 +114,7 @@ class JoystickHandlerDetails int m_MenuIdSticks[NUM_BBC_JOYSTICKS]{}; int m_MenuIdAxes[NUM_BBC_JOYSTICKS]{}; int m_Deadband; + double m_Gain{ 1.0 }; bool m_JoystickToKeys{ false }; bool m_AutoloadJoystickMap{ false }; HWND m_JoystickTarget{ nullptr }; @@ -689,11 +695,16 @@ void JoystickHandlerDetails::SetJoystickButton(int index, bool value) void JoystickHandlerDetails::ScaleJoystick(int index, unsigned int x, unsigned int y, unsigned int minX, unsigned int minY, unsigned int maxX, unsigned int maxY) { - /* Scale and reverse the readings */ - JoystickX[index] = (int)((double)(maxX - x) * 65535.0 / - (double)(maxX - minX)); - JoystickY[index] = (int)((double)(maxY - y) * 65535.0 / - (double)(maxY - minY)); + /* Gain and reverse the readings */ + double sx = 0.5 + ((double)(maxX - x) / (double)(maxX - minX) - 0.5) * m_Gain; + double sy = 0.5 + ((double)(maxY - y) / (double)(maxY - minY) - 0.5) * m_Gain; + + /* Scale to 0-65535 range */ + sx = std::max(0.0, std::min(65535.0, sx * 65535.0)); + sy = std::max(0.0, std::min(65535.0, sy * 65535.0)); + + JoystickX[index] = (int)sx; + JoystickY[index] = (int)sy; } /****************************************************************************/ @@ -1338,13 +1349,13 @@ static const char *CFG_OPTIONS_STICK_PCSTICK = "Stick%dPCStick"; static const char *CFG_OPTIONS_STICK_PCAXES = "Stick%dPCAxes"; static const char *CFG_OPTIONS_STICK_ANALOG = "Stick%dAnalogMousepad"; static const char *CFG_OPTIONS_STICK_DIGITAL = "Stick%dDigitalMousepad"; +static const char* CFG_OPTIONS_JOYSTICK_GAIN = "JoystickGain"; +static const char* CFG_OPTIONS_JOYSTICK_ORDER = "JoystickOrder%d"; static const char *CFG_OPTIONS_STICKS_TO_KEYS = "SticksToKeys"; static const char *CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP = "AutoloadJoystickMap"; static const char *CFG_OPTIONS_STICKS_DEADBAND = "SticksToKeysDeadBand"; -static const char *CFG_OPTIONS_JOYSTICK_ORDER = "JoystickOrder%d"; - /****************************************************************************/ bool JoystickHandlerDetails::GetNthBoolValue(Preferences& preferences, const char* format, int idx, bool& value) { @@ -1475,6 +1486,11 @@ void JoystickHandlerDetails::ReadPreferences(Preferences& preferences) else m_Deadband = DEFAULT_JOY_DEADBAND; + if (preferences.GetDWORDValue(CFG_OPTIONS_JOYSTICK_GAIN, dword)) + m_Gain = static_cast(dword) / 0x10000; + else + m_Gain = 1.0; + m_JoystickOrder.clear(); for (int idx = 0; idx < MAX_JOYSTICK_ORDER; ++idx) { @@ -1504,6 +1520,7 @@ void JoystickHandlerDetails::WritePreferences(Preferences& preferences) preferences.SetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys); preferences.SetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, m_AutoloadJoystickMap); preferences.SetDWORDValue(CFG_OPTIONS_STICKS_DEADBAND, m_Deadband); + preferences.SetDWORDValue(CFG_OPTIONS_JOYSTICK_GAIN, static_cast(m_Gain * 0x10000)); /* Remove obsolete values */ preferences.EraseValue(CFG_OPTIONS_STICKS); From 6bec0d43ea452398991d938e80e4ca840a556299 Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Fri, 26 Feb 2021 23:50:27 +0100 Subject: [PATCH 29/38] Moved most joystick handling back to BeebWin class --- Src/JoystickHandler.cpp | 1050 ++++++++++++++++----------------------- Src/JoystickHandler.h | 144 +++--- Src/SelectKeyDialog.cpp | 4 +- Src/beebwin.cpp | 73 +-- Src/beebwin.h | 112 ++++- Src/beebwinio.cpp | 2 +- Src/beebwinprefs.cpp | 6 +- 7 files changed, 653 insertions(+), 738 deletions(-) diff --git a/Src/JoystickHandler.cpp b/Src/JoystickHandler.cpp index 4a9039a8..bdc5c5cf 100644 --- a/Src/JoystickHandler.cpp +++ b/Src/JoystickHandler.cpp @@ -31,7 +31,7 @@ Boston, MA 02110-1301, USA. #include "sysvia.h" #include "filedialog.h" #include "SelectKeyDialog.h" -#include "atodconv.h" +#include "JoystickHandler.h" #include #include @@ -44,163 +44,46 @@ Boston, MA 02110-1301, USA. #define JOYMAP_TOKEN "*** BeebEm Joystick Map ***" -#define DEFAULT_JOY_DEADBAND 4096 - -struct JoystickId : std::pair -{ - using std::pair::pair; - - // Manufacturer ID aka Vendor ID - int& mId() { return first; } - // Product ID - int& pId() { return second; } -}; - -struct JoystickOrderEntry : JoystickId -{ - std::string Name{}; - int JoyIndex{ -1 }; - - JoystickOrderEntry() = default; - JoystickOrderEntry(JoystickId id, const std::string& name, int joyIndex) : - JoystickId(id), Name(name), JoyIndex(joyIndex) {} - JoystickOrderEntry(int mid, int pid, const std::string& name) : - JoystickId(mid, pid), Name(name) {} +/* Backward compatibility */ +static const char* CFG_OPTIONS_STICKS = "Sticks"; - std::string to_string(); - bool from_string(const std::string&); -}; +/* New joystick options */ +static const char* CFG_OPTIONS_STICK_PCSTICK = "Joystick%dPCStick"; +static const char* CFG_OPTIONS_STICK_PCAXES = "Joystick%dPCAxes"; +static const char* CFG_OPTIONS_STICK_ANALOG = "Joystick%dAnalogMousepad"; +static const char* CFG_OPTIONS_STICK_DIGITAL = "Joystick%dDigitalMousepad"; +static const char* CFG_OPTIONS_JOYSTICK_SENSITIVITY = "JoystickSensitivity"; +static const char* CFG_OPTIONS_JOYSTICK_ORDER = "JoystickOrder%d"; -struct JoystickDev -{ - JOYCAPS Caps{}; - int Instance{ 0 }; - int Order{ -1 }; - int JoyIndex{ -1 }; - bool Configured{ false }; - bool Present{ false }; - - JoystickId Id() { return JoystickId{ Caps.wMid, Caps.wPid }; } - std::string DisplayString(); -}; +static const char* CFG_OPTIONS_STICKS_TO_KEYS = "JoysticksToKeys"; +static const char* CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP = "AutoloadJoystickMap"; +static const char* CFG_OPTIONS_STICKS_THRESHOLD = "JoysticksToKeysThreshold"; -struct PCJoystickState -{ - JoystickDev* Dev{ nullptr }; - int JoyIndex{ -1 }; - bool Captured{ false }; - unsigned int PrevAxes{ 0 }; - unsigned int PrevBtns{ 0 }; - bool JoystickToKeysActive{ false }; -}; -struct BBCJoystickConfig -{ - bool Enabled{ false }; - int PCStick{ 0 }; - int PCAxes{ 0 }; - bool AnalogMousestick{ false }; - bool DigitalMousestick{ false }; +/*****************************************************************************/ +/* All this stuff will go away when joystick configuration is done via dialog + */ +struct JoystickMenuIdsType { + UINT Joysticks[2]; + UINT Axes[3]; + UINT AnalogMousestick; + UINT DigitalMousestick; }; -class JoystickHandlerDetails -{ - BeebWin* m_BeebWin; - - bool m_JoystickTimerRunning{ false }; - JoystickDev m_JoystickDevs[MAX_JOYSTICK_DEVS]; - PCJoystickState m_PCJoystickState[NUM_PC_JOYSTICKS]; - BBCJoystickConfig m_JoystickConfig[NUM_BBC_JOYSTICKS]; - int m_MenuIdSticks[NUM_BBC_JOYSTICKS]{}; - int m_MenuIdAxes[NUM_BBC_JOYSTICKS]{}; - int m_Deadband; - double m_Gain{ 1.0 }; - bool m_JoystickToKeys{ false }; - bool m_AutoloadJoystickMap{ false }; - HWND m_JoystickTarget{ nullptr }; - char m_JoystickMapPath[_MAX_PATH]{}; - std::vector m_JoystickOrder; - -public: - JoystickHandlerDetails(BeebWin* beebWin) : m_BeebWin{ beebWin } {} - - /* Accessors */ - int GetMenuIdSticks(int bbcIdx) { return m_MenuIdSticks[bbcIdx]; } - bool GetJoystickToKeys() { return m_JoystickToKeys; } - void SetJoystickToKeys(bool enabled) { m_JoystickToKeys = enabled; } - void SetJoystickTarget(HWND target) { m_JoystickTarget = target; } - - /* BeebWin access */ - HWND GetHWnd(); - HMENU GetHMenu(); - int GetXWinSize(); - int GetYWinSize(); - void CheckMenuItem(UINT id, bool checked); - void EnableMenuItem(UINT id, bool enabled); - void SetMenuItemText(UINT id, const std::string& text); - - /* Menu handling - will soon be gone */ - int MenuIdToStick(int bbcIdx, UINT menuId); - UINT StickToMenuId(int bbcIdx, int pcStick); - int MenuIdToAxes(int bbcIdx, UINT menuId); - UINT AxesToMenuId(int bbcIdx, int pcAxes); - void UpdateJoystickConfig(int bbcIdx); - bool IsPCJoystickAssigned(int pcIdx, int bbcIdx); - bool IsPCJoystickOn(int pcIdx); - void InitMenu(void); - void UpdateJoystickMenu(void); - void ProcessMenuCommand(int bbcIdx, UINT menuId); - void ProcessAxesMenuCommand(int bbcIdx, UINT menuId); - void ToggleJoystickToKeys(); - void ToggleAutoloadJoystickMap(); - - /* Initialization */ - void ScanJoysticks(void); - void ResetJoystick(void); - bool InitJoystick(bool verbose = false); - bool CaptureJoystick(int Index, bool verbose); - - /* BBC Analog */ - void SetJoystickButton(int index, bool value); - void ScaleJoystick(int index, unsigned int x, unsigned int y, - unsigned int minX, unsigned int minY, - unsigned int maxX, unsigned int maxY); - - /* Mousestick */ - void SetMousestickButton(int index, bool button); - void ScaleMousestick(unsigned int x, unsigned int y); - - /* Joystick to keyboard */ - unsigned int GetJoystickAxes(const JOYCAPS& caps, int deadband, const JOYINFOEX& joyInfoEx); - void TranslateAxes(int joyId, unsigned int axesState); - void TranslateJoystickButtons(int joyId, unsigned int buttons); - void TranslateOrSendKey(int vkey, bool keyUp); - void TranslateJoystick(int joyId); - - /* Timer handler */ - void UpdateJoysticks(void); - - /* Joystick to keyboard mapping */ - void CheckForJoystickMap(const char* path); - void ResetJoystickMap(void); - void ResetJoyMap(JoyMap* joymap); - void ResetJoyMapToDefaultUser(void); - bool ReadJoyMap(const char* filename, JoyMap* joymap); - void LoadJoystickMap(void); - void SaveJoystickMap(void); - bool WriteJoyMap(const char* filename, JoyMap* joymap); - - /* Preferences */ - bool GetNthBoolValue(Preferences& preferences, const char* format, int idx, bool& value); - bool GetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD& value); - bool GetNthStringValue(Preferences& preferences, const char* format, int idx, std::string& value); - void SetNthBoolValue(Preferences& preferences, const char* format, int idx, bool value); - void SetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD value); - void SetNthStringValue(Preferences& preferences, const char* format, int idx, const std::string& value); - void EraseNthValue(Preferences& preferences, const char* format, int idx); - void ReadPreferences(Preferences& preferences); - void WritePreferences(Preferences& preferences); - void WriteJoystickOrder(Preferences& preferences); +/*****************************************************************************/ +constexpr JoystickMenuIdsType JoystickMenuIds[NUM_BBC_JOYSTICKS]{ + { + { IDM_JOYSTICK, IDM_JOY1_PCJOY2 }, + { IDM_JOY1_PRIMARY, IDM_JOY1_SECONDARY1, IDM_JOY1_SECONDARY2 }, + IDM_ANALOGUE_MOUSESTICK, + IDM_DIGITAL_MOUSESTICK, + }, + { + { IDM_JOY2_PCJOY1, IDM_JOY2_PCJOY2 }, + { IDM_JOY2_PRIMARY, IDM_JOY2_SECONDARY1, IDM_JOY2_SECONDARY2 }, + IDM_JOY2_ANALOGUE_MOUSESTICK, + IDM_JOY2_DIGITAL_MOUSESTICK, + } }; /*****************************************************************************/ @@ -233,6 +116,44 @@ bool JoystickOrderEntry::from_string(const std::string& str) return sscanf(str.c_str(), "%x:%x", &mId(), &pId()) == 2; } +/****************************************************************************/ +static int MenuIdToStick(int bbcIdx, UINT menuId) +{ + for (int pcIdx = 0; pcIdx < _countof(JoystickMenuIdsType::Joysticks); ++pcIdx) + { + if (menuId == JoystickMenuIds[bbcIdx].Joysticks[pcIdx]) + return pcIdx + 1; + } + return 0; +} + +/****************************************************************************/ +static UINT StickToMenuId(int bbcIdx, int pcStick) +{ + if (pcStick > 0 && pcStick - 1 < _countof(JoystickMenuIdsType::Joysticks)) + return JoystickMenuIds[bbcIdx].Joysticks[pcStick - 1]; + return 0; +} + +/****************************************************************************/ +static int MenuIdToAxes(int bbcIdx, UINT menuId) +{ + for (int axesIdx = 0; axesIdx < _countof(JoystickMenuIdsType::Axes); ++axesIdx) + { + if (menuId == JoystickMenuIds[bbcIdx].Axes[axesIdx]) + return axesIdx; + } + return 0; +} + +/****************************************************************************/ +static UINT AxesToMenuId(int bbcIdx, int pcAxes) +{ + if (pcAxes >= 0 && pcAxes < _countof(JoystickMenuIdsType::Axes)) + return JoystickMenuIds[bbcIdx].Axes[pcAxes]; + return 0; +} + /*****************************************************************************/ std::string JoystickDev::DisplayString() { @@ -246,98 +167,223 @@ std::string JoystickDev::DisplayString() } /*****************************************************************************/ -HWND JoystickHandlerDetails::GetHWnd() { return m_BeebWin->m_hWnd; } +bool JoystickDev::Update() +{ + if (!Present) + return false; -HMENU JoystickHandlerDetails::GetHMenu() { return m_BeebWin->m_hMenu; } + InfoEx.dwSize = sizeof(InfoEx); + InfoEx.dwFlags = JOY_RETURNALL | JOY_RETURNPOVCTS; + return joyGetPosEx(JoyIndex, &InfoEx) == JOYERR_NOERROR; +} -int JoystickHandlerDetails::GetXWinSize() { return m_BeebWin->m_XWinSize; } +/*****************************************************************************/ +DWORD JoystickDev::GetButtons() +{ + return InfoEx.dwButtons; +} -int JoystickHandlerDetails::GetYWinSize() { return m_BeebWin->m_YWinSize; } +/*****************************************************************************/ +void JoystickDev::GetAxesValue(int axesSet, int& x, int& y) +{ + UINT minX, maxX; + UINT minY, maxY; + DWORD dwX, dwY; + switch (axesSet) + { + case 1: + dwX = InfoEx.dwUpos; minX = Caps.wUmin; maxX = Caps.wUmax; + dwY = InfoEx.dwRpos; minY = Caps.wRmin; maxY = Caps.wRmax; + break; + case 2: + dwX = InfoEx.dwZpos; minX = Caps.wZmin; maxX = Caps.wZmax; + dwY = InfoEx.dwRpos; minY = Caps.wRmin; maxY = Caps.wRmax; + break; + default: + dwX = InfoEx.dwXpos; minX = Caps.wXmin; maxX = Caps.wXmax; + dwY = InfoEx.dwYpos; minY = Caps.wYmin; maxY = Caps.wYmax; + break; + } + x = ((dwX - minX) * 65535 / (maxX - minX)); + y = ((dwY - minY) * 65535 / (maxY - minY)); +} -void JoystickHandlerDetails::CheckMenuItem(UINT id, bool checked) { m_BeebWin->CheckMenuItem(id, checked); } +DWORD JoystickDev::GetAxesState(int threshold) +{ + using InfoT = JOYINFOEX; + using CapsT = JOYCAPS; + unsigned int axes = 0; -void JoystickHandlerDetails::EnableMenuItem(UINT id, bool enabled) { m_BeebWin->EnableMenuItem(id, enabled); } + auto Scale = [this](DWORD InfoT::* pos, UINT CapsT::* min, UINT CapsT::* max) { + return (int)((double)(InfoEx.*pos - Caps.*min) * 65535.0 / + (double)(Caps.*max - Caps.*min)); + }; -void JoystickHandlerDetails::SetMenuItemText(UINT id, const std::string& text) -{ - MENUITEMINFO mii{ 0 }; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_STRING; - mii.fType = MFT_STRING; - mii.dwTypeData = const_cast(text.c_str()); - SetMenuItemInfo(GetHMenu(), id, FALSE, &mii); -} + auto Detect = [&axes, threshold](const int val, const int nbit, const int pbit) { + if (val < 32767 - threshold) + axes |= (1 << nbit); + else if (val > 32767 + threshold) + axes |= (1 << pbit); + }; -/*****************************************************************************/ -/* All this stuff will go away when joystick configuration is done via dialog - */ -struct JoystickMenuIdsType { - UINT Joysticks[2]; - UINT Axes[3]; - UINT AnalogMousestick; - UINT DigitalMousestick; -}; + int ty = Scale(&InfoT::dwYpos, &CapsT::wYmin, &CapsT::wYmax); + int tx = Scale(&InfoT::dwXpos, &CapsT::wXmin, &CapsT::wXmax); + int tz = Scale(&InfoT::dwZpos, &CapsT::wZmin, &CapsT::wZmin); + int tr = Scale(&InfoT::dwRpos, &CapsT::wRmin, &CapsT::wRmax); + int tu = Scale(&InfoT::dwUpos, &CapsT::wUmin, &CapsT::wUmax); + int tv = Scale(&InfoT::dwVpos, &CapsT::wVmin, &CapsT::wVmax); -/*****************************************************************************/ -constexpr JoystickMenuIdsType JoystickMenuIds[NUM_BBC_JOYSTICKS] { - { - { IDM_JOYSTICK, IDM_JOY1_PCJOY2 }, - { IDM_JOY1_PRIMARY, IDM_JOY1_SECONDARY1, IDM_JOY1_SECONDARY2 }, - IDM_ANALOGUE_MOUSESTICK, - IDM_DIGITAL_MOUSESTICK, - }, + Detect(ty, JOYSTICK_AXIS_UP, JOYSTICK_AXIS_DOWN); + Detect(tx, JOYSTICK_AXIS_LEFT, JOYSTICK_AXIS_RIGHT); + Detect(tz, JOYSTICK_AXIS_Z_N, JOYSTICK_AXIS_Z_P); + Detect(tr, JOYSTICK_AXIS_R_N, JOYSTICK_AXIS_R_P); + Detect(tu, JOYSTICK_AXIS_U_N, JOYSTICK_AXIS_U_P); + Detect(tv, JOYSTICK_AXIS_V_N, JOYSTICK_AXIS_V_P); + + if (InfoEx.dwPOV != JOY_POVCENTERED) { - { IDM_JOY2_PCJOY1, IDM_JOY2_PCJOY2 }, - { IDM_JOY2_PRIMARY, IDM_JOY2_SECONDARY1, IDM_JOY2_SECONDARY2 }, - IDM_JOY2_ANALOGUE_MOUSESTICK, - IDM_JOY2_DIGITAL_MOUSESTICK, + if (InfoEx.dwPOV >= 29250 || InfoEx.dwPOV < 6750) + axes |= (1 << JOYSTICK_AXIS_HAT_UP); + if (InfoEx.dwPOV >= 2250 && InfoEx.dwPOV < 15750) + axes |= (1 << JOYSTICK_AXIS_HAT_RIGHT); + if (InfoEx.dwPOV >= 11250 && InfoEx.dwPOV < 24750) + axes |= (1 << JOYSTICK_AXIS_HAT_DOWN); + if (InfoEx.dwPOV >= 20250 && InfoEx.dwPOV < 33750) + axes |= (1 << JOYSTICK_AXIS_HAT_LEFT); } -}; + return axes; +} /****************************************************************************/ -int JoystickHandlerDetails::MenuIdToStick(int bbcIdx, UINT menuId) +void JoystickHandler::ScanJoysticks() { - for (int pcIdx = 0; pcIdx < _countof(JoystickMenuIdsType::Joysticks); ++pcIdx) + JOYINFOEX joyInfoEx; + std::map> listById; + + // Clear PC joystick list + for (PCJoystickState& state : m_PCJoystickState) { - if (menuId == JoystickMenuIds[bbcIdx].Joysticks[pcIdx]) - return pcIdx + 1; + state.Captured = false; + state.Dev = nullptr; + state.JoyIndex = -1; } - return 0; -} -/****************************************************************************/ -UINT JoystickHandlerDetails::StickToMenuId(int bbcIdx, int pcStick) -{ - if (pcStick > 0 && pcStick - 1 < _countof(JoystickMenuIdsType::Joysticks)) - return JoystickMenuIds[bbcIdx].Joysticks[pcStick - 1]; - return 0; -} + // Get all configured and present joysticks + for (int devIdx = 0; devIdx < MAX_JOYSTICK_DEVS; ++devIdx) + { + JoystickDev& dev = m_JoystickDevs[devIdx]; + dev.Configured = false; + dev.Present = false; + dev.Instance = 0; + dev.Order = -1; + dev.JoyIndex = devIdx; -/****************************************************************************/ -int JoystickHandlerDetails::MenuIdToAxes(int bbcIdx, UINT menuId) -{ - for (int axesIdx = 0; axesIdx < _countof(JoystickMenuIdsType::Axes); ++axesIdx) + memset(&joyInfoEx, 0, sizeof(joyInfoEx)); + joyInfoEx.dwSize = sizeof(joyInfoEx); + joyInfoEx.dwFlags = JOY_RETURNBUTTONS; + + DWORD Result = joyGetDevCaps(devIdx, &dev.Caps, sizeof(JOYCAPS)); + if (Result == JOYERR_NOERROR) + { + dev.Configured = true; + + Result = joyGetPosEx(devIdx, &joyInfoEx); + if (Result == JOYERR_NOERROR) + { + dev.Present = true; + listById[dev.Id()].push_back(&dev); + dev.Instance = listById[dev.Id()].size(); + } + } + } + + int joyIdx = 0; + int order = 0; + // Start with joysticks in the order list + for (JoystickOrderEntry& entry : m_JoystickOrder) + { + std::list& list = listById[entry]; + if (list.size() != 0) + { + JoystickDev& dev = *(list.front()); + list.pop_front(); + dev.Order = order++; + entry.JoyIndex = dev.JoyIndex; + if (dev.Present && joyIdx < NUM_PC_JOYSTICKS) + { + m_PCJoystickState[joyIdx].Dev = &dev; + m_PCJoystickState[joyIdx].JoyIndex = dev.JoyIndex; + ++joyIdx; + } + } + else + { + entry.JoyIndex = -1; + } + } + + std::list newJoysticks; + // Add joysticks not in the order list + for (JoystickDev& dev : m_JoystickDevs) + { + if (joyIdx >= NUM_PC_JOYSTICKS) + break; + + if (dev.Present && dev.Order == -1) + { + m_PCJoystickState[joyIdx].Dev = &dev; + m_PCJoystickState[joyIdx].JoyIndex = dev.JoyIndex; + ++joyIdx; + newJoysticks.push_back(&dev); + } + } + + // If joystick order list is too long, remove some unconnected entries + // Remove entries from the beginning or from the end, or some other order? + int to_remove = newJoysticks.size() + m_JoystickOrder.size() - MAX_JOYSTICK_ORDER; + unsigned int idx = 0; + while (to_remove > 0 && idx < m_JoystickOrder.size()) { - if (menuId == JoystickMenuIds[bbcIdx].Axes[axesIdx]) - return axesIdx; + if (m_JoystickOrder[idx].JoyIndex == -1) + { + m_JoystickOrder.erase(m_JoystickOrder.begin() + idx); + --to_remove; + } + else + ++idx; + } + + // Add new joystick at the end of order list + for (JoystickDev* dev : newJoysticks) + m_JoystickOrder.emplace_back(dev->Id(), dev->DisplayString(), dev->JoyIndex); + + // Update order in joystick list + for (idx = 0; idx < static_cast(m_JoystickOrder.size()); ++idx) + { + JoystickOrderEntry& entry = m_JoystickOrder[idx]; + if (entry.JoyIndex != -1) + m_JoystickDevs[entry.JoyIndex].Order = idx; } - return 0; } /****************************************************************************/ -UINT JoystickHandlerDetails::AxesToMenuId(int bbcIdx, int pcAxes) +JoystickHandlerPtr::JoystickHandlerPtr() : std::unique_ptr{ std::make_unique() } {} + +/****************************************************************************/ +JoystickHandlerPtr::~JoystickHandlerPtr() {} + +/****************************************************************************/ +void BeebWin::SetJoystickTarget(HWND target) { - if (pcAxes >= 0 && pcAxes < _countof(JoystickMenuIdsType::Axes)) - return JoystickMenuIds[bbcIdx].Axes[pcAxes]; - return 0; + m_JoystickTarget = target; } /****************************************************************************/ // Update joystick configuration from checked menu items -void JoystickHandlerDetails::UpdateJoystickConfig(int bbcIdx) +void BeebWin::UpdateJoystickConfig(int bbcIdx) { - m_JoystickConfig[bbcIdx].Enabled = (m_MenuIdAxes[bbcIdx] != 0); + m_JoystickConfig[bbcIdx].Enabled = (m_MenuIdSticks[bbcIdx] != 0); m_JoystickConfig[bbcIdx].PCStick = MenuIdToStick(bbcIdx, m_MenuIdSticks[bbcIdx]); m_JoystickConfig[bbcIdx].PCAxes = MenuIdToAxes(bbcIdx, m_MenuIdAxes[bbcIdx]); m_JoystickConfig[bbcIdx].AnalogMousestick = @@ -347,23 +393,33 @@ void JoystickHandlerDetails::UpdateJoystickConfig(int bbcIdx) } /****************************************************************************/ -// Check if this PC joystick is assigned to this BBC joystick -bool JoystickHandlerDetails::IsPCJoystickAssigned(int pcIdx, int bbcIdx) +int BeebWin::GetPCJoystick(int bbcIdx) { - return m_JoystickConfig[bbcIdx].PCStick == pcIdx + 1; + return m_JoystickConfig[bbcIdx].PCStick; +} + +/****************************************************************************/ +bool BeebWin::GetAnalogMousestick(int bbcIdx) +{ + return m_JoystickConfig[bbcIdx].AnalogMousestick; +} + +/****************************************************************************/ +bool BeebWin::GetDigitalMousestick(int bbcIdx) +{ + return m_JoystickConfig[bbcIdx].DigitalMousestick; } /****************************************************************************/ // Check if PC joystick is assigned to any BBC joystick -// TODO: Update m_PCStickForJoystick early and use it here -bool JoystickHandlerDetails::IsPCJoystickOn(int pcIdx) +bool BeebWin::IsPCJoystickOn(int pcIdx) { if (pcIdx >= _countof(JoystickMenuIdsType::Joysticks)) return false; for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) { - if (IsPCJoystickAssigned(pcIdx, bbcIdx)) + if (GetPCJoystick(bbcIdx) == pcIdx + 1) return true; } @@ -371,7 +427,7 @@ bool JoystickHandlerDetails::IsPCJoystickOn(int pcIdx) } /*****************************************************************************/ -void JoystickHandlerDetails::InitMenu() +void BeebWin::InitJoystickMenu() { for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) { @@ -397,20 +453,20 @@ void JoystickHandlerDetails::InitMenu() } /****************************************************************************/ -void JoystickHandlerDetails::UpdateJoystickMenu() +void BeebWin::UpdateJoystickMenu() { bool EnableInit = false; // Enable "Initialize Joysticks" menu item if any more joysticks could be possibly captured (or always?) for (int pcIdx = 0; pcIdx < NUM_PC_JOYSTICKS; ++pcIdx) { - if ((m_JoystickToKeys || IsPCJoystickOn(pcIdx)) && !m_PCJoystickState[pcIdx].Captured) + if ((m_JoystickToKeys || IsPCJoystickOn(pcIdx)) && !m_JoystickHandler->m_PCJoystickState[pcIdx].Captured) EnableInit = true; } EnableMenuItem(IDM_INIT_JOYSTICK, EnableInit); // Check "Initialize Joysticks" menu item if any joystick is already captured - CheckMenuItem(IDM_INIT_JOYSTICK, m_PCJoystickState[0].Captured); + CheckMenuItem(IDM_INIT_JOYSTICK, m_JoystickHandler->m_PCJoystickState[0].Captured); // Enable axes menu items if any PC joystick is assigned to the related BBC joystick for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) @@ -433,11 +489,8 @@ void JoystickHandlerDetails::UpdateJoystickMenu() } #endif - for (int pcIdx = 0; pcIdx < NUM_PC_JOYSTICKS; ++pcIdx) - { - if (IsPCJoystickAssigned(pcIdx, bbcIdx)) - EnableAxes = true; - } + if (GetPCJoystick(bbcIdx) != 0) + EnableAxes = true; for (int axesIdx = 0; axesIdx < _countof(JoystickMenuIdsType::Axes); ++axesIdx) EnableMenuItem(JoystickMenuIds[bbcIdx].Axes[axesIdx], EnableAxes); @@ -445,7 +498,7 @@ void JoystickHandlerDetails::UpdateJoystickMenu() } /****************************************************************************/ -void JoystickHandlerDetails::ProcessMenuCommand(int bbcIdx, UINT MenuId) +void BeebWin::ProcessJoystickMenuCommand(int bbcIdx, UINT MenuId) { /* Disable current selection */ if (m_MenuIdSticks[bbcIdx] != 0) @@ -482,7 +535,7 @@ void JoystickHandlerDetails::ProcessMenuCommand(int bbcIdx, UINT MenuId) } /****************************************************************************/ -void JoystickHandlerDetails::ProcessAxesMenuCommand(int bbcIdx, UINT MenuId) +void BeebWin::ProcessJoystickAxesMenuCommand(int bbcIdx, UINT MenuId) { CheckMenuItem(m_MenuIdAxes[bbcIdx], false); m_MenuIdAxes[bbcIdx] = MenuId; @@ -491,7 +544,7 @@ void JoystickHandlerDetails::ProcessAxesMenuCommand(int bbcIdx, UINT MenuId) } /****************************************************************************/ -void JoystickHandlerDetails::ToggleJoystickToKeys(void) +void BeebWin::ProcessJoystickToKeysCommand(void) { m_JoystickToKeys = !m_JoystickToKeys; InitJoystick(false); @@ -499,142 +552,31 @@ void JoystickHandlerDetails::ToggleJoystickToKeys(void) } /****************************************************************************/ -void JoystickHandlerDetails::ToggleAutoloadJoystickMap(void) +void BeebWin::ProcessAutoloadJoystickMapCommand(void) { m_AutoloadJoystickMap = !m_AutoloadJoystickMap; CheckMenuItem(IDM_AUTOLOADJOYMAP, m_AutoloadJoystickMap); } /****************************************************************************/ -void JoystickHandlerDetails::ResetJoystick(void) +void BeebWin::ResetJoystick() { for (int pcIdx = 0; pcIdx < NUM_PC_JOYSTICKS; ++pcIdx) { - m_PCJoystickState[pcIdx].Captured = false; - TranslateJoystick(pcIdx); - } - -} - -/****************************************************************************/ -void JoystickHandlerDetails::ScanJoysticks(void) -{ - JOYINFOEX joyInfoEx; - std::map> listById; - - // Clear PC joystick list - for (PCJoystickState& state : m_PCJoystickState) - { + auto& state = m_JoystickHandler->m_PCJoystickState[pcIdx]; state.Captured = false; - state.Dev = nullptr; - state.JoyIndex = -1; - } - - // Get all configured and present joysticks - for (int devIdx = 0; devIdx < MAX_JOYSTICK_DEVS; ++devIdx) - { - JoystickDev& dev = m_JoystickDevs[devIdx]; - dev.Configured = false; - dev.Present = false; - dev.Instance = 0; - dev.Order = -1; - dev.JoyIndex = devIdx; - - memset(&joyInfoEx, 0, sizeof(joyInfoEx)); - joyInfoEx.dwSize = sizeof(joyInfoEx); - joyInfoEx.dwFlags = JOY_RETURNBUTTONS; - - DWORD Result = joyGetDevCaps(devIdx, &dev.Caps, sizeof(JOYCAPS)); - if (Result == JOYERR_NOERROR) - { - dev.Configured = true; - - Result = joyGetPosEx(devIdx, &joyInfoEx); - if (Result == JOYERR_NOERROR) - { - dev.Present = true; - listById[dev.Id()].push_back(&dev); - dev.Instance = listById[dev.Id()].size(); - } - } - } - - int joyIdx = 0; - int order = 0; - // Start with joysticks in the order list - for (JoystickOrderEntry& entry : m_JoystickOrder) - { - std::list& list = listById[entry]; - if (list.size() != 0) - { - JoystickDev& dev = *(list.front()); - list.pop_front(); - dev.Order = order++; - entry.JoyIndex = dev.JoyIndex; - if (dev.Present && joyIdx < NUM_PC_JOYSTICKS) - { - m_PCJoystickState[joyIdx].Dev = &dev; - m_PCJoystickState[joyIdx].JoyIndex = dev.JoyIndex; - ++joyIdx; - } - } - else - { - entry.JoyIndex = -1; - } - } - - std::list newJoysticks; - // Add joysticks not in the order list - for (JoystickDev& dev : m_JoystickDevs) - { - if (joyIdx >= NUM_PC_JOYSTICKS) - break; - - if (dev.Present && dev.Order == -1) - { - m_PCJoystickState[joyIdx].Dev = &dev; - m_PCJoystickState[joyIdx].JoyIndex = dev.JoyIndex; - ++joyIdx; - newJoysticks.push_back(&dev); - } - } - - // If joystick order list is too long, remove some unconnected entries - // Remove entries from the beginning or from the end, or some other order? - int to_remove = newJoysticks.size() + m_JoystickOrder.size() - MAX_JOYSTICK_ORDER; - unsigned int idx = 0; - while (to_remove > 0 && idx < m_JoystickOrder.size()) - { - if (m_JoystickOrder[idx].JoyIndex == -1) - { - m_JoystickOrder.erase(m_JoystickOrder.begin() + idx); - --to_remove; - } - ++idx; - } - - // Add new joystick at the end of order list - for (JoystickDev* dev : newJoysticks) - m_JoystickOrder.emplace_back(dev->Id(), dev->DisplayString(), dev->JoyIndex); - - // Update order in joystick list - for (idx = 0; idx < static_cast(m_JoystickOrder.size()); ++idx) - { - JoystickOrderEntry& entry = m_JoystickOrder[idx]; - if (entry.JoyIndex != -1) - m_JoystickDevs[entry.JoyIndex].Order = idx; + TranslateJoystick(pcIdx); } } /****************************************************************************/ -bool JoystickHandlerDetails::InitJoystick(bool verbose) +bool BeebWin::InitJoystick(bool verbose) { bool Success = true; ResetJoystick(); - ScanJoysticks(); + m_JoystickHandler->ScanJoysticks(); if (IsPCJoystickOn(0) || IsPCJoystickOn(1) || m_JoystickToKeys) { @@ -646,7 +588,7 @@ bool JoystickHandlerDetails::InitJoystick(bool verbose) if (!m_JoystickTimerRunning) { - SetTimer(GetHWnd(), 3, 20, NULL); + SetTimer(m_hWnd, 3, 20, NULL); m_JoystickTimerRunning = true; } } @@ -654,7 +596,7 @@ bool JoystickHandlerDetails::InitJoystick(bool verbose) { if (m_JoystickTimerRunning) { - KillTimer(GetHWnd(), 3); + KillTimer(m_hWnd, 3); m_JoystickTimerRunning = false; } } @@ -665,13 +607,13 @@ bool JoystickHandlerDetails::InitJoystick(bool verbose) } /****************************************************************************/ -bool JoystickHandlerDetails::CaptureJoystick(int Index, bool verbose) +bool BeebWin::CaptureJoystick(int Index, bool verbose) { bool success = false; - if (m_PCJoystickState[Index].Dev) + if (m_JoystickHandler->m_PCJoystickState[Index].Dev) { - m_PCJoystickState[Index].Captured = true; + m_JoystickHandler->m_PCJoystickState[Index].Captured = true; success = true; } else if (verbose && (IsPCJoystickOn(Index) || m_JoystickToKeys && Index == 0)) @@ -679,25 +621,25 @@ bool JoystickHandlerDetails::CaptureJoystick(int Index, bool verbose) char str[100]; sprintf(str, "Failed to initialise joystick %d", Index + 1); - MessageBox(GetHWnd(), str, WindowTitle, MB_OK | MB_ICONWARNING); + MessageBox(m_hWnd, str, WindowTitle, MB_OK | MB_ICONWARNING); } return success; } /****************************************************************************/ -void JoystickHandlerDetails::SetJoystickButton(int index, bool value) +void BeebWin::SetJoystickButton(int index, bool value) { JoystickButton[index] = value; } /****************************************************************************/ -void JoystickHandlerDetails::ScaleJoystick(int index, unsigned int x, unsigned int y, +void BeebWin::ScaleJoystick(int index, unsigned int x, unsigned int y, unsigned int minX, unsigned int minY, unsigned int maxX, unsigned int maxY) { /* Gain and reverse the readings */ - double sx = 0.5 + ((double)(maxX - x) / (double)(maxX - minX) - 0.5) * m_Gain; - double sy = 0.5 + ((double)(maxY - y) / (double)(maxY - minY) - 0.5) * m_Gain; + double sx = 0.5 + ((double)(maxX - x) / (double)(maxX - minX) - 0.5) * m_JoystickSensitivity; + double sy = 0.5 + ((double)(maxY - y) / (double)(maxY - minY) - 0.5) * m_JoystickSensitivity; /* Scale to 0-65535 range */ sx = std::max(0.0, std::min(65535.0, sx * 65535.0)); @@ -708,7 +650,7 @@ void JoystickHandlerDetails::ScaleJoystick(int index, unsigned int x, unsigned i } /****************************************************************************/ -void JoystickHandlerDetails::SetMousestickButton(int index, bool value) +void BeebWin::SetMousestickButton(int index, bool value) { if (index == 0) { @@ -735,12 +677,12 @@ void JoystickHandlerDetails::SetMousestickButton(int index, bool value) } /****************************************************************************/ -void JoystickHandlerDetails::ScaleMousestick(unsigned int x, unsigned int y) +void BeebWin::ScaleMousestick(unsigned int x, unsigned int y) { for (int index = 0; index < 2; ++index) { - int XPos = (GetXWinSize() - x) * 65535 / GetXWinSize(); - int YPos = (GetYWinSize() - y) * 65535 / GetYWinSize(); + int XPos = (m_XWinSize - x) * 65535 / m_XWinSize; + int YPos = (m_YWinSize - y) * 65535 / m_YWinSize; if (m_JoystickConfig[index].AnalogMousestick) { @@ -780,60 +722,13 @@ void JoystickHandlerDetails::ScaleMousestick(unsigned int x, unsigned int y) } } -/****************************************************************************/ -unsigned int JoystickHandlerDetails::GetJoystickAxes(const JOYCAPS& caps, int deadband, const JOYINFOEX& joyInfoEx) -{ - using Info = JOYINFOEX; - using Caps = JOYCAPS; - unsigned int axes = 0; - - auto Scale = [&caps, &joyInfoEx](DWORD Info::*pos, UINT Caps::*min, UINT Caps::*max) { - return (int)((double)(joyInfoEx.*pos - caps.*min) * 65535.0 / - (double)(caps.*max - caps.*min)); - }; - - auto Detect = [&axes, deadband](const int val, const int nbit, const int pbit) { - if (val < 32768 - deadband) - axes |= (1 << nbit); - else if (val > 32768 + deadband) - axes |= (1 << pbit); - }; - - int ty = Scale(&Info::dwYpos, &Caps::wYmin, &Caps::wYmax); - int tx = Scale(&Info::dwXpos, &Caps::wXmin, &Caps::wXmax); - int tz = Scale(&Info::dwZpos, &Caps::wZmin, &Caps::wZmin); - int tr = Scale(&Info::dwRpos, &Caps::wRmin, &Caps::wRmax); - int tu = Scale(&Info::dwUpos, &Caps::wUmin, &Caps::wUmax); - int tv = Scale(&Info::dwVpos, &Caps::wVmin, &Caps::wVmax); - - Detect(ty, JOYSTICK_AXIS_UP, JOYSTICK_AXIS_DOWN); - Detect(tx, JOYSTICK_AXIS_LEFT, JOYSTICK_AXIS_RIGHT); - Detect(tz, JOYSTICK_AXIS_Z_N, JOYSTICK_AXIS_Z_P); - Detect(tr, JOYSTICK_AXIS_R_N, JOYSTICK_AXIS_R_P); - Detect(tu, JOYSTICK_AXIS_U_N, JOYSTICK_AXIS_U_P); - Detect(tv, JOYSTICK_AXIS_V_N, JOYSTICK_AXIS_V_P); - - if (joyInfoEx.dwPOV != JOY_POVCENTERED) - { - if (joyInfoEx.dwPOV >= 29250 || joyInfoEx.dwPOV < 6750) - axes |= (1 << JOYSTICK_AXIS_HAT_UP); - if (joyInfoEx.dwPOV >= 2250 && joyInfoEx.dwPOV < 15750) - axes |= (1 << JOYSTICK_AXIS_HAT_RIGHT); - if (joyInfoEx.dwPOV >= 11250 && joyInfoEx.dwPOV < 24750) - axes |= (1 << JOYSTICK_AXIS_HAT_DOWN); - if (joyInfoEx.dwPOV >= 20250 && joyInfoEx.dwPOV < 33750) - axes |= (1 << JOYSTICK_AXIS_HAT_LEFT); - } - - return axes; -} - /****************************************************************************/ // Translate joystick position changes to key up or down message -void JoystickHandlerDetails::TranslateAxes(int joyId, unsigned int axesState) +void BeebWin::TranslateAxes(int joyId, unsigned int axesState) { + auto& joyState = m_JoystickHandler->m_PCJoystickState[joyId]; + unsigned int& prevAxes = joyState.PrevAxes; int vkeys = BEEB_VKEY_JOY_START + joyId * (JOYSTICK_MAX_AXES + JOYSTICK_MAX_BTNS); - unsigned int& prevAxes = m_PCJoystickState[joyId].PrevAxes; if (axesState != prevAxes) { @@ -853,11 +748,11 @@ void JoystickHandlerDetails::TranslateAxes(int joyId, unsigned int axesState) } /****************************************************************************/ -void JoystickHandlerDetails::TranslateJoystickButtons(int joyId, unsigned int buttons) +void BeebWin::TranslateJoystickButtons(int joyId, unsigned int buttons) { const int BUTTON_COUNT = 32; - - unsigned int& prevBtns = m_PCJoystickState[joyId].PrevBtns; + auto& joyState = m_JoystickHandler->m_PCJoystickState[joyId]; + unsigned int& prevBtns = joyState.PrevBtns; int vkeys = BEEB_VKEY_JOY_START + JOYSTICK_MAX_AXES + joyId * (JOYSTICK_MAX_AXES + JOYSTICK_MAX_BTNS); @@ -880,54 +775,43 @@ void JoystickHandlerDetails::TranslateJoystickButtons(int joyId, unsigned int bu } /****************************************************************************/ -void JoystickHandlerDetails::TranslateOrSendKey(int vkey, bool keyUp) +void BeebWin::TranslateOrSendKey(int vkey, bool keyUp) { if (m_JoystickTarget == nullptr) { int row, col; - m_BeebWin->TranslateKey(vkey, keyUp, row, col); + TranslateKey(vkey, keyUp, row, col); } else if (!keyUp) { - // Keyboard input dialog is visible - translate button down to key down + // Joystick mapping dialog is visible - translate button down to key down // message and send to dialog PostMessage(m_JoystickTarget, WM_KEYDOWN, vkey, 0); } } /****************************************************************************/ -void JoystickHandlerDetails::TranslateJoystick(int joyId) +void BeebWin::TranslateJoystick(int joyId) { - static const JOYCAPS dummyJoyCaps = { - 0, 0, "", - 0, 65535, 0, 65535, 0, 65535, - 16, 10, 1000, - 0, 65535, 0, 65535, 0, 65535 - }; + auto& handler = m_JoystickHandler; + auto& joyState = handler->m_PCJoystickState[joyId]; + auto* joyDev = joyState.Dev; bool success = false; - JOYINFOEX joyInfoEx{}; - - const JOYCAPS* joyCaps = &m_PCJoystickState[joyId].Dev->Caps; - DWORD joyIndex = m_PCJoystickState[joyId].JoyIndex; + DWORD buttons = 0; - joyInfoEx.dwSize = sizeof(joyInfoEx); - joyInfoEx.dwFlags = JOY_RETURNALL | JOY_RETURNPOVCTS; - - if (m_PCJoystickState[joyId].Captured && - joyGetPosEx(joyIndex, &joyInfoEx) == JOYERR_NOERROR) + if (joyState.Captured && joyDev->Update()) + { success = true; + buttons = joyDev->GetButtons(); + } // If joystick is disabled or error occurs, reset all axes and buttons if (!success) { - joyCaps = &dummyJoyCaps; - joyInfoEx.dwXpos = joyInfoEx.dwYpos = joyInfoEx.dwZpos = 32768; - joyInfoEx.dwRpos = joyInfoEx.dwUpos = joyInfoEx.dwVpos = 32768; - joyInfoEx.dwPOV = JOY_POVCENTERED; - if (m_PCJoystickState[joyId].Captured) + if (joyState.Captured) { // Reset 'captured' flag and update menu entry - m_PCJoystickState[joyId].Captured = false; + joyState.Captured = false; UpdateJoystickMenu(); } } @@ -935,79 +819,65 @@ void JoystickHandlerDetails::TranslateJoystick(int joyId) // PC joystick to BBC joystick for (int bbcIndex = 0; bbcIndex < NUM_BBC_JOYSTICKS; ++bbcIndex) { - if (IsPCJoystickAssigned(joyId, bbcIndex)) + if (GetPCJoystick(bbcIndex) == joyId + 1) { if (bbcIndex == 1 && m_JoystickConfig[0].PCStick == m_JoystickConfig[1].PCStick && m_JoystickConfig[0].PCAxes != m_JoystickConfig[1].PCAxes) { // If both BBC joysticks are mapped to the same PC gamepad, but not // the same axes, map buttons 2 and 4 to the Beeb's PB1 input - SetJoystickButton(bbcIndex, (joyInfoEx.dwButtons & (JOY_BUTTON2 | JOY_BUTTON4)) != 0); + SetJoystickButton(bbcIndex, (buttons & (JOY_BUTTON2 | JOY_BUTTON4)) != 0); } else { - SetJoystickButton(bbcIndex, (joyInfoEx.dwButtons & (JOY_BUTTON1 | JOY_BUTTON3)) != 0); + SetJoystickButton(bbcIndex, (buttons & (JOY_BUTTON1 | JOY_BUTTON3)) != 0); } if (bbcIndex == 0 && !m_JoystickConfig[1].Enabled) { // Second BBC joystick not enabled - map buttons 2 and 4 to Beeb's PB1 input - SetJoystickButton(1, (joyInfoEx.dwButtons & (JOY_BUTTON2 | JOY_BUTTON4)) != 0); + SetJoystickButton(1, (buttons & (JOY_BUTTON2 | JOY_BUTTON4)) != 0); } int axesConfig = m_JoystickConfig[bbcIndex].PCAxes; - if (axesConfig == 0) - { - ScaleJoystick(bbcIndex, joyInfoEx.dwXpos, joyInfoEx.dwYpos, - joyCaps->wXmin, joyCaps->wYmin, - joyCaps->wXmax, joyCaps->wYmax); - } - else if (axesConfig == 1) - { - ScaleJoystick(bbcIndex, joyInfoEx.dwUpos, joyInfoEx.dwRpos, - joyCaps->wUmin, joyCaps->wRmin, - joyCaps->wUmax, joyCaps->wRmax); - } - else if (axesConfig == 2) - { - ScaleJoystick(bbcIndex, joyInfoEx.dwZpos, joyInfoEx.dwRpos, - joyCaps->wZmin, joyCaps->wRmin, - joyCaps->wZmax, joyCaps->wRmax); - } + int x = 32767, y = 32767; + if (success) + joyDev->GetAxesValue(axesConfig, x, y); + ScaleJoystick(bbcIndex, x, y, 0, 0, 65535, 65535); } } // Joystick to keyboard mapping - if (m_JoystickToKeys) + if (m_JoystickToKeys && success) { - auto axes = GetJoystickAxes(*joyCaps, m_Deadband, joyInfoEx); + auto axes = joyDev->GetAxesState(m_JoystickToKeysThreshold); TranslateAxes(joyId, axes); - TranslateJoystickButtons(joyId, joyInfoEx.dwButtons); - m_PCJoystickState[joyId].JoystickToKeysActive = true; + TranslateJoystickButtons(joyId, buttons); + handler->m_PCJoystickState[joyId].JoystickToKeysActive = true; } // Make sure to reset keyboard state - if (!m_JoystickToKeys && m_PCJoystickState[joyId].JoystickToKeysActive) + if (!m_JoystickToKeys && handler->m_PCJoystickState[joyId].JoystickToKeysActive) { TranslateAxes(joyId, 0); TranslateJoystickButtons(joyId, 0); - m_PCJoystickState[joyId].JoystickToKeysActive = false; + handler->m_PCJoystickState[joyId].JoystickToKeysActive = false; } } /*****************************************************************************/ -void JoystickHandlerDetails::UpdateJoysticks() +void BeebWin::UpdateJoysticks() { for (int idx = 0; idx < NUM_PC_JOYSTICKS; ++idx) { - if (m_PCJoystickState[0].Captured) + if (m_JoystickHandler->m_PCJoystickState[0].Captured) TranslateJoystick(idx); } } /*****************************************************************************/ // Look for file specific joystick map -void JoystickHandlerDetails::CheckForJoystickMap(const char *path) +void BeebWin::CheckForJoystickMap(const char *path) { char file[_MAX_PATH]; char drive[_MAX_DRIVE]; @@ -1037,16 +907,16 @@ void JoystickHandlerDetails::CheckForJoystickMap(const char *path) } /****************************************************************************/ -void JoystickHandlerDetails::ResetJoystickMap() +void BeebWin::ResetJoystickMap() { - if (MessageBox(GetHWnd(), "Clear joystick to keyboard mapping table?", WindowTitle, MB_YESNO | MB_ICONQUESTION) != IDYES) + if (MessageBox(m_hWnd, "Clear joystick to keyboard mapping table?", WindowTitle, MB_YESNO | MB_ICONQUESTION) != IDYES) return; ResetJoyMap(&JoystickMap); } /****************************************************************************/ -void JoystickHandlerDetails::ResetJoyMap(JoyMap* joymap) +void BeebWin::ResetJoyMap(JoyMap* joymap) { // Initialize all input to unassigned for (auto& mapping : *joymap) @@ -1059,11 +929,11 @@ void JoystickHandlerDetails::ResetJoyMap(JoyMap* joymap) } /****************************************************************************/ -void JoystickHandlerDetails::ResetJoyMapToDefaultUser(void) +void BeebWin::ResetJoyMapToDefaultUser(void) { char keymap[_MAX_PATH]; strcpy(keymap, "DefaultUser.jmap"); - m_BeebWin->GetDataPath(m_BeebWin->m_UserDataPath, keymap); + GetDataPath(m_UserDataPath, keymap); ResetJoyMap(&JoystickMap); if (GetFileAttributes(keymap) != INVALID_FILE_ATTRIBUTES) ReadJoyMap(keymap, &JoystickMap); @@ -1082,10 +952,10 @@ static void makeupper(char* str) } /****************************************************************************/ -bool JoystickHandlerDetails::ReadJoyMap(const char *filename, JoyMap *joymap) +bool BeebWin::ReadJoyMap(const char *filename, JoyMap *joymap) { bool success = true; - FILE *infile = m_BeebWin->OpenReadFile(filename, "key map", JOYMAP_TOKEN "\n"); + FILE *infile = OpenReadFile(filename, "joystick map", JOYMAP_TOKEN "\n"); char buf[256]; JoyMap newJoyMap; int line = 1; @@ -1115,7 +985,7 @@ bool JoystickHandlerDetails::ReadJoyMap(const char *filename, JoyMap *joymap) char errstr[500]; sprintf(errstr, "Invalid line in joystick mapping file:\n %s\n line %d\n", filename, line); - MessageBox(GetHWnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); + MessageBox(m_hWnd, errstr, WindowTitle, MB_OK | MB_ICONERROR); success = false; break; } @@ -1127,7 +997,7 @@ bool JoystickHandlerDetails::ReadJoyMap(const char *filename, JoyMap *joymap) char errstr[500]; sprintf(errstr, "Invalid input name in joystick mapping file:\n %s\n line %d\n", filename, line); - MessageBox(GetHWnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); + MessageBox(m_hWnd, errstr, WindowTitle, MB_OK | MB_ICONERROR); success = false; break; } @@ -1149,7 +1019,7 @@ bool JoystickHandlerDetails::ReadJoyMap(const char *filename, JoyMap *joymap) char errstr[500]; sprintf(errstr, "Invalid key name in joystick mapping file:\n %s\n line %d\n", filename, line); - MessageBox(GetHWnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); + MessageBox(m_hWnd, errstr, WindowTitle, MB_OK | MB_ICONERROR); success = false; break; } @@ -1179,7 +1049,7 @@ bool JoystickHandlerDetails::ReadJoyMap(const char *filename, JoyMap *joymap) char errstr[500]; sprintf(errstr, "Invalid key name in joystick mapping file:\n %s\n line %d\n", filename, line); - MessageBox(GetHWnd(), errstr, WindowTitle, MB_OK | MB_ICONERROR); + MessageBox(m_hWnd, errstr, WindowTitle, MB_OK | MB_ICONERROR); success = false; break; } @@ -1202,7 +1072,7 @@ bool JoystickHandlerDetails::ReadJoyMap(const char *filename, JoyMap *joymap) } /****************************************************************************/ -void JoystickHandlerDetails::LoadJoystickMap() +void BeebWin::LoadJoystickMap() { char DefaultPath[_MAX_PATH]; char FileName[_MAX_PATH]; @@ -1212,15 +1082,15 @@ void JoystickHandlerDetails::LoadJoystickMap() // otherwise start in user data path. if (m_AutoloadJoystickMap) { - m_BeebWin->GetPreferences().GetStringValue("DiscsPath", DefaultPath); - m_BeebWin->GetDataPath(m_BeebWin->GetUserDataPath(), DefaultPath); + m_Preferences.GetStringValue("DiscsPath", DefaultPath); + GetDataPath(GetUserDataPath(), DefaultPath); } else { - strcpy(DefaultPath, m_BeebWin->GetUserDataPath()); + strcpy(DefaultPath, GetUserDataPath()); } - FileDialog fileDialog(GetHWnd(), FileName, sizeof(FileName), DefaultPath, filter); + FileDialog fileDialog(m_hWnd, FileName, sizeof(FileName), DefaultPath, filter); if (m_JoystickMapPath[0] == '\0' && !m_AutoloadJoystickMap) { @@ -1239,9 +1109,9 @@ void JoystickHandlerDetails::LoadJoystickMap() } /****************************************************************************/ -bool JoystickHandlerDetails::WriteJoyMap(const char *filename, JoyMap *joymap) +bool BeebWin::WriteJoyMap(const char *filename, JoyMap *joymap) { - FILE* outfile = m_BeebWin->OpenWriteFile(filename, "joystick map"); + FILE* outfile = OpenWriteFile(filename, "joystick map"); if (outfile == NULL) return false; @@ -1301,7 +1171,7 @@ static bool hasFileExt(const char* fileName, const char* fileExt) } /****************************************************************************/ -void JoystickHandlerDetails::SaveJoystickMap() +void BeebWin::SaveJoystickMap() { char DefaultPath[_MAX_PATH]; // Add space for .jmap exstension @@ -1312,15 +1182,15 @@ void JoystickHandlerDetails::SaveJoystickMap() // otherwise start in user data path. if (m_AutoloadJoystickMap) { - m_BeebWin->GetPreferences().GetStringValue("DiscsPath", DefaultPath); - m_BeebWin->GetDataPath(m_BeebWin->GetUserDataPath(), DefaultPath); + m_Preferences.GetStringValue("DiscsPath", DefaultPath); + GetDataPath(GetUserDataPath(), DefaultPath); } else { - strcpy(DefaultPath, m_BeebWin->GetUserDataPath()); + strcpy(DefaultPath, GetUserDataPath()); } - FileDialog fileDialog(GetHWnd(), FileName, sizeof(FileName), DefaultPath, filter); + FileDialog fileDialog(m_hWnd, FileName, sizeof(FileName), DefaultPath, filter); if (m_JoystickMapPath[0] == '\0' && !m_AutoloadJoystickMap) { @@ -1341,23 +1211,8 @@ void JoystickHandlerDetails::SaveJoystickMap() } } -/* Backward compatibility */ -static const char *CFG_OPTIONS_STICKS = "Sticks"; - -/* New joystick options */ -static const char *CFG_OPTIONS_STICK_PCSTICK = "Stick%dPCStick"; -static const char *CFG_OPTIONS_STICK_PCAXES = "Stick%dPCAxes"; -static const char *CFG_OPTIONS_STICK_ANALOG = "Stick%dAnalogMousepad"; -static const char *CFG_OPTIONS_STICK_DIGITAL = "Stick%dDigitalMousepad"; -static const char* CFG_OPTIONS_JOYSTICK_GAIN = "JoystickGain"; -static const char* CFG_OPTIONS_JOYSTICK_ORDER = "JoystickOrder%d"; - -static const char *CFG_OPTIONS_STICKS_TO_KEYS = "SticksToKeys"; -static const char *CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP = "AutoloadJoystickMap"; -static const char *CFG_OPTIONS_STICKS_DEADBAND = "SticksToKeysDeadBand"; - /****************************************************************************/ -bool JoystickHandlerDetails::GetNthBoolValue(Preferences& preferences, const char* format, int idx, bool& value) +static bool GetNthBoolValue(Preferences& preferences, const char* format, int idx, bool& value) { char option_str[256]; sprintf_s(option_str, format, idx); @@ -1365,7 +1220,7 @@ bool JoystickHandlerDetails::GetNthBoolValue(Preferences& preferences, const cha } /****************************************************************************/ -bool JoystickHandlerDetails::GetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD& value) +static bool GetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD& value) { char option_str[256]; sprintf_s(option_str, format, idx); @@ -1373,7 +1228,7 @@ bool JoystickHandlerDetails::GetNthDWORDValue(Preferences& preferences, const ch } /****************************************************************************/ -bool JoystickHandlerDetails::GetNthStringValue(Preferences& preferences, const char* format, int idx, std::string& value) +static bool GetNthStringValue(Preferences& preferences, const char* format, int idx, std::string& value) { char option_str[256]; sprintf_s(option_str, format, idx); @@ -1381,7 +1236,7 @@ bool JoystickHandlerDetails::GetNthStringValue(Preferences& preferences, const c } /****************************************************************************/ -void JoystickHandlerDetails::SetNthBoolValue(Preferences& preferences, const char* format, int idx, bool value) +static void SetNthBoolValue(Preferences& preferences, const char* format, int idx, bool value) { char option_str[256]; sprintf_s(option_str, format, idx); @@ -1389,7 +1244,7 @@ void JoystickHandlerDetails::SetNthBoolValue(Preferences& preferences, const cha } /****************************************************************************/ -void JoystickHandlerDetails::SetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD value) +static void SetNthDWORDValue(Preferences& preferences, const char* format, int idx, DWORD value) { char option_str[256]; sprintf_s(option_str, format, idx); @@ -1397,7 +1252,7 @@ void JoystickHandlerDetails::SetNthDWORDValue(Preferences& preferences, const ch } /****************************************************************************/ -void JoystickHandlerDetails::SetNthStringValue(Preferences& preferences, const char* format, int idx, const std::string& value) +static void SetNthStringValue(Preferences& preferences, const char* format, int idx, const std::string& value) { char option_str[256]; sprintf_s(option_str, format, idx); @@ -1405,7 +1260,7 @@ void JoystickHandlerDetails::SetNthStringValue(Preferences& preferences, const c } /****************************************************************************/ -void JoystickHandlerDetails::EraseNthValue(Preferences& preferences, const char* format, int idx) +static void EraseNthValue(Preferences& preferences, const char* format, int idx) { char option_str[256]; sprintf_s(option_str, format, idx); @@ -1413,10 +1268,29 @@ void JoystickHandlerDetails::EraseNthValue(Preferences& preferences, const char* } /****************************************************************************/ -void JoystickHandlerDetails::ReadPreferences(Preferences& preferences) +void BeebWin::WriteJoystickOrder() +{ + auto& order = m_JoystickHandler->m_JoystickOrder; + for (UINT idx = 0; idx < MAX_JOYSTICK_ORDER; ++idx) + { + if (idx < order.size()) + { + SetNthStringValue(m_Preferences, CFG_OPTIONS_JOYSTICK_ORDER, idx + 1, + order[idx].to_string()); + } + else + { + EraseNthValue(m_Preferences, CFG_OPTIONS_JOYSTICK_ORDER, idx + 1); + } + } +} + +/****************************************************************************/ +void BeebWin::ReadJoystickPreferences() { DWORD dword; bool flag; + auto& handler = m_JoystickHandler; /* Clear joystick configuration */ for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) @@ -1434,13 +1308,15 @@ void JoystickHandlerDetails::ReadPreferences(Preferences& preferences) } /* Backward compatibility - "Sticks" contains MenuId */ - if (preferences.GetDWORDValue(CFG_OPTIONS_STICKS, dword)) + if (m_Preferences.GetDWORDValue(CFG_OPTIONS_STICKS, dword)) { + BBCJoystickConfig& config = m_JoystickConfig[0]; m_MenuIdSticks[0] = dword; - m_JoystickConfig[0].Enabled = true; - m_JoystickConfig[0].PCStick = MenuIdToStick(0, dword); - m_JoystickConfig[0].AnalogMousestick = (dword == JoystickMenuIds[0].AnalogMousestick); - m_JoystickConfig[0].DigitalMousestick = (dword == JoystickMenuIds[0].DigitalMousestick); + + config.Enabled = true; + config.PCStick = MenuIdToStick(0, dword); + config.AnalogMousestick = (dword == JoystickMenuIds[0].AnalogMousestick); + config.DigitalMousestick = (dword == JoystickMenuIds[0].DigitalMousestick); } /* New joystick options */ @@ -1448,149 +1324,83 @@ void JoystickHandlerDetails::ReadPreferences(Preferences& preferences) { BBCJoystickConfig& config = m_JoystickConfig[bbcIdx]; - if (GetNthBoolValue(preferences, CFG_OPTIONS_STICK_ANALOG, bbcIdx + 1, flag) && flag) + if (GetNthBoolValue(m_Preferences, CFG_OPTIONS_STICK_ANALOG, bbcIdx + 1, flag) && flag) { config.AnalogMousestick = true; config.Enabled = true; m_MenuIdSticks[bbcIdx] = JoystickMenuIds[bbcIdx].AnalogMousestick; } - else if (GetNthBoolValue(preferences, CFG_OPTIONS_STICK_DIGITAL, bbcIdx + 1, flag) && flag) + else if (GetNthBoolValue(m_Preferences, CFG_OPTIONS_STICK_DIGITAL, bbcIdx + 1, flag) && flag) { config.DigitalMousestick = true; config.Enabled = true; m_MenuIdSticks[bbcIdx] = JoystickMenuIds[bbcIdx].DigitalMousestick; } - else if (GetNthDWORDValue(preferences, CFG_OPTIONS_STICK_PCSTICK, bbcIdx + 1, dword) && dword != 0) + else if (GetNthDWORDValue(m_Preferences, CFG_OPTIONS_STICK_PCSTICK, bbcIdx + 1, dword) && dword != 0) { config.PCStick = dword; config.Enabled = true; m_MenuIdSticks[bbcIdx] = StickToMenuId(bbcIdx, dword); } - if (GetNthDWORDValue(preferences, CFG_OPTIONS_STICK_PCAXES, bbcIdx + 1, dword)) + if (GetNthDWORDValue(m_Preferences, CFG_OPTIONS_STICK_PCAXES, bbcIdx + 1, dword)) config.PCAxes = dword; m_MenuIdAxes[bbcIdx] = AxesToMenuId(bbcIdx, config.PCAxes); } - if (!preferences.GetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys)) + if (!m_Preferences.GetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys)) m_JoystickToKeys = false; - if (!preferences.GetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, - m_AutoloadJoystickMap)) + if (!m_Preferences.GetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, m_AutoloadJoystickMap)) m_AutoloadJoystickMap = false; - if (preferences.GetDWORDValue(CFG_OPTIONS_STICKS_DEADBAND, dword)) - m_Deadband = dword; + if (m_Preferences.GetDWORDValue(CFG_OPTIONS_STICKS_THRESHOLD, dword)) + m_JoystickToKeysThreshold = dword; else - m_Deadband = DEFAULT_JOY_DEADBAND; + m_JoystickToKeysThreshold = DEFAULT_JOYSTICK_THRESHOLD; - if (preferences.GetDWORDValue(CFG_OPTIONS_JOYSTICK_GAIN, dword)) - m_Gain = static_cast(dword) / 0x10000; + if (m_Preferences.GetDWORDValue(CFG_OPTIONS_JOYSTICK_SENSITIVITY, dword)) + m_JoystickSensitivity = static_cast(dword) / 0x10000; else - m_Gain = 1.0; + m_JoystickSensitivity = 1.0; - m_JoystickOrder.clear(); + handler->m_JoystickOrder.clear(); for (int idx = 0; idx < MAX_JOYSTICK_ORDER; ++idx) { std::string value; JoystickOrderEntry entry; - if (!GetNthStringValue(preferences, CFG_OPTIONS_JOYSTICK_ORDER, idx + 1, value) + if (!GetNthStringValue(m_Preferences, CFG_OPTIONS_JOYSTICK_ORDER, idx + 1, value) || !entry.from_string(value)) break; - m_JoystickOrder.push_back(entry); + handler->m_JoystickOrder.push_back(entry); } - } /****************************************************************************/ -void JoystickHandlerDetails::WritePreferences(Preferences& preferences) +void BeebWin::WriteJoystickPreferences() { for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) { BBCJoystickConfig& config = m_JoystickConfig[bbcIdx]; - SetNthBoolValue(preferences, CFG_OPTIONS_STICK_ANALOG, bbcIdx + 1, config.AnalogMousestick); - SetNthBoolValue(preferences, CFG_OPTIONS_STICK_DIGITAL, bbcIdx + 1, config.DigitalMousestick); - SetNthDWORDValue(preferences, CFG_OPTIONS_STICK_PCSTICK, bbcIdx + 1, config.PCStick); - SetNthDWORDValue(preferences, CFG_OPTIONS_STICK_PCAXES, bbcIdx + 1, config.PCAxes); + SetNthBoolValue(m_Preferences, CFG_OPTIONS_STICK_ANALOG, bbcIdx + 1, config.AnalogMousestick); + SetNthBoolValue(m_Preferences, CFG_OPTIONS_STICK_DIGITAL, bbcIdx + 1, config.DigitalMousestick); + SetNthDWORDValue(m_Preferences, CFG_OPTIONS_STICK_PCSTICK, bbcIdx + 1, config.PCStick); + SetNthDWORDValue(m_Preferences, CFG_OPTIONS_STICK_PCAXES, bbcIdx + 1, config.PCAxes); } - preferences.SetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys); - preferences.SetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, m_AutoloadJoystickMap); - preferences.SetDWORDValue(CFG_OPTIONS_STICKS_DEADBAND, m_Deadband); - preferences.SetDWORDValue(CFG_OPTIONS_JOYSTICK_GAIN, static_cast(m_Gain * 0x10000)); + m_Preferences.SetBoolValue(CFG_OPTIONS_STICKS_TO_KEYS, m_JoystickToKeys); + m_Preferences.SetBoolValue(CFG_OPTIONS_AUTOLOAD_JOYSICK_MAP, m_AutoloadJoystickMap); + m_Preferences.SetDWORDValue(CFG_OPTIONS_STICKS_THRESHOLD, m_JoystickToKeysThreshold); + m_Preferences.SetDWORDValue(CFG_OPTIONS_JOYSTICK_SENSITIVITY, static_cast(m_JoystickSensitivity * 0x10000)); /* Remove obsolete values */ - preferences.EraseValue(CFG_OPTIONS_STICKS); - preferences.EraseValue("Sticks2"); - preferences.EraseValue("JoystickAxes1"); - preferences.EraseValue("JoystickAxes2"); - preferences.EraseValue("Stick1ToKeysDeadBand"); - preferences.EraseValue("Stick2ToKeysDeadBand"); -} - -/****************************************************************************/ -void JoystickHandlerDetails::WriteJoystickOrder(Preferences& preferences) -{ - for (UINT idx = 0; idx < MAX_JOYSTICK_ORDER; ++idx) - { - if (idx < m_JoystickOrder.size()) - { - SetNthStringValue(preferences, CFG_OPTIONS_JOYSTICK_ORDER, idx + 1, - m_JoystickOrder[idx].to_string()); - } - else - { - EraseNthValue(preferences, CFG_OPTIONS_JOYSTICK_ORDER, idx + 1); - } - } + m_Preferences.EraseValue(CFG_OPTIONS_STICKS); + m_Preferences.EraseValue("Sticks2"); + m_Preferences.EraseValue("JoystickAxes1"); + m_Preferences.EraseValue("JoystickAxes2"); + m_Preferences.EraseValue("Stick1ToKeysDeadBand"); + m_Preferences.EraseValue("Stick2ToKeysDeadBand"); } - -/****************************************************************************/ -JoystickHandler::JoystickHandler(BeebWin* beebWin) : m_Details{ std::make_unique(beebWin) } {} - -JoystickHandler::~JoystickHandler() {} - -int JoystickHandler::GetMenuIdSticks(int bbcIdx) { return m_Details->GetMenuIdSticks(bbcIdx); } - -bool JoystickHandler::GetJoystickToKeys() { return m_Details->GetJoystickToKeys(); } - -void JoystickHandler::SetJoystickToKeys(bool enabled) { m_Details->SetJoystickToKeys(enabled); } - -void JoystickHandler::SetJoystickTarget(HWND target) { m_Details->SetJoystickTarget(target); } - -void JoystickHandler::InitMenu(void) { m_Details->InitMenu(); } - -void JoystickHandler::ProcessMenuCommand(int bbcIdx, UINT menuId) { m_Details->ProcessMenuCommand(bbcIdx, menuId); } - -void JoystickHandler::ProcessAxesMenuCommand(int bbcIdx, UINT menuId) { m_Details->ProcessAxesMenuCommand(bbcIdx, menuId); } - -void JoystickHandler::ToggleJoystickToKeys() { m_Details->ToggleJoystickToKeys(); } - -void JoystickHandler::ToggleAutoloadJoystickMap() { m_Details->ToggleAutoloadJoystickMap(); } - -bool JoystickHandler::InitJoystick(bool verbose) { return m_Details->InitJoystick(verbose); } - -void JoystickHandler::SetMousestickButton(int index, bool button) { m_Details->SetMousestickButton(index, button); } - -void JoystickHandler::ScaleMousestick(unsigned int x, unsigned int y) { m_Details->ScaleMousestick(x, y); } - -void JoystickHandler::UpdateJoysticks(void) { m_Details->UpdateJoysticks(); } - -void JoystickHandler::CheckForJoystickMap(const char* path) { m_Details->CheckForJoystickMap(path); } - -void JoystickHandler::ResetJoystickMap(void) { m_Details->ResetJoystickMap(); } - -void JoystickHandler::ResetJoyMapToDefaultUser(void) { m_Details->ResetJoyMapToDefaultUser(); } - -void JoystickHandler::LoadJoystickMap(void) { m_Details->LoadJoystickMap(); } - -void JoystickHandler::SaveJoystickMap(void) { m_Details->SaveJoystickMap(); } - -void JoystickHandler::ReadPreferences(Preferences& preferences) { m_Details->ReadPreferences(preferences); } - -void JoystickHandler::WritePreferences(Preferences& preferences) { m_Details->WritePreferences(preferences); } - -void JoystickHandler::WriteJoystickOrder(Preferences& preferences) { m_Details->WriteJoystickOrder(preferences); } diff --git a/Src/JoystickHandler.h b/Src/JoystickHandler.h index 13e9c8b5..b0ed9997 100644 --- a/Src/JoystickHandler.h +++ b/Src/JoystickHandler.h @@ -25,91 +25,81 @@ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ****************************************************************/ -#ifndef BEEBWINJOYSTICK_H -#define BEEBWINJOYSTICK_H +#ifndef JOYSTICKHANDLER_H +#define JOYSTICKHANDLER_H -#include +#include + +#include "beebwin.h" /* Max number of joysticks in joystickapi */ #define MAX_JOYSTICK_DEVS 16 -/* Max number of joysticks to capture */ -#define NUM_PC_JOYSTICKS 4 /* Max number of entries on joystick order list */ #define MAX_JOYSTICK_ORDER 16 -#define JOYSTICK_MAX_AXES 16 -#define JOYSTICK_MAX_BTNS 16 - -#define JOYSTICK_AXIS_UP 0 -#define JOYSTICK_AXIS_DOWN 1 -#define JOYSTICK_AXIS_LEFT 2 -#define JOYSTICK_AXIS_RIGHT 3 -#define JOYSTICK_AXIS_Z_N 4 -#define JOYSTICK_AXIS_Z_P 5 -#define JOYSTICK_AXIS_R_N 6 -#define JOYSTICK_AXIS_R_P 7 -#define JOYSTICK_AXIS_U_N 8 -#define JOYSTICK_AXIS_U_P 9 -#define JOYSTICK_AXIS_V_N 10 -#define JOYSTICK_AXIS_V_P 11 -#define JOYSTICK_AXIS_HAT_UP 12 -#define JOYSTICK_AXIS_HAT_DOWN 13 -#define JOYSTICK_AXIS_HAT_LEFT 14 -#define JOYSTICK_AXIS_HAT_RIGHT 15 -#define JOYSTICK_AXES_COUNT 16 - -#define BEEB_VKEY_JOY_START 256 -#define BEEB_VKEY_JOY_COUNT (NUM_PC_JOYSTICKS * \ - (JOYSTICK_MAX_AXES + JOYSTICK_MAX_BTNS)) // 4*32 = 128 -#define BEEB_VKEY_JOY_END (BEEB_VKEY_JOY_START + BEEB_VKEY_JOY_COUNT) // 256+128 = 384 - -#define BEEB_VKEY_COUNT BEEB_VKEY_JOY_END - -class BeebWin; -class JoystickHandlerDetails; - -class JoystickHandler +struct JoystickId : std::pair +{ + using std::pair::pair; + + // Manufacturer ID aka Vendor ID + int& mId() { return first; } + // Product ID + int& pId() { return second; } +}; + +struct JoystickOrderEntry : JoystickId +{ + std::string Name{}; + int JoyIndex{ -1 }; + + JoystickOrderEntry() = default; + JoystickOrderEntry(JoystickId id, const std::string& name, int joyIndex) : + JoystickId(id), Name(name), JoyIndex(joyIndex) {} + JoystickOrderEntry(int mid, int pid, const std::string& name) : + JoystickId(mid, pid), Name(name) {} + + std::string to_string(); + bool from_string(const std::string&); +}; + +struct JoystickDev { - std::unique_ptr m_Details; - -public: - JoystickHandler(BeebWin* beebWin); - ~JoystickHandler(); - - /* Accessors */ - int GetMenuIdSticks(int bbcIdx); - bool GetJoystickToKeys(); - void SetJoystickToKeys(bool enabled); - void SetJoystickTarget(HWND target); - - /* Menu handling - will soon be gone */ - void InitMenu(void); - void ProcessMenuCommand(int bbcIdx, UINT menuId); - void ProcessAxesMenuCommand(int bbcIdx, UINT menuId); - void ToggleJoystickToKeys(); - void ToggleAutoloadJoystickMap(); - - /* Initialization */ - bool InitJoystick(bool verbose = false); - - /* Mousestick */ - void SetMousestickButton(int index, bool button); - void ScaleMousestick(unsigned int x, unsigned int y); - - /* Timer handler */ - void UpdateJoysticks(void); - - /* Joystick to keyboard mapping */ - void CheckForJoystickMap(const char* path); - void ResetJoystickMap(void); - void ResetJoyMapToDefaultUser(void); - void LoadJoystickMap(void); - void SaveJoystickMap(void); - - /* Preferences */ - void ReadPreferences(Preferences& preferences); - void WritePreferences(Preferences& preferences); - void WriteJoystickOrder(Preferences& preferences); + JOYCAPS Caps{}; + JOYINFOEX InfoEx{}; + int Instance{ 0 }; + int Order{ -1 }; + int JoyIndex{ -1 }; + bool Configured{ false }; + bool Present{ false }; + + JoystickId Id() { return JoystickId{ Caps.wMid, Caps.wPid }; } + std::string DisplayString(); + bool Update(); + DWORD GetButtons(); + DWORD GetAxesState(int threshold); + void GetAxesValue(int axesSet, int& x, int& y); +}; + +struct PCJoystickState +{ + JoystickDev* Dev{ nullptr }; + int JoyIndex{ -1 }; + bool Captured{ false }; + unsigned int PrevAxes{ 0 }; + unsigned int PrevBtns{ 0 }; + bool JoystickToKeysActive{ false }; +}; + +struct JoystickHandler +{ + JoystickDev m_JoystickDevs[MAX_JOYSTICK_DEVS]; + PCJoystickState m_PCJoystickState[NUM_PC_JOYSTICKS]; + std::vector m_JoystickOrder; + + void ScanJoysticks(void); + + JoystickHandler() {} + ~JoystickHandler() {} }; #endif diff --git a/Src/SelectKeyDialog.cpp b/Src/SelectKeyDialog.cpp index 0267d53a..6d37b292 100644 --- a/Src/SelectKeyDialog.cpp +++ b/Src/SelectKeyDialog.cpp @@ -121,12 +121,12 @@ INT_PTR SelectKeyDialog::DlgProc( if (LOWORD(wParam) == WA_INACTIVE) { hCurrentDialog = nullptr; - mainWin->m_Joysticks.SetJoystickTarget(nullptr); + mainWin->SetJoystickTarget(nullptr); } else { hCurrentDialog = m_hwnd; - mainWin->m_Joysticks.SetJoystickTarget(m_hwnd); + mainWin->SetJoystickTarget(m_hwnd); hCurrentAccelTable = nullptr; } break; diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index e541f43c..d787920f 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -136,7 +136,6 @@ static JoyMap *joystickTable = &JoystickMap; /****************************************************************************/ BeebWin::BeebWin() - : m_Joysticks(this) { m_DXInit = false; m_hWnd = NULL; @@ -323,7 +322,7 @@ bool BeebWin::Initialise() ReadROMFile(RomFile, RomConfig); ApplyPrefs(); - m_Joysticks.CheckForJoystickMap(m_CommandLineFileName1); + CheckForJoystickMap(m_CommandLineFileName1); if (m_DebugScript[0] != '\0') { @@ -390,7 +389,7 @@ void BeebWin::ApplyPrefs() GetDataPath(m_UserDataPath, keymap); ReadKeyMap(keymap, &defaultMapping); - m_Joysticks.ResetJoyMapToDefaultUser(); + ResetJoyMapToDefaultUser(); InitMenu(); ShowMenu(true); @@ -409,7 +408,7 @@ void BeebWin::ApplyPrefs() PrinterDisable(); /* Joysticks can only be initialised after the window is created (needs hwnd) */ - m_Joysticks.InitJoystick(false); + InitJoystick(false); LoadFDC(NULL, true); RTCInit(); @@ -882,6 +881,16 @@ void BeebWin::EnableMenuItem(UINT id, bool enabled) ::EnableMenuItem(m_hMenu, id, enabled ? MF_ENABLED : MF_GRAYED); } +void BeebWin::SetMenuItemText(UINT id, const std::string& text) +{ + MENUITEMINFO mii{ 0 }; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_STRING; + mii.fType = MFT_STRING; + mii.dwTypeData = const_cast(text.c_str()); + SetMenuItemInfo(m_hMenu, id, FALSE, &mii); +} + void BeebWin::InitMenu(void) { char menu_string[256]; @@ -1104,7 +1113,7 @@ void BeebWin::InitMenu(void) CheckMenuItem(IDM_AUTOSAVE_PREFS_CMOS, m_AutoSavePrefsCMOS); CheckMenuItem(IDM_AUTOSAVE_PREFS_FOLDERS, m_AutoSavePrefsFolders); CheckMenuItem(IDM_AUTOSAVE_PREFS_ALL, m_AutoSavePrefsAll); - m_Joysticks.InitMenu(); + InitJoystickMenu(); } void BeebWin::UpdateDisplayRendererMenu() { @@ -1617,7 +1626,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle mainWin->m_RelMousePos.y = mainWin->m_YWinSize; } - mainWin->m_Joysticks.ScaleMousestick(mainWin->m_RelMousePos.x, + mainWin->ScaleMousestick(mainWin->m_RelMousePos.x, mainWin->m_RelMousePos.y); mainWin->ChangeAMXPosition(xDelta, yDelta); @@ -1630,7 +1639,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle int xPos = GET_X_LPARAM(lParam); int yPos = GET_Y_LPARAM(lParam); - mainWin->m_Joysticks.ScaleMousestick(xPos, yPos); + mainWin->ScaleMousestick(xPos, yPos); mainWin->SetAMXPosition(xPos, yPos); // Experiment: show menu in full screen when cursor moved to top of window @@ -1645,13 +1654,13 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle } else { - mainWin->m_Joysticks.SetMousestickButton(0, (uParam & MK_LBUTTON) != 0); + mainWin->SetMousestickButton(0, (uParam & MK_LBUTTON) != 0); AMXButtons |= AMX_LEFT_BUTTON; } break; case WM_LBUTTONUP: - mainWin->m_Joysticks.SetMousestickButton(0, (uParam & MK_LBUTTON) != 0); + mainWin->SetMousestickButton(0, (uParam & MK_LBUTTON) != 0); AMXButtons &= ~AMX_LEFT_BUTTON; break; @@ -1664,12 +1673,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle break; case WM_RBUTTONDOWN: - mainWin->m_Joysticks.SetMousestickButton(1, (uParam & MK_RBUTTON) != 0); + mainWin->SetMousestickButton(1, (uParam & MK_RBUTTON) != 0); AMXButtons |= AMX_RIGHT_BUTTON; break; case WM_RBUTTONUP: - mainWin->m_Joysticks.SetMousestickButton(1, (uParam & MK_RBUTTON) != 0); + mainWin->SetMousestickButton(1, (uParam & MK_RBUTTON) != 0); AMXButtons &= ~AMX_RIGHT_BUTTON; break; @@ -1711,7 +1720,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, // window handle } else if (uParam == 3) { - mainWin->m_Joysticks.UpdateJoysticks(); + mainWin->UpdateJoysticks(); } break; @@ -2812,7 +2821,7 @@ void BeebWin::HandleCommand(int MenuId) { // Also switch on analogue mousestick (touch screen uses // mousestick position) - if (m_Joysticks.GetMenuIdSticks(0) != IDM_ANALOGUE_MOUSESTICK) + if (!GetAnalogMousestick(0)) HandleCommand(IDM_ANALOGUE_MOUSESTICK); if (EthernetPortEnabled) @@ -3265,38 +3274,38 @@ void BeebWin::HandleCommand(int MenuId) case IDM_ANALOGUE_MOUSESTICK: case IDM_DIGITAL_MOUSESTICK: case IDM_JOY1_PCJOY2: - m_Joysticks.ProcessMenuCommand(0, MenuId); + ProcessJoystickMenuCommand(0, MenuId); break; case IDM_JOY2_PCJOY1: case IDM_JOY2_ANALOGUE_MOUSESTICK: case IDM_JOY2_DIGITAL_MOUSESTICK: case IDM_JOY2_PCJOY2: - m_Joysticks.ProcessMenuCommand(1, MenuId); + ProcessJoystickMenuCommand(1, MenuId); break; case IDM_JOY1_PRIMARY: case IDM_JOY1_SECONDARY1: case IDM_JOY1_SECONDARY2: - m_Joysticks.ProcessAxesMenuCommand(0, MenuId); + ProcessJoystickAxesMenuCommand(0, MenuId); break; case IDM_JOY2_PRIMARY: case IDM_JOY2_SECONDARY1: case IDM_JOY2_SECONDARY2: - m_Joysticks.ProcessAxesMenuCommand(1, MenuId); + ProcessJoystickAxesMenuCommand(1, MenuId); break; case IDM_INIT_JOYSTICK: - m_Joysticks.InitJoystick(true); + InitJoystick(true); break; case IDM_JOYSTICK_TO_KEYS: - m_Joysticks.ToggleJoystickToKeys(); + ProcessJoystickToKeysCommand(); break; case IDM_AUTOLOADJOYMAP: - m_Joysticks.ToggleAutoloadJoystickMap(); + ProcessAutoloadJoystickMapCommand(); break; case IDM_FREEZEINACTIVE: @@ -3323,15 +3332,15 @@ void BeebWin::HandleCommand(int MenuId) break; case IDM_LOADJOYMAP: - m_Joysticks.LoadJoystickMap(); + LoadJoystickMap(); break; case IDM_SAVEJOYMAP: - m_Joysticks.SaveJoystickMap(); + SaveJoystickMap(); break; case IDM_RESETJOYMAP: - m_Joysticks.ResetJoystickMap(); + ResetJoystickMap(); break; case IDM_DEFINEKEYMAP: @@ -4119,7 +4128,7 @@ void BeebWin::OpenUserKeyboardDialog() m_WasPaused = mainWin->IsPaused(); - m_JoystickToKeysWasEnabled = m_Joysticks.GetJoystickToKeys(); + m_JoystickToKeysWasEnabled = GetJoystickToKeys(); if (!m_WasPaused) { @@ -4141,12 +4150,12 @@ void BeebWin::OpenJoystickMapDialog() } // Enable mapping to capture keys in dialog - m_JoystickToKeysWasEnabled = m_Joysticks.GetJoystickToKeys(); + m_JoystickToKeysWasEnabled = GetJoystickToKeys(); if (!m_JoystickToKeysWasEnabled) { - m_Joysticks.SetJoystickToKeys(true); - m_Joysticks.InitJoystick(false); + SetJoystickToKeys(true); + InitJoystick(false); } UserKeyboardDialog(m_hWnd, true); @@ -4163,10 +4172,10 @@ void BeebWin::UserKeyboardDialogClosed() } // Restore joystick state - if (m_Joysticks.GetJoystickToKeys() && !m_JoystickToKeysWasEnabled) + if (GetJoystickToKeys() && !m_JoystickToKeysWasEnabled) { - m_Joysticks.SetJoystickToKeys(m_JoystickToKeysWasEnabled); - m_Joysticks.InitJoystick(false); + SetJoystickToKeys(m_JoystickToKeysWasEnabled); + InitJoystick(false); } // Unmute @@ -4345,7 +4354,7 @@ void BeebWin::CheckForLocalPrefs(const char *path, bool bLoadPrefs) HandleCommand(m_DisplayRenderer); InitMenu(); SetWindowText(m_hWnd, WindowTitle); - m_Joysticks.InitJoystick(false); + InitJoystick(false); } } @@ -4366,7 +4375,7 @@ void BeebWin::CheckForLocalPrefs(const char *path, bool bLoadPrefs) if (bLoadPrefs) { - m_Joysticks.CheckForJoystickMap(path); + CheckForJoystickMap(path); } } diff --git a/Src/beebwin.h b/Src/beebwin.h index 4dc1d548..84a24fc7 100644 --- a/Src/beebwin.h +++ b/Src/beebwin.h @@ -32,6 +32,7 @@ Boston, MA 02110-1301, USA. #include #include #include +#include #include #include @@ -43,7 +44,7 @@ Boston, MA 02110-1301, USA. #include "port.h" #include "preferences.h" #include "video.h" -#include "JoystickHandler.h" +#include "atodconv.h" /* Used in message boxes */ #define GETHWND (mainWin->GethWnd()) @@ -52,6 +53,40 @@ Boston, MA 02110-1301, USA. #define CFG_KEYBOARD_LAYOUT "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout" #define CFG_SCANCODE_MAP "Scancode Map" +// Max number of joysticks to capture +#define NUM_PC_JOYSTICKS 4 + +// Default Joystick to keyboard threshold +#define DEFAULT_JOYSTICK_THRESHOLD 4096 + +#define JOYSTICK_MAX_AXES 16 +#define JOYSTICK_MAX_BTNS 16 + +#define JOYSTICK_AXIS_UP 0 +#define JOYSTICK_AXIS_DOWN 1 +#define JOYSTICK_AXIS_LEFT 2 +#define JOYSTICK_AXIS_RIGHT 3 +#define JOYSTICK_AXIS_Z_N 4 +#define JOYSTICK_AXIS_Z_P 5 +#define JOYSTICK_AXIS_R_N 6 +#define JOYSTICK_AXIS_R_P 7 +#define JOYSTICK_AXIS_U_N 8 +#define JOYSTICK_AXIS_U_P 9 +#define JOYSTICK_AXIS_V_N 10 +#define JOYSTICK_AXIS_V_P 11 +#define JOYSTICK_AXIS_HAT_UP 12 +#define JOYSTICK_AXIS_HAT_DOWN 13 +#define JOYSTICK_AXIS_HAT_LEFT 14 +#define JOYSTICK_AXIS_HAT_RIGHT 15 +#define JOYSTICK_AXES_COUNT 16 + +#define BEEB_VKEY_JOY_START 256 +#define BEEB_VKEY_JOY_COUNT (NUM_PC_JOYSTICKS * \ + (JOYSTICK_MAX_AXES + JOYSTICK_MAX_BTNS)) // 4*32 = 128 +#define BEEB_VKEY_JOY_END (BEEB_VKEY_JOY_START + BEEB_VKEY_JOY_COUNT) // 256+128 = 384 + +#define BEEB_VKEY_COUNT BEEB_VKEY_JOY_END + struct KeyMapping { int row; // Beeb row int col; // Beeb col @@ -132,6 +167,24 @@ enum class MessageType { Info }; +struct BBCJoystickConfig +{ + bool Enabled{ false }; + int PCStick{ 0 }; + int PCAxes{ 0 }; + bool AnalogMousestick{ false }; + bool DigitalMousestick{ false }; +}; + +struct JoystickHandler; + +struct JoystickHandlerPtr : std::unique_ptr +{ + // Delegate constructor and destructor + JoystickHandlerPtr(); + ~JoystickHandlerPtr(); +}; + class BeebWin { public: @@ -218,6 +271,18 @@ class BeebWin { bool IsWindowMinimized() const; void DisplayClientAreaText(HDC hdc); void DisplayFDCBoardInfo(HDC hDC, int x, int y); + void SetJoystickTarget(HWND target); + void SetJoystickButton(int index, bool value); + void ScaleJoystick(int index, unsigned int x, unsigned int y, + unsigned int minX, unsigned int minY, + unsigned int maxX, unsigned int maxY); + void SetMousestickButton(int index, bool button); + void ScaleMousestick(unsigned int x, unsigned int y); + void UpdateJoysticks(void); + void TranslateJoystick(int joyId); + void TranslateOrSendKey(int vkey, bool keyUp); + void TranslateAxes(int joyId, unsigned int axesState); + void TranslateJoystickButtons(int joyId, unsigned int buttons); void HandleCommand(int MenuId); void SetAMXPosition(unsigned int x, unsigned int y); void ChangeAMXPosition(int deltaX, int deltaY); @@ -314,7 +379,17 @@ class BeebWin { int m_MenuIdVolume; int m_MenuIdTiming; int m_FPSTarget; - JoystickHandler m_Joysticks; + JoystickHandlerPtr m_JoystickHandler; + bool m_JoystickTimerRunning{ false }; + int m_MenuIdSticks[NUM_BBC_JOYSTICKS]{}; + int m_MenuIdAxes[NUM_BBC_JOYSTICKS]{}; + BBCJoystickConfig m_JoystickConfig[NUM_BBC_JOYSTICKS]; + int m_JoystickToKeysThreshold{ DEFAULT_JOYSTICK_THRESHOLD }; + double m_JoystickSensitivity{ 1.0 }; + bool m_JoystickToKeys{ false }; + bool m_AutoloadJoystickMap{ false }; + HWND m_JoystickTarget{ nullptr }; + char m_JoystickMapPath[_MAX_PATH]{}; bool m_HideCursor; bool m_CaptureMouse; bool m_MouseCaptured; @@ -490,8 +565,25 @@ class BeebWin { void UpdateSoundStreamerMenu(); + void InitJoystickMenu(void); + void UpdateJoystickMenu(void); + void ProcessJoystickMenuCommand(int bbcIdx, UINT menuId); + void ProcessJoystickAxesMenuCommand(int bbcIdx, UINT menuId); + void ProcessJoystickToKeysCommand(); + void ProcessAutoloadJoystickMapCommand(); + + void UpdateJoystickConfig(int bbcIdx); + bool GetAnalogMousestick(int bbcIdx); + bool GetDigitalMousestick(int bbcIdx); + int GetPCJoystick(int bbcIdx); + bool IsPCJoystickOn(int pcIdx); + + bool GetJoystickToKeys() { return m_JoystickToKeys; } + void SetJoystickToKeys(bool value) { m_JoystickToKeys = value; } + void CheckMenuItem(UINT id, bool checked); void EnableMenuItem(UINT id, bool enabled); + void SetMenuItemText(UINT id, const std::string& text); // DirectX - calls DDraw or DX9 fn void InitDX(void); @@ -520,6 +612,9 @@ class BeebWin { int ReadDisc(int Drive, bool bCheckForPrefs); void Load1770DiscImage(const char *FileName, int Drive, DiscType Type); void LoadTape(void); + bool InitJoystick(bool verbose = false); + bool CaptureJoystick(int Index, bool verbose); + void ResetJoystick(void); void RestoreState(void); void SaveState(void); void NewDiscImage(int Drive); @@ -562,6 +657,15 @@ class BeebWin { bool ReadKeyMap(const char *filename, KeyMap *keymap); bool WriteKeyMap(const char *filename, KeyMap *keymap); + void CheckForJoystickMap(const char* path); + void ResetJoystickMap(void); + void ResetJoyMap(JoyMap* joymap); + void ResetJoyMapToDefaultUser(void); + bool ReadJoyMap(const char* filename, JoyMap* joymap); + bool WriteJoyMap(const char* filename, JoyMap* joymap); + void LoadJoystickMap(void); + void SaveJoystickMap(void); + void Report(MessageType type, const char *format, ...); bool RegCreateKey(HKEY hKeyRoot, LPCSTR lpSubKey); @@ -574,7 +678,9 @@ class BeebWin { // Preferences void LoadPreferences(); void SavePreferences(bool saveAll); - Preferences& GetPreferences() { return m_Preferences; } + void ReadJoystickPreferences(); + void WriteJoystickPreferences(); + void WriteJoystickOrder(); private: enum class PaletteType : char { diff --git a/Src/beebwinio.cpp b/Src/beebwinio.cpp index f8bc5604..46154764 100644 --- a/Src/beebwinio.cpp +++ b/Src/beebwinio.cpp @@ -303,7 +303,7 @@ void BeebWin::LoadTape(void) LoadCSWTape(FileName); } - m_Joysticks.CheckForJoystickMap(FileName); + CheckForJoystickMap(FileName); } } diff --git a/Src/beebwinprefs.cpp b/Src/beebwinprefs.cpp index dee6b5d1..b845e786 100644 --- a/Src/beebwinprefs.cpp +++ b/Src/beebwinprefs.cpp @@ -252,7 +252,7 @@ void BeebWin::LoadPreferences() if (!m_Preferences.GetBoolValue("Music5000Enabled", Music5000Enabled)) Music5000Enabled = false; - m_Joysticks.ReadPreferences(m_Preferences); + ReadJoystickPreferences(); if (!m_Preferences.GetBoolValue(CFG_OPTIONS_FREEZEINACTIVE, m_FreezeWhenInactive)) m_FreezeWhenInactive = true; @@ -717,7 +717,7 @@ void BeebWin::SavePreferences(bool saveAll) m_Preferences.SetDWORDValue("BitmapCaptureResolution", m_MenuIdCaptureResolution); m_Preferences.SetDWORDValue("BitmapCaptureFormat", m_MenuIdCaptureFormat); - m_Joysticks.WritePreferences(m_Preferences); + WriteJoystickPreferences(); RECT rect; GetWindowRect(m_hWnd,&rect); @@ -736,7 +736,7 @@ void BeebWin::SavePreferences(bool saveAll) m_Preferences.SetBoolValue("WriteInstructionCounts", m_WriteInstructionCounts); - m_Joysticks.WriteJoystickOrder(m_Preferences); + WriteJoystickOrder(); if (m_Preferences.Save(m_PrefsFile) == Preferences::Result::Success) { m_AutoSavePrefsChanged = false; From e5f2e80a78e53477a44867891e8dc57535d2ca9e Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Sat, 27 Feb 2021 23:25:58 +0100 Subject: [PATCH 30/38] Switched from WinMM to DirectInput - Autodetect secondary thumbstick axes - Display joystick names in menu - More user friendly axis names - Map right thumbstick axes Z/RZ to RX/RY in joystick to keyboard mapping if those axes are not present to make mapping files more portable - Changed axes order, old mapping files may need to be updated --- Documents/JoystickMap.txt | 44 ++-- Src/BeebEm.rc | 6 +- Src/BeebEm.vcxproj | 8 +- Src/JoystickHandler.cpp | 465 +++++++++++++++++++++++++++++--------- Src/JoystickHandler.h | 109 +++++---- Src/SelectKeyDialog.cpp | 72 ++++-- Src/beebemrc.h | 2 - Src/beebwin.cpp | 6 +- Src/beebwin.h | 31 +-- 9 files changed, 530 insertions(+), 213 deletions(-) diff --git a/Documents/JoystickMap.txt b/Documents/JoystickMap.txt index 9cc12e78..77ebf90b 100644 --- a/Documents/JoystickMap.txt +++ b/Documents/JoystickMap.txt @@ -33,26 +33,26 @@ # joystick or gamepad models. # Xbox 360 controller has following assignments -# (with JOYINFOEX fields in the middle column): -# Up, Down, Left, Right - dwY, dwX - Left Analog Up, Down, Left, Right -# Axis3+,Axis3- - dwZ - Left Trigger, Right Trigger -# Axis4-,Axis4+,Axis5-,Axis5+ - dwR, dwU - Right Analog Up, Down, Left, Right -# Axis7-,Axis7+,Axis8-,Axis8+ - dwPOV - Hat Up, Down, Left, Right -# Btn1-4 - A, B, X, Y -# Btn5,6 - Left Shoulder, Right Shoulder -# Btn7,8 - Select, Start -# Btn9,10 - Left Thumbstick, Right Thumbstick +# (with DIJOYSTATE2 fields in the middle column): +# Left, Right, Up, Down - lX, lY - Left Analog Left, Right, Up, Down +# Z+,Z- - lZ - Left Trigger, Right Trigger +# RLeft, RRight, RUp, RDown - lRx, lRy - Right Analog Left, Right, Up, Down +# HatLeft, HatRight, HatUp, HatDown - rgdwPOV[0] - Hat Up, Down, Left, Right +# Btn1-4 - A, B, X, Y +# Btn5,6 - Left Shoulder, Right Shoulder +# Btn7,8 - Back, Start +# Btn9,10 - Left Thumbstick, Right Thumbstick # Other common layout: -# Up, Down, Left, Right - dwY, dwX - Left Analog Up, Down, Left, Right -# Axis4-,Axis4+,Axis3-,Axis3+ - dwR, dwZ - Right Analog Up, Down, Left, Right -# Axis7-,Axis7+,Axis8-,Axis8+ - dwPOV - Hat Up, Down, Left, Right -# Btn1-4 - Y, B, A, X -# Btn5,6 - Left Shoulder, Right Shoulder -# Btn7,8 - Left Trigger, Right Trigger -# Btn9,10 - Select, Start -# Btn11,12 - Left Thumbstick, Right Thumbstick -# Btn12 - Home +# Left, Right, Up, Down - lX, lY - Left Analog Left, Right, Up, Down +# RLeft, RRight, RUp, RDown - lZ, lRz - Right Analog Left, Right, Up, Down +# HatLeft, HatRight, HatUp, HatDown - rgdwPOV[0] - Hat Up, Down, Left, Right +# Btn1-4 - Y, B, A, X +# Btn5,6 - Left Shoulder, Right Shoulder +# Btn7,8 - Left Trigger, Right Trigger +# Btn9,10 - Select/Back, Start +# Btn11,12 - Left Thumbstick, Right Thumbstick +# Btn13 - Home # Sample mapping for Chuckie Egg @@ -60,10 +60,10 @@ Joy1Up A Joy1Down Z Joy1Left , Joy1Right . -Joy1Axis7- A -Joy1Axis7+ Z -Joy1Axis8- , -Joy1Axis8+ . +Joy1HatUp A +Joy1HatDown Z +Joy1HatLeft , +Joy1HatRight . Joy1Btn1 SPACE Joy1Btn6 RETURN Joy1Btn7 1 diff --git a/Src/BeebEm.rc b/Src/BeebEm.rc index fc72239c..f04f03b9 100644 --- a/Src/BeebEm.rc +++ b/Src/BeebEm.rc @@ -717,8 +717,7 @@ BEGIN MENUITEM "&Digital Mousestick", IDM_DIGITAL_MOUSESTICK MENUITEM SEPARATOR MENUITEM "Primary Stick", IDM_JOY1_PRIMARY - MENUITEM "Right Thumbstick (XBox)", IDM_JOY1_SECONDARY1 - MENUITEM "Right Thumbstick (other)", IDM_JOY1_SECONDARY2 + MENUITEM "Right Thumbstick", IDM_JOY1_SECONDARY1 END POPUP "Second Joystick" BEGIN @@ -728,8 +727,7 @@ BEGIN MENUITEM "&Digital Mousestick", IDM_JOY2_DIGITAL_MOUSESTICK MENUITEM SEPARATOR MENUITEM "Primary Stick", IDM_JOY2_PRIMARY - MENUITEM "Right Thumbstick (XBox)", IDM_JOY2_SECONDARY1 - MENUITEM "Right Thumbstick (other)", IDM_JOY2_SECONDARY2 + MENUITEM "Right Thumbstick", IDM_JOY2_SECONDARY1 END POPUP "Joystick To Keyboard" BEGIN diff --git a/Src/BeebEm.vcxproj b/Src/BeebEm.vcxproj index 857e2d6a..3d326eeb 100644 --- a/Src/BeebEm.vcxproj +++ b/Src/BeebEm.vcxproj @@ -104,7 +104,7 @@ true - Comctl32.lib;d3d9.lib;dsound.lib;Gdiplus.lib;Shlwapi.lib;Vfw32.lib;winmm.lib;Ws2_32.lib;%(AdditionalDependencies) + Comctl32.lib;d3d9.lib;dsound.lib;dinput8.lib;Gdiplus.lib;Shlwapi.lib;Vfw32.lib;Ws2_32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) @@ -120,7 +120,7 @@ Windows true - Comctl32.lib;d3d9.lib;dsound.lib;Gdiplus.lib;Shlwapi.lib;Vfw32.lib;winmm.lib;Ws2_32.lib;%(AdditionalDependencies) + Comctl32.lib;d3d9.lib;dsound.lib;dinput8.lib;Gdiplus.lib;Shlwapi.lib;Vfw32.lib;Ws2_32.lib;%(AdditionalDependencies) @@ -142,7 +142,7 @@ true - Comctl32.lib;d3d9.lib;dsound.lib;Gdiplus.lib;Shlwapi.lib;Vfw32.lib;winmm.lib;Ws2_32.lib;%(AdditionalDependencies) + Comctl32.lib;d3d9.lib;dsound.lib;dinput8.lib;Gdiplus.lib;Shlwapi.lib;Vfw32.lib;Ws2_32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) @@ -162,7 +162,7 @@ true true true - Comctl32.lib;d3d9.lib;dsound.lib;Gdiplus.lib;Shlwapi.lib;Vfw32.lib;winmm.lib;Ws2_32.lib;%(AdditionalDependencies) + Comctl32.lib;d3d9.lib;dsound.lib;dinput8.lib;Gdiplus.lib;Shlwapi.lib;Vfw32.lib;Ws2_32.lib;%(AdditionalDependencies) diff --git a/Src/JoystickHandler.cpp b/Src/JoystickHandler.cpp index bdc5c5cf..f8e73154 100644 --- a/Src/JoystickHandler.cpp +++ b/Src/JoystickHandler.cpp @@ -65,7 +65,7 @@ static const char* CFG_OPTIONS_STICKS_THRESHOLD = "JoysticksToKeysThreshold"; */ struct JoystickMenuIdsType { UINT Joysticks[2]; - UINT Axes[3]; + UINT Axes[2]; UINT AnalogMousestick; UINT DigitalMousestick; }; @@ -74,13 +74,13 @@ struct JoystickMenuIdsType { constexpr JoystickMenuIdsType JoystickMenuIds[NUM_BBC_JOYSTICKS]{ { { IDM_JOYSTICK, IDM_JOY1_PCJOY2 }, - { IDM_JOY1_PRIMARY, IDM_JOY1_SECONDARY1, IDM_JOY1_SECONDARY2 }, + { IDM_JOY1_PRIMARY, IDM_JOY1_SECONDARY1 }, IDM_ANALOGUE_MOUSESTICK, IDM_DIGITAL_MOUSESTICK, }, { { IDM_JOY2_PCJOY1, IDM_JOY2_PCJOY2 }, - { IDM_JOY2_PRIMARY, IDM_JOY2_SECONDARY1, IDM_JOY2_SECONDARY2 }, + { IDM_JOY2_PRIMARY, IDM_JOY2_SECONDARY1 }, IDM_JOY2_ANALOGUE_MOUSESTICK, IDM_JOY2_DIGITAL_MOUSESTICK, } @@ -92,10 +92,8 @@ std::string JoystickOrderEntry::to_string() char buf[10]; sprintf_s(buf, "%04x:%04x", mId(), pId()); std::string result(buf); -#if 0 // Joystick names from joyGetDevCaps are useless if (Name.length() != 0) result += " # " + Name; -#endif return result; } @@ -157,68 +155,107 @@ static UINT AxesToMenuId(int bbcIdx, int pcAxes) /*****************************************************************************/ std::string JoystickDev::DisplayString() { - if (!Configured) + if (!m_Configured) return std::string{}; - std::string name = Caps.szPname; - if (Instance != 1) - name += " #" + std::to_string(Instance); + std::string name = m_Name; + if (m_Instance != 1) + name += " #" + std::to_string(m_Instance); return name; } +#ifndef NDEBUG +void OutputDebug(const char* format, ...) +{ + char buf[256]; + va_list var; + va_start(var, format); + vsnprintf(buf, sizeof(buf), format, var); + OutputDebugString(buf); + va_end(var); +} +#else +void OutputDebug(const char* format, ...) {} +#endif + /*****************************************************************************/ bool JoystickDev::Update() { - if (!Present) + HRESULT hr; + if (!m_Present) return false; - InfoEx.dwSize = sizeof(InfoEx); - InfoEx.dwFlags = JOY_RETURNALL | JOY_RETURNPOVCTS; - return joyGetPosEx(JoyIndex, &InfoEx) == JOYERR_NOERROR; + hr = m_Device->Poll(); + if (FAILED(hr)) { OutputDebug("Poll result %08x\n", hr); } + if (FAILED(hr)) + { + hr = m_Device->Acquire(); + OutputDebug("Acquire result %08x\n", hr); + + if (hr == DIERR_UNPLUGGED) + return false; + + return true; + } + if (FAILED(hr = m_Device->GetDeviceState(sizeof(DIJOYSTATE2), &m_JoyState))) + { + OutputDebug("GeteviceState result %08x\n", hr); + return false; + } + return true; } /*****************************************************************************/ DWORD JoystickDev::GetButtons() { - return InfoEx.dwButtons; + DWORD buttons{ 0 }; + for (int i = 0; i < JOYSTICK_MAX_BTNS && i < sizeof(DIJOYSTATE2::rgbButtons); ++i) + { + if (m_JoyState.rgbButtons[i] & 0x80) + buttons |= (1 << i); + } + return buttons; } +static constexpr unsigned int setbit(int bit) { return 1u << bit; } +constexpr DWORD xboxAxes = setbit(JOYSTICK_AXIS_RX_P) | setbit(JOYSTICK_AXIS_RY_P); +constexpr DWORD stdpadAxes = setbit(JOYSTICK_AXIS_Z_P) | setbit(JOYSTICK_AXIS_RZ_P); + /*****************************************************************************/ void JoystickDev::GetAxesValue(int axesSet, int& x, int& y) { - UINT minX, maxX; - UINT minY, maxY; - DWORD dwX, dwY; switch (axesSet) { case 1: - dwX = InfoEx.dwUpos; minX = Caps.wUmin; maxX = Caps.wUmax; - dwY = InfoEx.dwRpos; minY = Caps.wRmin; maxY = Caps.wRmax; - break; case 2: - dwX = InfoEx.dwZpos; minX = Caps.wZmin; maxX = Caps.wZmax; - dwY = InfoEx.dwRpos; minY = Caps.wRmin; maxY = Caps.wRmax; + if ((m_PresentAxes & xboxAxes) == xboxAxes) + { + x = m_JoyState.lRx; + y = m_JoyState.lRy; + } + else if ((m_PresentAxes & stdpadAxes) == stdpadAxes) + { + x = m_JoyState.lZ; + y = m_JoyState.lRz; + } + else + { + x = 32767; + y = 32767; + } break; default: - dwX = InfoEx.dwXpos; minX = Caps.wXmin; maxX = Caps.wXmax; - dwY = InfoEx.dwYpos; minY = Caps.wYmin; maxY = Caps.wYmax; + x = m_JoyState.lX; + y = m_JoyState.lY; break; } - x = ((dwX - minX) * 65535 / (maxX - minX)); - y = ((dwY - minY) * 65535 / (maxY - minY)); } DWORD JoystickDev::GetAxesState(int threshold) { - using InfoT = JOYINFOEX; - using CapsT = JOYCAPS; + DIJOYSTATE2& state = m_JoyState; unsigned int axes = 0; - auto Scale = [this](DWORD InfoT::* pos, UINT CapsT::* min, UINT CapsT::* max) { - return (int)((double)(InfoEx.*pos - Caps.*min) * 65535.0 / - (double)(Caps.*max - Caps.*min)); - }; - auto Detect = [&axes, threshold](const int val, const int nbit, const int pbit) { if (val < 32767 - threshold) axes |= (1 << nbit); @@ -226,39 +263,157 @@ DWORD JoystickDev::GetAxesState(int threshold) axes |= (1 << pbit); }; - int ty = Scale(&InfoT::dwYpos, &CapsT::wYmin, &CapsT::wYmax); - int tx = Scale(&InfoT::dwXpos, &CapsT::wXmin, &CapsT::wXmax); - int tz = Scale(&InfoT::dwZpos, &CapsT::wZmin, &CapsT::wZmin); - int tr = Scale(&InfoT::dwRpos, &CapsT::wRmin, &CapsT::wRmax); - int tu = Scale(&InfoT::dwUpos, &CapsT::wUmin, &CapsT::wUmax); - int tv = Scale(&InfoT::dwVpos, &CapsT::wVmin, &CapsT::wVmax); + Detect(state.lX, JOYSTICK_AXIS_LEFT, JOYSTICK_AXIS_RIGHT); + Detect(state.lY, JOYSTICK_AXIS_UP, JOYSTICK_AXIS_DOWN); - Detect(ty, JOYSTICK_AXIS_UP, JOYSTICK_AXIS_DOWN); - Detect(tx, JOYSTICK_AXIS_LEFT, JOYSTICK_AXIS_RIGHT); - Detect(tz, JOYSTICK_AXIS_Z_N, JOYSTICK_AXIS_Z_P); - Detect(tr, JOYSTICK_AXIS_R_N, JOYSTICK_AXIS_R_P); - Detect(tu, JOYSTICK_AXIS_U_N, JOYSTICK_AXIS_U_P); - Detect(tv, JOYSTICK_AXIS_V_N, JOYSTICK_AXIS_V_P); + // If axes RX and RY are not present, but Z and RZ are, + // map Z and RZ to RX and RY + if ((m_PresentAxes & xboxAxes) == 0) + { + if (m_PresentAxes & setbit(JOYSTICK_AXIS_Z_P)) + Detect(state.lZ, JOYSTICK_AXIS_RX_N, JOYSTICK_AXIS_RX_P); + if (m_PresentAxes & setbit(JOYSTICK_AXIS_RZ_P)) + Detect(state.lZ, JOYSTICK_AXIS_RY_N, JOYSTICK_AXIS_RY_P); + } + else + { + if (m_PresentAxes & setbit(JOYSTICK_AXIS_Z_P)) + Detect(state.lZ, JOYSTICK_AXIS_Z_N, JOYSTICK_AXIS_Z_P); + if (m_PresentAxes & setbit(JOYSTICK_AXIS_RX_P)) + Detect(state.lRx, JOYSTICK_AXIS_RX_N, JOYSTICK_AXIS_RX_P); + if (m_PresentAxes & setbit(JOYSTICK_AXIS_RY_P)) + Detect(state.lRy, JOYSTICK_AXIS_RY_N, JOYSTICK_AXIS_RY_P); + if (m_PresentAxes & setbit(JOYSTICK_AXIS_RZ_P)) + Detect(state.lRz, JOYSTICK_AXIS_RZ_N, JOYSTICK_AXIS_RZ_P); + } - if (InfoEx.dwPOV != JOY_POVCENTERED) + if (m_PresentAxes & setbit(JOYSTICK_AXIS_HAT_UP)) { - if (InfoEx.dwPOV >= 29250 || InfoEx.dwPOV < 6750) - axes |= (1 << JOYSTICK_AXIS_HAT_UP); - if (InfoEx.dwPOV >= 2250 && InfoEx.dwPOV < 15750) - axes |= (1 << JOYSTICK_AXIS_HAT_RIGHT); - if (InfoEx.dwPOV >= 11250 && InfoEx.dwPOV < 24750) - axes |= (1 << JOYSTICK_AXIS_HAT_DOWN); - if (InfoEx.dwPOV >= 20250 && InfoEx.dwPOV < 33750) - axes |= (1 << JOYSTICK_AXIS_HAT_LEFT); + DWORD pov = state.rgdwPOV[0]; + if (pov != -1) + { + if (pov >= 29250 || pov < 6750) + axes |= (1 << JOYSTICK_AXIS_HAT_UP); + if (pov >= 2250 && pov < 15750) + axes |= (1 << JOYSTICK_AXIS_HAT_RIGHT); + if (pov >= 11250 && pov < 24750) + axes |= (1 << JOYSTICK_AXIS_HAT_DOWN); + if (pov >= 20250 && pov < 33750) + axes |= (1 << JOYSTICK_AXIS_HAT_LEFT); + } } return axes; } /****************************************************************************/ -void JoystickHandler::ScanJoysticks() +void JoystickDev::EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi) { - JOYINFOEX joyInfoEx; + // For axes that are returned, set the DIPROP_RANGE property for the + // enumerated axis in order to scale min/max values. + if (pdidoi->dwType & DIDFT_AXIS) + { + DIPROPRANGE diprg{}; + diprg.diph.dwSize = sizeof(DIPROPRANGE); + diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); + diprg.diph.dwHow = DIPH_BYID; + diprg.diph.dwObj = pdidoi->dwType; // Specify the enumerated axis + diprg.lMin = 0; + diprg.lMax = 65535; + + // Set the range for the axis + if (FAILED(m_Device->SetProperty(DIPROP_RANGE, &diprg.diph))) + return; + } + + if (pdidoi->dwType & DIDFT_BUTTON) + { + auto instance = DIDFT_GETINSTANCE(pdidoi->dwType); + if (instance < JOYSTICK_MAX_BTNS) + m_PresentButtons |= setbit(instance); + } + + if (pdidoi->guidType == GUID_YAxis) + m_PresentAxes |= setbit(JOYSTICK_AXIS_UP) | setbit(JOYSTICK_AXIS_DOWN); + if (pdidoi->guidType == GUID_XAxis) + m_PresentAxes |= setbit(JOYSTICK_AXIS_LEFT) | setbit(JOYSTICK_AXIS_RIGHT); + if (pdidoi->guidType == GUID_ZAxis) + m_PresentAxes |= setbit(JOYSTICK_AXIS_Z_N) | setbit(JOYSTICK_AXIS_Z_P); + if (pdidoi->guidType == GUID_RxAxis) + m_PresentAxes |= setbit(JOYSTICK_AXIS_RX_N) | setbit(JOYSTICK_AXIS_RX_P); + if (pdidoi->guidType == GUID_RyAxis) + m_PresentAxes |= setbit(JOYSTICK_AXIS_RY_N) | setbit(JOYSTICK_AXIS_RY_P); + if (pdidoi->guidType == GUID_RzAxis) + m_PresentAxes |= setbit(JOYSTICK_AXIS_RZ_N) | setbit(JOYSTICK_AXIS_RZ_P); + if (pdidoi->guidType == GUID_POV) + m_PresentAxes |= setbit(JOYSTICK_AXIS_HAT_LEFT) | setbit(JOYSTICK_AXIS_HAT_RIGHT) + | setbit(JOYSTICK_AXIS_HAT_UP) | setbit(JOYSTICK_AXIS_HAT_DOWN); +} + +/****************************************************************************/ +JoystickDev::JoystickDev(JoystickDev&& r) +{ + m_GUIDInstance = r.m_GUIDInstance; + m_Id = r.m_Id; + std::swap(m_Name, r.m_Name); + std::swap(m_Device, r.m_Device); + m_PresentAxes = r.m_PresentAxes; + m_PresentButtons = r.m_PresentButtons; + m_Instance = r.m_Instance; + m_Order = r.m_Order; + m_JoyIndex = r.m_JoyIndex; + m_Configured = r.m_Configured; + m_Present = r.m_Present; +} + +/****************************************************************************/ +void JoystickDev::CloseDevice() +{ + if (m_Device) + { + m_Device->Unacquire(); + m_Device->Release(); + m_Device = nullptr; + } +} + +/****************************************************************************/ +JoystickDev::~JoystickDev() +{ + CloseDevice(); +} + +/****************************************************************************/ +void JoystickHandler::AddDeviceInstance(const DIDEVICEINSTANCE* pdidInstance) +{ + int mid = LOWORD(pdidInstance->guidProduct.Data1); + int pid = HIWORD(pdidInstance->guidProduct.Data1); + + int index = m_JoystickDevs.size(); + + JoystickDev dev; + dev.m_GUIDInstance = pdidInstance->guidInstance; + dev.m_Id = JoystickId(mid, pid); + dev.m_Name = pdidInstance->tszInstanceName; + dev.m_Configured = true; + dev.m_Present = true; + dev.m_JoyIndex = index; + + m_JoystickDevs.emplace_back(std::move(dev)); +} + +/****************************************************************************/ + +static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, VOID* pContext) +{ + reinterpret_cast(pContext)->AddDeviceInstance(pdidInstance); + return DIENUM_CONTINUE; +} + +/****************************************************************************/ +HRESULT JoystickHandler::ScanJoysticks() +{ +// JOYINFOEX joyInfoEx; std::map> listById; // Clear PC joystick list @@ -269,33 +424,21 @@ void JoystickHandler::ScanJoysticks() state.JoyIndex = -1; } - // Get all configured and present joysticks - for (int devIdx = 0; devIdx < MAX_JOYSTICK_DEVS; ++devIdx) + // Clear joystick devs vector + m_JoystickDevs.clear(); + + if (FAILED(m_DirectInputInitResult)) + return m_DirectInputInitResult; + + HRESULT hr = m_pDirectInput->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, this, DIEDFL_ATTACHEDONLY); + if (FAILED(hr)) + return hr; + + for (unsigned int devIdx = 0; devIdx < m_JoystickDevs.size(); ++devIdx) { JoystickDev& dev = m_JoystickDevs[devIdx]; - dev.Configured = false; - dev.Present = false; - dev.Instance = 0; - dev.Order = -1; - dev.JoyIndex = devIdx; - - memset(&joyInfoEx, 0, sizeof(joyInfoEx)); - joyInfoEx.dwSize = sizeof(joyInfoEx); - joyInfoEx.dwFlags = JOY_RETURNBUTTONS; - - DWORD Result = joyGetDevCaps(devIdx, &dev.Caps, sizeof(JOYCAPS)); - if (Result == JOYERR_NOERROR) - { - dev.Configured = true; - - Result = joyGetPosEx(devIdx, &joyInfoEx); - if (Result == JOYERR_NOERROR) - { - dev.Present = true; - listById[dev.Id()].push_back(&dev); - dev.Instance = listById[dev.Id()].size(); - } - } + listById[dev.Id()].push_back(&dev); + dev.m_Instance = listById[dev.Id()].size(); } int joyIdx = 0; @@ -308,12 +451,12 @@ void JoystickHandler::ScanJoysticks() { JoystickDev& dev = *(list.front()); list.pop_front(); - dev.Order = order++; - entry.JoyIndex = dev.JoyIndex; - if (dev.Present && joyIdx < NUM_PC_JOYSTICKS) + dev.m_Order = order++; + entry.JoyIndex = dev.m_JoyIndex; + if (dev.m_Present && joyIdx < NUM_PC_JOYSTICKS) { m_PCJoystickState[joyIdx].Dev = &dev; - m_PCJoystickState[joyIdx].JoyIndex = dev.JoyIndex; + m_PCJoystickState[joyIdx].JoyIndex = dev.m_JoyIndex; ++joyIdx; } } @@ -330,10 +473,10 @@ void JoystickHandler::ScanJoysticks() if (joyIdx >= NUM_PC_JOYSTICKS) break; - if (dev.Present && dev.Order == -1) + if (dev.m_Present && dev.m_Order == -1) { m_PCJoystickState[joyIdx].Dev = &dev; - m_PCJoystickState[joyIdx].JoyIndex = dev.JoyIndex; + m_PCJoystickState[joyIdx].JoyIndex = dev.m_JoyIndex; ++joyIdx; newJoysticks.push_back(&dev); } @@ -356,14 +499,75 @@ void JoystickHandler::ScanJoysticks() // Add new joystick at the end of order list for (JoystickDev* dev : newJoysticks) - m_JoystickOrder.emplace_back(dev->Id(), dev->DisplayString(), dev->JoyIndex); + m_JoystickOrder.emplace_back(dev->Id(), dev->DisplayString(), dev->m_JoyIndex); // Update order in joystick list for (idx = 0; idx < static_cast(m_JoystickOrder.size()); ++idx) { JoystickOrderEntry& entry = m_JoystickOrder[idx]; if (entry.JoyIndex != -1) - m_JoystickDevs[entry.JoyIndex].Order = idx; + m_JoystickDevs[entry.JoyIndex].m_Order = idx; + } + return S_OK; +} + +/****************************************************************************/ + +static BOOL CALLBACK EnumJoystickObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext) +{ + reinterpret_cast(pContext)->EnumObjectsCallback(pdidoi); + return DIENUM_CONTINUE; +} + +/****************************************************************************/ +HRESULT JoystickHandler::OpenDevice(HWND mainWindow, JoystickDev* dev) +{ + HRESULT hr; + + // Do nothing if device already open + if (dev->m_Device) + return S_OK; + + if (FAILED(hr = m_pDirectInput->CreateDevice(dev->m_GUIDInstance, &dev->m_Device, nullptr))) + return hr; + + if (FAILED(hr = dev->m_Device->SetDataFormat(&c_dfDIJoystick2))) + { + dev->CloseDevice(); + return hr; + } + + if (FAILED(hr = dev->m_Device->SetCooperativeLevel(mainWindow, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND))) + { + dev->CloseDevice(); + return hr; + } + + if (FAILED(hr = dev->m_Device->EnumObjects(EnumJoystickObjectsCallback, (VOID*)dev, DIDFT_ALL))) + { + dev->CloseDevice(); + return hr; + } + + return S_OK; +} + +/****************************************************************************/ +HRESULT JoystickHandler::InitDirectInput() +{ + if (FAILED(m_DirectInputInitResult = DirectInput8Create(GetModuleHandle(NULL), + DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&m_pDirectInput, NULL))) + return m_DirectInputInitResult; + return S_OK; +} + +/****************************************************************************/ +JoystickHandler::~JoystickHandler() +{ + if (m_pDirectInput) + { + m_pDirectInput->Release(); + m_pDirectInput = nullptr; } } @@ -472,12 +676,11 @@ void BeebWin::UpdateJoystickMenu() for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) { bool EnableAxes = false; -#if 0 // This needs rework (proper joystick name from DirectInput) static const char* const menuItems[2] = { "First PC &Joystick", "Second PC Joystick" }; for (int pcIdx = 0; pcIdx < _countof(JoystickMenuIdsType::Joysticks); ++pcIdx) { - auto* dev = m_PCJoystickState[pcIdx].Dev; + auto* dev = m_JoystickHandler->m_PCJoystickState[pcIdx].Dev; if (dev) { SetMenuItemText(JoystickMenuIds[bbcIdx].Joysticks[pcIdx], dev->DisplayString()); @@ -487,7 +690,6 @@ void BeebWin::UpdateJoystickMenu() SetMenuItemText(JoystickMenuIds[bbcIdx].Joysticks[pcIdx], menuItems[pcIdx]); } } -#endif if (GetPCJoystick(bbcIdx) != 0) EnableAxes = true; @@ -569,6 +771,47 @@ void BeebWin::ResetJoystick() } } +/****************************************************************************/ +bool BeebWin::ScanJoysticks(bool verbose) +{ + if (!m_JoystickHandler->m_DirectInputInitialized) + m_JoystickHandler->InitDirectInput(); + + if (FAILED(m_JoystickHandler->m_DirectInputInitResult)) + { + if (verbose) + { + char msg[128]; + snprintf(msg, sizeof(msg), "DirectInput initialization failed.\nError Code: %08X", m_JoystickHandler->m_DirectInputInitResult); + MessageBox(m_hWnd, msg, WindowTitle, MB_OK | MB_ICONERROR); + } + return false; + } + + HRESULT hr; + if (FAILED(hr = m_JoystickHandler->ScanJoysticks())) + { + if (verbose) + { + char msg[128]; + snprintf(msg, sizeof(msg), "Joystick enumeration failed.\nError Code: %08X", hr); + MessageBox(m_hWnd, msg, WindowTitle, MB_OK | MB_ICONERROR); + } + return false; + } + + if (m_JoystickHandler->m_JoystickDevs.size() == 0) + { + if (verbose) + { + MessageBox(m_hWnd, "No joysticks found", WindowTitle, MB_OK | MB_ICONERROR); + } + return false; + } + + return true; +} + /****************************************************************************/ bool BeebWin::InitJoystick(bool verbose) { @@ -576,9 +819,7 @@ bool BeebWin::InitJoystick(bool verbose) ResetJoystick(); - m_JoystickHandler->ScanJoysticks(); - - if (IsPCJoystickOn(0) || IsPCJoystickOn(1) || m_JoystickToKeys) + if (GetPCJoystick(0) || GetPCJoystick(1) || m_JoystickToKeys) { for (int pcIdx = 0; pcIdx < NUM_PC_JOYSTICKS; ++pcIdx) { @@ -610,18 +851,23 @@ bool BeebWin::InitJoystick(bool verbose) bool BeebWin::CaptureJoystick(int Index, bool verbose) { bool success = false; + JoystickDev* dev = m_JoystickHandler->m_PCJoystickState[Index].Dev; - if (m_JoystickHandler->m_PCJoystickState[Index].Dev) - { - m_JoystickHandler->m_PCJoystickState[Index].Captured = true; - success = true; - } - else if (verbose && (IsPCJoystickOn(Index) || m_JoystickToKeys && Index == 0)) + if (dev) { - char str[100]; - sprintf(str, "Failed to initialise joystick %d", Index + 1); + HRESULT hr = m_JoystickHandler->OpenDevice(m_hWnd, dev); + if (!FAILED(hr)) + { + m_JoystickHandler->m_PCJoystickState[Index].Captured = true; + success = true; + } + else if (verbose) + { + char str[256]; + snprintf(str, sizeof(str), "Failed to initialise %s", dev->DisplayString().c_str()); - MessageBox(m_hWnd, str, WindowTitle, MB_OK | MB_ICONWARNING); + MessageBox(m_hWnd, str, WindowTitle, MB_OK | MB_ICONWARNING); + } } return success; @@ -732,7 +978,7 @@ void BeebWin::TranslateAxes(int joyId, unsigned int axesState) if (axesState != prevAxes) { - for (int axId = 0; axId < JOYSTICK_AXES_COUNT; ++axId) + for (int axId = 0; axId < JOYSTICK_MAX_AXES; ++axId) { if ((axesState & ~prevAxes) & (1 << axId)) { @@ -868,6 +1114,10 @@ void BeebWin::TranslateJoystick(int joyId) /*****************************************************************************/ void BeebWin::UpdateJoysticks() { + // Don't update joysticks if not in foreground + if (!m_active && m_JoystickTarget == nullptr) + return; + for (int idx = 0; idx < NUM_PC_JOYSTICKS; ++idx) { if (m_JoystickHandler->m_PCJoystickState[0].Captured) @@ -1345,7 +1595,12 @@ void BeebWin::ReadJoystickPreferences() } if (GetNthDWORDValue(m_Preferences, CFG_OPTIONS_STICK_PCAXES, bbcIdx + 1, dword)) + { + // Axes value 2 is obsolete, replace with 1 + if (dword == 2) + dword = 1; config.PCAxes = dword; + } m_MenuIdAxes[bbcIdx] = AxesToMenuId(bbcIdx, config.PCAxes); } diff --git a/Src/JoystickHandler.h b/Src/JoystickHandler.h index b0ed9997..7446a60f 100644 --- a/Src/JoystickHandler.h +++ b/Src/JoystickHandler.h @@ -28,78 +28,103 @@ Boston, MA 02110-1301, USA. #ifndef JOYSTICKHANDLER_H #define JOYSTICKHANDLER_H +#define DIRECTINPUT_VERSION 0x0800 + #include #include "beebwin.h" -/* Max number of joysticks in joystickapi */ -#define MAX_JOYSTICK_DEVS 16 +#include +#include +#include + /* Max number of entries on joystick order list */ #define MAX_JOYSTICK_ORDER 16 struct JoystickId : std::pair { - using std::pair::pair; + using std::pair::pair; - // Manufacturer ID aka Vendor ID - int& mId() { return first; } - // Product ID - int& pId() { return second; } + // Manufacturer ID aka Vendor ID + int& mId() { return first; } + // Product ID + int& pId() { return second; } }; struct JoystickOrderEntry : JoystickId { - std::string Name{}; - int JoyIndex{ -1 }; + std::string Name{}; + int JoyIndex{ -1 }; - JoystickOrderEntry() = default; - JoystickOrderEntry(JoystickId id, const std::string& name, int joyIndex) : - JoystickId(id), Name(name), JoyIndex(joyIndex) {} - JoystickOrderEntry(int mid, int pid, const std::string& name) : - JoystickId(mid, pid), Name(name) {} + JoystickOrderEntry() = default; + JoystickOrderEntry(JoystickId id, const std::string& name, int joyIndex) : + JoystickId(id), Name(name), JoyIndex(joyIndex) {} + JoystickOrderEntry(int mid, int pid, const std::string& name) : + JoystickId(mid, pid), Name(name) {} - std::string to_string(); - bool from_string(const std::string&); + std::string to_string(); + bool from_string(const std::string&); }; struct JoystickDev { - JOYCAPS Caps{}; - JOYINFOEX InfoEx{}; - int Instance{ 0 }; - int Order{ -1 }; - int JoyIndex{ -1 }; - bool Configured{ false }; - bool Present{ false }; - - JoystickId Id() { return JoystickId{ Caps.wMid, Caps.wPid }; } - std::string DisplayString(); - bool Update(); - DWORD GetButtons(); - DWORD GetAxesState(int threshold); - void GetAxesValue(int axesSet, int& x, int& y); + GUID m_GUIDInstance{}; + JoystickId m_Id{ 0, 0 }; + std::string m_Name; + LPDIRECTINPUTDEVICE8 m_Device{ nullptr }; + DWORD m_PresentAxes{ 0 }; + DWORD m_PresentButtons{ 0 }; + DIJOYSTATE2 m_JoyState; + + int m_Instance{ 0 }; + int m_Order{ -1 }; + int m_JoyIndex{ -1 }; + bool m_Configured{ false }; + bool m_Present{ false }; + + JoystickId Id() { return m_Id; } + std::string DisplayString(); + bool Update(); + DWORD GetButtons(); + DWORD GetAxesState(int threshold); + void GetAxesValue(int axesSet, int& x, int& y); + void EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi); + void CloseDevice(); + + JoystickDev() {} + JoystickDev(const JoystickDev&) = delete; + JoystickDev(JoystickDev&& r); + JoystickDev& operator=(const JoystickDev&) = delete; + ~JoystickDev(); }; struct PCJoystickState { - JoystickDev* Dev{ nullptr }; - int JoyIndex{ -1 }; - bool Captured{ false }; - unsigned int PrevAxes{ 0 }; - unsigned int PrevBtns{ 0 }; - bool JoystickToKeysActive{ false }; + JoystickDev* Dev{ nullptr }; + int JoyIndex{ -1 }; + bool Captured{ false }; + unsigned int PrevAxes{ 0 }; + unsigned int PrevBtns{ 0 }; + bool JoystickToKeysActive{ false }; }; struct JoystickHandler { - JoystickDev m_JoystickDevs[MAX_JOYSTICK_DEVS]; - PCJoystickState m_PCJoystickState[NUM_PC_JOYSTICKS]; - std::vector m_JoystickOrder; + bool m_DirectInputInitialized{ false }; + HRESULT m_DirectInputInitResult{ E_FAIL }; + LPDIRECTINPUT8 m_pDirectInput{ nullptr }; + + std::vector m_JoystickDevs; + PCJoystickState m_PCJoystickState[NUM_PC_JOYSTICKS]; + std::vector m_JoystickOrder; - void ScanJoysticks(void); + HRESULT InitDirectInput(void); + void AddDeviceInstance(const DIDEVICEINSTANCE*); + HRESULT ScanJoysticks(void); + HRESULT OpenDevice(HWND mainWindow, JoystickDev* dev); - JoystickHandler() {} - ~JoystickHandler() {} + JoystickHandler() {} + ~JoystickHandler(); }; #endif diff --git a/Src/SelectKeyDialog.cpp b/Src/SelectKeyDialog.cpp index 6d37b292..2d6151bb 100644 --- a/Src/SelectKeyDialog.cpp +++ b/Src/SelectKeyDialog.cpp @@ -216,6 +216,26 @@ bool SelectKeyDialog::Shift() const LPCSTR SelectKeyDialog::KeyName(int Key) { + static const std::map axisNamesMap = { + { JOYSTICK_AXIS_LEFT, "Left" }, + { JOYSTICK_AXIS_RIGHT, "Right" }, + { JOYSTICK_AXIS_UP, "Up" }, + { JOYSTICK_AXIS_DOWN, "Down" }, + { JOYSTICK_AXIS_Z_N, "Z-" }, + { JOYSTICK_AXIS_Z_P, "Z+" }, + { JOYSTICK_AXIS_RX_N, "RLeft" }, + { JOYSTICK_AXIS_RX_P, "RRight" }, + { JOYSTICK_AXIS_RY_N, "RUp" }, + { JOYSTICK_AXIS_RY_P, "RDown" }, + { JOYSTICK_AXIS_RZ_N, "RZ-" }, + { JOYSTICK_AXIS_RZ_P, "RZ+" }, + { JOYSTICK_AXIS_HAT_LEFT, "HatLeft" }, + { JOYSTICK_AXIS_HAT_RIGHT, "HatRight" }, + { JOYSTICK_AXIS_HAT_UP, "HatUp" }, + { JOYSTICK_AXIS_HAT_DOWN, "HatDown" } + + }; + static CHAR Character[2]; // Used to return single characters. if (Key >= BEEB_VKEY_JOY_START && Key < BEEB_VKEY_JOY_END) @@ -231,21 +251,10 @@ LPCSTR SelectKeyDialog::KeyName(int Key) if (Key < JOYSTICK_MAX_AXES) { - if (Key == JOYSTICK_AXIS_UP) - { - strcat(Name, "Up"); - } - else if (Key == JOYSTICK_AXIS_DOWN) - { - strcat(Name, "Down"); - } - else if (Key == JOYSTICK_AXIS_LEFT) - { - strcat(Name, "Left"); - } - else if (Key == JOYSTICK_AXIS_RIGHT) + auto iter = axisNamesMap.find(Key); + if (iter != axisNamesMap.end()) { - strcat(Name, "Right"); + strcat(Name, iter->second); } else { @@ -361,9 +370,38 @@ int SelectKeyDialog::JoyVKeyByName(const char* Name) return keyMap; }(); - auto iter = nameToVKeyMap.find(toupper(Name)); - if (iter == nameToVKeyMap.end()) + std::string uname = toupper(Name); + auto iter = nameToVKeyMap.find(uname); + if (iter != nameToVKeyMap.end()) + return iter->second; + + // Read axis number + if (uname.substr(0, 3) != "JOY") + return -1; + uname = uname.substr(3); + char* endp; + long joyIdx = strtol(uname.c_str(), &endp, 10); + if (joyIdx < 1 || joyIdx > NUM_PC_JOYSTICKS || !endp || endp == uname.c_str()) + return -1; + uname = uname.substr(endp - uname.c_str()); + + if (uname.substr(0, 4) != "AXIS") + return -1; + uname = uname.substr(4); + long axis = strtol(uname.c_str(), &endp, 10); + if (axis < 1 || axis > JOYSTICK_MAX_AXES || !endp || endp == uname.c_str()) + return -1; + uname = uname.substr(endp - uname.c_str()); + + int pos = 0; + if (uname.size() != 1) + return -1; + if (uname[0] == '-') + pos = 0; + else if (uname[0] == '+') + pos = 1; + else return -1; - return iter->second; + return BEEB_VKEY_JOY_START + (joyIdx - 1) * (JOYSTICK_MAX_AXES + JOYSTICK_MAX_BTNS) + (axis - 1) * 2 + pos; } diff --git a/Src/beebemrc.h b/Src/beebemrc.h index 05b9a430..c6121b7a 100644 --- a/Src/beebemrc.h +++ b/Src/beebemrc.h @@ -445,14 +445,12 @@ Boston, MA 02110-1301, USA. #define IDM_JOY1_PCJOY2 40305 #define IDM_JOY1_PRIMARY 40306 #define IDM_JOY1_SECONDARY1 40307 -#define IDM_JOY1_SECONDARY2 40308 #define IDM_JOY2_PCJOY1 40309 #define IDM_JOY2_PCJOY2 40310 #define IDM_JOY2_ANALOGUE_MOUSESTICK 40311 #define IDM_JOY2_DIGITAL_MOUSESTICK 40312 #define IDM_JOY2_PRIMARY 40313 #define IDM_JOY2_SECONDARY1 40314 -#define IDM_JOY2_SECONDARY2 40315 #define IDM_CAPTUREMOUSE 40318 #define IDC_STATIC -1 diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index d787920f..8631757b 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -176,6 +176,7 @@ BeebWin::BeebWin() m_SpVoice = NULL; m_hTextView = NULL; m_frozen = false; + m_active = true; aviWriter = NULL; m_WriteProtectDisc[0] = !IsDiscWritable(0); m_WriteProtectDisc[1] = !IsDiscWritable(1); @@ -408,6 +409,7 @@ void BeebWin::ApplyPrefs() PrinterDisable(); /* Joysticks can only be initialised after the window is created (needs hwnd) */ + ScanJoysticks(false); InitJoystick(false); LoadFDC(NULL, true); @@ -3286,17 +3288,16 @@ void BeebWin::HandleCommand(int MenuId) case IDM_JOY1_PRIMARY: case IDM_JOY1_SECONDARY1: - case IDM_JOY1_SECONDARY2: ProcessJoystickAxesMenuCommand(0, MenuId); break; case IDM_JOY2_PRIMARY: case IDM_JOY2_SECONDARY1: - case IDM_JOY2_SECONDARY2: ProcessJoystickAxesMenuCommand(1, MenuId); break; case IDM_INIT_JOYSTICK: + ScanJoysticks(true); InitJoystick(true); break; @@ -3995,6 +3996,7 @@ void BeebWin::SetSoundMenu() void BeebWin::Activate(bool active) { + m_active = active; if (active) m_frozen = false; else if (m_FreezeWhenInactive) diff --git a/Src/beebwin.h b/Src/beebwin.h index 84a24fc7..5522fa27 100644 --- a/Src/beebwin.h +++ b/Src/beebwin.h @@ -62,23 +62,22 @@ Boston, MA 02110-1301, USA. #define JOYSTICK_MAX_AXES 16 #define JOYSTICK_MAX_BTNS 16 -#define JOYSTICK_AXIS_UP 0 -#define JOYSTICK_AXIS_DOWN 1 -#define JOYSTICK_AXIS_LEFT 2 -#define JOYSTICK_AXIS_RIGHT 3 +#define JOYSTICK_AXIS_LEFT 0 +#define JOYSTICK_AXIS_RIGHT 1 +#define JOYSTICK_AXIS_UP 2 +#define JOYSTICK_AXIS_DOWN 3 #define JOYSTICK_AXIS_Z_N 4 #define JOYSTICK_AXIS_Z_P 5 -#define JOYSTICK_AXIS_R_N 6 -#define JOYSTICK_AXIS_R_P 7 -#define JOYSTICK_AXIS_U_N 8 -#define JOYSTICK_AXIS_U_P 9 -#define JOYSTICK_AXIS_V_N 10 -#define JOYSTICK_AXIS_V_P 11 -#define JOYSTICK_AXIS_HAT_UP 12 -#define JOYSTICK_AXIS_HAT_DOWN 13 -#define JOYSTICK_AXIS_HAT_LEFT 14 -#define JOYSTICK_AXIS_HAT_RIGHT 15 -#define JOYSTICK_AXES_COUNT 16 +#define JOYSTICK_AXIS_RX_N 6 +#define JOYSTICK_AXIS_RX_P 7 +#define JOYSTICK_AXIS_RY_N 8 +#define JOYSTICK_AXIS_RY_P 9 +#define JOYSTICK_AXIS_RZ_N 10 +#define JOYSTICK_AXIS_RZ_P 11 +#define JOYSTICK_AXIS_HAT_LEFT 12 +#define JOYSTICK_AXIS_HAT_RIGHT 13 +#define JOYSTICK_AXIS_HAT_UP 14 +#define JOYSTICK_AXIS_HAT_DOWN 15 #define BEEB_VKEY_JOY_START 256 #define BEEB_VKEY_JOY_COUNT (NUM_PC_JOYSTICKS * \ @@ -346,6 +345,7 @@ class BeebWin { HMENU m_hMenu; bool m_frozen; + bool m_active; char* m_screen; char* m_screen_blur; double m_RealTimeTarget; @@ -612,6 +612,7 @@ class BeebWin { int ReadDisc(int Drive, bool bCheckForPrefs); void Load1770DiscImage(const char *FileName, int Drive, DiscType Type); void LoadTape(void); + bool ScanJoysticks(bool verbose = false); bool InitJoystick(bool verbose = false); bool CaptureJoystick(int Index, bool verbose); void ResetJoystick(void); From c133d9eded3c2b640f11a89c274c675631068d94 Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Sun, 28 Feb 2021 01:55:07 +0100 Subject: [PATCH 31/38] Merged PCJoystickState with JoystickDev --- Src/JoystickHandler.cpp | 228 +++++++++++++++++++++++++--------------- Src/JoystickHandler.h | 28 ++--- Src/beebwin.h | 2 +- 3 files changed, 163 insertions(+), 95 deletions(-) diff --git a/Src/JoystickHandler.cpp b/Src/JoystickHandler.cpp index f8e73154..1c22c5ba 100644 --- a/Src/JoystickHandler.cpp +++ b/Src/JoystickHandler.cpp @@ -175,7 +175,7 @@ void OutputDebug(const char* format, ...) va_end(var); } #else -void OutputDebug(const char* format, ...) {} +void OutputDebug(const char* format, ...) { (void)format; } #endif /*****************************************************************************/ @@ -360,15 +360,21 @@ JoystickDev::JoystickDev(JoystickDev&& r) m_PresentAxes = r.m_PresentAxes; m_PresentButtons = r.m_PresentButtons; m_Instance = r.m_Instance; - m_Order = r.m_Order; + m_OnOrderList = r.m_OnOrderList; m_JoyIndex = r.m_JoyIndex; m_Configured = r.m_Configured; m_Present = r.m_Present; + m_Captured = r.m_Captured; + m_PrevAxes = r.m_PrevAxes; + m_PrevBtns = r.m_PrevBtns; + m_JoystickToKeysActive = r.m_JoystickToKeysActive; + r.m_Present = false; } /****************************************************************************/ void JoystickDev::CloseDevice() { + m_Captured = false; if (m_Device) { m_Device->Unacquire(); @@ -413,18 +419,11 @@ static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, /****************************************************************************/ HRESULT JoystickHandler::ScanJoysticks() { -// JOYINFOEX joyInfoEx; - std::map> listById; + bool usePreferredCfg = m_HavePreferredJoyCfg; - // Clear PC joystick list - for (PCJoystickState& state : m_PCJoystickState) - { - state.Captured = false; - state.Dev = nullptr; - state.JoyIndex = -1; - } + std::map> listById; - // Clear joystick devs vector + // Clear joystick devs vector - closes any open devices m_JoystickDevs.clear(); if (FAILED(m_DirectInputInitResult)) @@ -441,8 +440,7 @@ HRESULT JoystickHandler::ScanJoysticks() dev.m_Instance = listById[dev.Id()].size(); } - int joyIdx = 0; - int order = 0; + std::vector sorted; // Start with joysticks in the order list for (JoystickOrderEntry& entry : m_JoystickOrder) { @@ -451,14 +449,12 @@ HRESULT JoystickHandler::ScanJoysticks() { JoystickDev& dev = *(list.front()); list.pop_front(); - dev.m_Order = order++; + dev.m_OnOrderList = true; + dev.m_JoyIndex = sorted.size(); + if (usePreferredCfg && IsEqualGUID(dev.m_GUIDInstance, m_PreferredJoyCfg.guidInstance)) + usePreferredCfg = false; + sorted.emplace_back(std::move(dev)); entry.JoyIndex = dev.m_JoyIndex; - if (dev.m_Present && joyIdx < NUM_PC_JOYSTICKS) - { - m_PCJoystickState[joyIdx].Dev = &dev; - m_PCJoystickState[joyIdx].JoyIndex = dev.m_JoyIndex; - ++joyIdx; - } } else { @@ -466,25 +462,37 @@ HRESULT JoystickHandler::ScanJoysticks() } } - std::list newJoysticks; + int newJoysticks = 0; + + // Add preferred joystick first + if (usePreferredCfg) + { + for (JoystickDev& dev : m_JoystickDevs) + { + if (dev.m_Present && !dev.m_OnOrderList && IsEqualGUID(dev.m_GUIDInstance, m_PreferredJoyCfg.guidInstance)) + { + dev.m_JoyIndex = sorted.size(); + sorted.emplace_back(std::move(dev)); + ++newJoysticks; + break; + } + } + } + // Add joysticks not in the order list for (JoystickDev& dev : m_JoystickDevs) { - if (joyIdx >= NUM_PC_JOYSTICKS) - break; - - if (dev.m_Present && dev.m_Order == -1) + if (dev.m_Present && !dev.m_OnOrderList) { - m_PCJoystickState[joyIdx].Dev = &dev; - m_PCJoystickState[joyIdx].JoyIndex = dev.m_JoyIndex; - ++joyIdx; - newJoysticks.push_back(&dev); + dev.m_JoyIndex = sorted.size(); + sorted.emplace_back(std::move(dev)); + ++newJoysticks; } } // If joystick order list is too long, remove some unconnected entries // Remove entries from the beginning or from the end, or some other order? - int to_remove = newJoysticks.size() + m_JoystickOrder.size() - MAX_JOYSTICK_ORDER; + int to_remove = newJoysticks + m_JoystickOrder.size() - MAX_JOYSTICK_ORDER; unsigned int idx = 0; while (to_remove > 0 && idx < m_JoystickOrder.size()) { @@ -497,17 +505,19 @@ HRESULT JoystickHandler::ScanJoysticks() ++idx; } - // Add new joystick at the end of order list - for (JoystickDev* dev : newJoysticks) - m_JoystickOrder.emplace_back(dev->Id(), dev->DisplayString(), dev->m_JoyIndex); + // Replace joystick device vector with sorted one + m_JoystickDevs = std::move(sorted); - // Update order in joystick list - for (idx = 0; idx < static_cast(m_JoystickOrder.size()); ++idx) + // Add new joysticks at the end of order list + for (JoystickDev& dev : m_JoystickDevs) { - JoystickOrderEntry& entry = m_JoystickOrder[idx]; - if (entry.JoyIndex != -1) - m_JoystickDevs[entry.JoyIndex].m_Order = idx; + if (!dev.m_OnOrderList) + { + m_JoystickOrder.emplace_back(dev.Id(), dev.DisplayString(), dev.m_JoyIndex); + dev.m_OnOrderList = true; + } } + return S_OK; } @@ -558,6 +568,19 @@ HRESULT JoystickHandler::InitDirectInput() if (FAILED(m_DirectInputInitResult = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&m_pDirectInput, NULL))) return m_DirectInputInitResult; + + HRESULT hr; + IDirectInputJoyConfig8* pJoyConfig = nullptr; + if (FAILED(hr = m_pDirectInput->QueryInterface(IID_IDirectInputJoyConfig8, (void**)&pJoyConfig))) + return S_OK; + + m_PreferredJoyCfg.dwSize = sizeof(m_PreferredJoyCfg); + if (SUCCEEDED(pJoyConfig->GetConfig(0, &m_PreferredJoyCfg, DIJC_GUIDINSTANCE))) + m_HavePreferredJoyCfg = true; + + if (pJoyConfig) + pJoyConfig->Release(); + return S_OK; } @@ -659,18 +682,9 @@ void BeebWin::InitJoystickMenu() /****************************************************************************/ void BeebWin::UpdateJoystickMenu() { - bool EnableInit = false; - // Enable "Initialize Joysticks" menu item if any more joysticks could be possibly captured (or always?) - for (int pcIdx = 0; pcIdx < NUM_PC_JOYSTICKS; ++pcIdx) - { - if ((m_JoystickToKeys || IsPCJoystickOn(pcIdx)) && !m_JoystickHandler->m_PCJoystickState[pcIdx].Captured) - EnableInit = true; - } - EnableMenuItem(IDM_INIT_JOYSTICK, EnableInit); - - // Check "Initialize Joysticks" menu item if any joystick is already captured - CheckMenuItem(IDM_INIT_JOYSTICK, m_JoystickHandler->m_PCJoystickState[0].Captured); + // Check "Rescan Joysticks" menu item if any joystick is found + CheckMenuItem(IDM_INIT_JOYSTICK, m_JoystickHandler->m_JoystickDevs.size() != 0); // Enable axes menu items if any PC joystick is assigned to the related BBC joystick for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) @@ -680,7 +694,7 @@ void BeebWin::UpdateJoystickMenu() for (int pcIdx = 0; pcIdx < _countof(JoystickMenuIdsType::Joysticks); ++pcIdx) { - auto* dev = m_JoystickHandler->m_PCJoystickState[pcIdx].Dev; + auto* dev = m_JoystickHandler->GetDev(pcIdx); if (dev) { SetMenuItemText(JoystickMenuIds[bbcIdx].Joysticks[pcIdx], dev->DisplayString()); @@ -763,17 +777,21 @@ void BeebWin::ProcessAutoloadJoystickMapCommand(void) /****************************************************************************/ void BeebWin::ResetJoystick() { - for (int pcIdx = 0; pcIdx < NUM_PC_JOYSTICKS; ++pcIdx) + for (auto& dev : m_JoystickHandler->m_JoystickDevs) { - auto& state = m_JoystickHandler->m_PCJoystickState[pcIdx]; - state.Captured = false; - TranslateJoystick(pcIdx); + if (dev.m_Captured) + { + dev.m_Captured = false; + TranslateJoystick(dev.m_JoyIndex); + } } } /****************************************************************************/ bool BeebWin::ScanJoysticks(bool verbose) { + ResetJoystick(); + if (!m_JoystickHandler->m_DirectInputInitialized) m_JoystickHandler->InitDirectInput(); @@ -813,20 +831,35 @@ bool BeebWin::ScanJoysticks(bool verbose) } /****************************************************************************/ -bool BeebWin::InitJoystick(bool verbose) +void BeebWin::InitJoystick(bool verbose) { - bool Success = true; ResetJoystick(); - if (GetPCJoystick(0) || GetPCJoystick(1) || m_JoystickToKeys) + if (m_JoystickToKeys) { for (int pcIdx = 0; pcIdx < NUM_PC_JOYSTICKS; ++pcIdx) { - if (IsPCJoystickOn(pcIdx) || m_JoystickToKeys) - Success = CaptureJoystick(pcIdx, verbose); + CaptureJoystick(pcIdx, verbose); } + } + + int pcJoy1 = GetPCJoystick(0); + if (pcJoy1 != 0) + { + if (!m_JoystickToKeys || pcJoy1 > NUM_PC_JOYSTICKS) + CaptureJoystick(pcJoy1 - 1, verbose); + } + int pcJoy2 = GetPCJoystick(1); + if (pcJoy2 != 0 && pcJoy2 != pcJoy1) + { + if (!m_JoystickToKeys || pcJoy2 > NUM_PC_JOYSTICKS) + CaptureJoystick(pcJoy2 - 1, verbose); + } + + if (GetPCJoystick(0) || GetPCJoystick(1) || m_JoystickToKeys) + { if (!m_JoystickTimerRunning) { SetTimer(m_hWnd, 3, 20, NULL); @@ -843,22 +876,20 @@ bool BeebWin::InitJoystick(bool verbose) } UpdateJoystickMenu(); - - return Success; } /****************************************************************************/ bool BeebWin::CaptureJoystick(int Index, bool verbose) { bool success = false; - JoystickDev* dev = m_JoystickHandler->m_PCJoystickState[Index].Dev; + JoystickDev* dev = m_JoystickHandler->GetDev(Index); if (dev) { HRESULT hr = m_JoystickHandler->OpenDevice(m_hWnd, dev); if (!FAILED(hr)) { - m_JoystickHandler->m_PCJoystickState[Index].Captured = true; + dev->m_Captured = true; success = true; } else if (verbose) @@ -972,8 +1003,8 @@ void BeebWin::ScaleMousestick(unsigned int x, unsigned int y) // Translate joystick position changes to key up or down message void BeebWin::TranslateAxes(int joyId, unsigned int axesState) { - auto& joyState = m_JoystickHandler->m_PCJoystickState[joyId]; - unsigned int& prevAxes = joyState.PrevAxes; + JoystickDev* dev = m_JoystickHandler->GetDev(joyId); + unsigned int& prevAxes = dev->m_PrevAxes; int vkeys = BEEB_VKEY_JOY_START + joyId * (JOYSTICK_MAX_AXES + JOYSTICK_MAX_BTNS); if (axesState != prevAxes) @@ -996,15 +1027,14 @@ void BeebWin::TranslateAxes(int joyId, unsigned int axesState) /****************************************************************************/ void BeebWin::TranslateJoystickButtons(int joyId, unsigned int buttons) { - const int BUTTON_COUNT = 32; - auto& joyState = m_JoystickHandler->m_PCJoystickState[joyId]; - unsigned int& prevBtns = joyState.PrevBtns; + JoystickDev* dev = m_JoystickHandler->GetDev(joyId); + unsigned int& prevBtns = dev->m_PrevBtns; int vkeys = BEEB_VKEY_JOY_START + JOYSTICK_MAX_AXES + joyId * (JOYSTICK_MAX_AXES + JOYSTICK_MAX_BTNS); if (buttons != prevBtns) { - for (int btnId = 0; btnId < BUTTON_COUNT; ++btnId) + for (int btnId = 0; btnId < JOYSTICK_MAX_BTNS; ++btnId) { if ((buttons & ~prevBtns) & (1 << btnId)) { @@ -1040,12 +1070,14 @@ void BeebWin::TranslateOrSendKey(int vkey, bool keyUp) void BeebWin::TranslateJoystick(int joyId) { auto& handler = m_JoystickHandler; - auto& joyState = handler->m_PCJoystickState[joyId]; - auto* joyDev = joyState.Dev; + auto* joyDev = handler->GetDev(joyId); bool success = false; DWORD buttons = 0; - if (joyState.Captured && joyDev->Update()) + if (!joyDev) + return; + + if (joyDev->m_Captured && joyDev->Update()) { success = true; buttons = joyDev->GetButtons(); @@ -1054,10 +1086,10 @@ void BeebWin::TranslateJoystick(int joyId) // If joystick is disabled or error occurs, reset all axes and buttons if (!success) { - if (joyState.Captured) + if (joyDev->m_Captured) { // Reset 'captured' flag and update menu entry - joyState.Captured = false; + joyDev->m_Captured = false; UpdateJoystickMenu(); } } @@ -1094,34 +1126,66 @@ void BeebWin::TranslateJoystick(int joyId) } // Joystick to keyboard mapping - if (m_JoystickToKeys && success) + if (joyId < NUM_PC_JOYSTICKS && m_JoystickToKeys && success) { auto axes = joyDev->GetAxesState(m_JoystickToKeysThreshold); TranslateAxes(joyId, axes); TranslateJoystickButtons(joyId, buttons); - handler->m_PCJoystickState[joyId].JoystickToKeysActive = true; + joyDev->m_JoystickToKeysActive = true; } // Make sure to reset keyboard state - if (!m_JoystickToKeys && handler->m_PCJoystickState[joyId].JoystickToKeysActive) + if (!m_JoystickToKeys && joyDev->m_JoystickToKeysActive) { TranslateAxes(joyId, 0); TranslateJoystickButtons(joyId, 0); - handler->m_PCJoystickState[joyId].JoystickToKeysActive = false; + joyDev->m_JoystickToKeysActive = false; } } /*****************************************************************************/ void BeebWin::UpdateJoysticks() { + int translated = 0; + // Don't update joysticks if not in foreground if (!m_active && m_JoystickTarget == nullptr) return; - for (int idx = 0; idx < NUM_PC_JOYSTICKS; ++idx) + // Translate first four joysticks if joystick to keyboard is enabled + if (m_JoystickToKeys) + { + for (int idx = 0; idx < NUM_PC_JOYSTICKS; ++idx) + { + JoystickDev* dev = m_JoystickHandler->GetDev(idx); + if (dev && dev->m_Captured) + { + TranslateJoystick(idx); + translated |= setbit(idx); + } + } + } + + // Translate joystick for BBC joystick 1 + if (m_JoystickConfig[0].Enabled && m_JoystickConfig[0].PCStick != 0) { - if (m_JoystickHandler->m_PCJoystickState[0].Captured) - TranslateJoystick(idx); + int pcStick = m_JoystickConfig[0].PCStick - 1; + if (!(translated & setbit(pcStick))) + { + TranslateJoystick(pcStick); + translated |= setbit(pcStick); + } + } + + // Translate joystick for BBC joystick 2 + if (m_JoystickConfig[1].Enabled && m_JoystickConfig[1].PCStick != 0) + { + int pcStick = m_JoystickConfig[1].PCStick - 1; + if (!(translated & setbit(pcStick))) + { + TranslateJoystick(pcStick); + translated |= setbit(pcStick); + } } } diff --git a/Src/JoystickHandler.h b/Src/JoystickHandler.h index 7446a60f..ea6b47bb 100644 --- a/Src/JoystickHandler.h +++ b/Src/JoystickHandler.h @@ -77,10 +77,14 @@ struct JoystickDev DIJOYSTATE2 m_JoyState; int m_Instance{ 0 }; - int m_Order{ -1 }; + bool m_OnOrderList{ false }; int m_JoyIndex{ -1 }; bool m_Configured{ false }; bool m_Present{ false }; + bool m_Captured{ false }; + unsigned int m_PrevAxes{ 0 }; + unsigned int m_PrevBtns{ 0 }; + bool m_JoystickToKeysActive{ false }; JoystickId Id() { return m_Id; } std::string DisplayString(); @@ -98,30 +102,30 @@ struct JoystickDev ~JoystickDev(); }; -struct PCJoystickState -{ - JoystickDev* Dev{ nullptr }; - int JoyIndex{ -1 }; - bool Captured{ false }; - unsigned int PrevAxes{ 0 }; - unsigned int PrevBtns{ 0 }; - bool JoystickToKeysActive{ false }; -}; - struct JoystickHandler { bool m_DirectInputInitialized{ false }; HRESULT m_DirectInputInitResult{ E_FAIL }; LPDIRECTINPUT8 m_pDirectInput{ nullptr }; + DIJOYCONFIG m_PreferredJoyCfg{}; + bool m_HavePreferredJoyCfg{ false }; std::vector m_JoystickDevs; - PCJoystickState m_PCJoystickState[NUM_PC_JOYSTICKS]; std::vector m_JoystickOrder; HRESULT InitDirectInput(void); void AddDeviceInstance(const DIDEVICEINSTANCE*); HRESULT ScanJoysticks(void); HRESULT OpenDevice(HWND mainWindow, JoystickDev* dev); + JoystickDev* GetDev(int pcIdx) + { + return (pcIdx >= 0 && static_cast(pcIdx) < m_JoystickDevs.size()) ? &m_JoystickDevs[pcIdx] : nullptr; + } + bool IsCaptured(int pcIdx) + { + JoystickDev* dev = GetDev(pcIdx); + return dev && dev->m_Captured; + } JoystickHandler() {} ~JoystickHandler(); diff --git a/Src/beebwin.h b/Src/beebwin.h index 5522fa27..8b05bf09 100644 --- a/Src/beebwin.h +++ b/Src/beebwin.h @@ -613,7 +613,7 @@ class BeebWin { void Load1770DiscImage(const char *FileName, int Drive, DiscType Type); void LoadTape(void); bool ScanJoysticks(bool verbose = false); - bool InitJoystick(bool verbose = false); + void InitJoystick(bool verbose = false); bool CaptureJoystick(int Index, bool verbose); void ResetJoystick(void); void RestoreState(void); From e1d4762db0d8ed113fdf81929625e05d49748715 Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Sun, 28 Feb 2021 20:42:04 +0100 Subject: [PATCH 32/38] Added Change Joystick Order dialog --- Src/BeebEm.rc | 28 +++++- Src/BeebEm.vcxproj | 2 + Src/BeebEm.vcxproj.filters | 6 ++ Src/JoystickHandler.cpp | 23 +++++ Src/JoystickHandler.h | 1 + Src/JoystickOrderDialog.cpp | 164 ++++++++++++++++++++++++++++++++++++ Src/JoystickOrderDialog.h | 30 +++++++ Src/beebemrc.h | 30 ++----- Src/beebwin.cpp | 7 ++ 9 files changed, 264 insertions(+), 27 deletions(-) create mode 100644 Src/JoystickOrderDialog.cpp create mode 100644 Src/JoystickOrderDialog.h diff --git a/Src/BeebEm.rc b/Src/BeebEm.rc index f04f03b9..8fa07dcd 100644 --- a/Src/BeebEm.rc +++ b/Src/BeebEm.rc @@ -109,7 +109,7 @@ BEGIN CONTROL "^ ~",IDK_CARET,"Button",BS_OWNERDRAW | WS_TABSTOP,243,19,17,14 CONTROL "SFT LK",IDK_SHIFT_LOCK,"Button",BS_OWNERDRAW | WS_TABSTOP,9,62,29,14 CONTROL "Unassign",IDK_UNASSIGN,"Button",BS_OWNERDRAW | WS_TABSTOP,9,86,60,14 - CONTROL "Reset Mapping",IDK_RESET_MAPPING,"Button",WS_TABSTOP,9,101,60,14 + PUSHBUTTON "Reset Mapping",IDK_RESET_MAPPING,9,101,60,14 END IDD_KEYBOARD_LINKS DIALOG 0, 0, 160, 66 @@ -259,6 +259,17 @@ BEGIN PUSHBUTTON "Save Config",IDC_SAVE,235,136,50,14 END +IDD_JOYSTICKORDER DIALOGEX 0, 0, 170, 133 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Joystick Order" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,113,113,50,14 + LISTBOX IDC_JOYSTICKLIST,7,7,141,103,WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_JOYSTICKSPIN,"msctls_updown32",UDS_ARROWKEYS,151,35,12,34 + CONTROL "Show disconnected",IDC_JOYSTICKSHOWALL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,115,79,10 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -322,6 +333,14 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 197 END + + IDD_JOYSTICKORDER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 163 + TOPMARGIN, 7 + BOTTOMMARGIN, 127 + END END #endif // APSTUDIO_INVOKED @@ -740,6 +759,7 @@ BEGIN MENUITEM "Save Joystick Mapping...", IDM_SAVEJOYMAP END MENUITEM "Rescan Joysticks", IDM_INIT_JOYSTICK + MENUITEM "Change Joystick Order", IDM_JOYSTICKORDER MENUITEM SEPARATOR MENUITEM "&Freeze when inactive", IDM_FREEZEINACTIVE MENUITEM "&Hide Cursor", IDM_HIDECURSOR @@ -788,18 +808,18 @@ END // TEXTINCLUDE // -1 TEXTINCLUDE +1 TEXTINCLUDE BEGIN "beebemrc.h\0" END -2 TEXTINCLUDE +2 TEXTINCLUDE BEGIN "#include ""winresrc.h""\r\n" "\0" END -3 TEXTINCLUDE +3 TEXTINCLUDE BEGIN "\r\n" "\0" diff --git a/Src/BeebEm.vcxproj b/Src/BeebEm.vcxproj index 3d326eeb..40797d9c 100644 --- a/Src/BeebEm.vcxproj +++ b/Src/BeebEm.vcxproj @@ -197,6 +197,7 @@ + @@ -344,6 +345,7 @@ + diff --git a/Src/BeebEm.vcxproj.filters b/Src/BeebEm.vcxproj.filters index 6b221c45..ae99f78e 100644 --- a/Src/BeebEm.vcxproj.filters +++ b/Src/BeebEm.vcxproj.filters @@ -249,6 +249,9 @@ Source Files + + Source Files + @@ -500,6 +503,9 @@ Header Files + + Header Files + diff --git a/Src/JoystickHandler.cpp b/Src/JoystickHandler.cpp index 1c22c5ba..341c9481 100644 --- a/Src/JoystickHandler.cpp +++ b/Src/JoystickHandler.cpp @@ -371,6 +371,28 @@ JoystickDev::JoystickDev(JoystickDev&& r) r.m_Present = false; } +/****************************************************************************/ +JoystickDev& JoystickDev::operator=(JoystickDev&& r) +{ + m_GUIDInstance = r.m_GUIDInstance; + m_Id = r.m_Id; + std::swap(m_Name, r.m_Name); + std::swap(m_Device, r.m_Device); + m_PresentAxes = r.m_PresentAxes; + m_PresentButtons = r.m_PresentButtons; + m_Instance = r.m_Instance; + m_OnOrderList = r.m_OnOrderList; + m_JoyIndex = r.m_JoyIndex; + m_Configured = r.m_Configured; + m_Present = r.m_Present; + m_Captured = r.m_Captured; + m_PrevAxes = r.m_PrevAxes; + m_PrevBtns = r.m_PrevBtns; + m_JoystickToKeysActive = r.m_JoystickToKeysActive; + r.m_Present = false; + return *this; +} + /****************************************************************************/ void JoystickDev::CloseDevice() { @@ -784,6 +806,7 @@ void BeebWin::ResetJoystick() dev.m_Captured = false; TranslateJoystick(dev.m_JoyIndex); } + dev.CloseDevice(); } } diff --git a/Src/JoystickHandler.h b/Src/JoystickHandler.h index ea6b47bb..1ac17ea4 100644 --- a/Src/JoystickHandler.h +++ b/Src/JoystickHandler.h @@ -99,6 +99,7 @@ struct JoystickDev JoystickDev(const JoystickDev&) = delete; JoystickDev(JoystickDev&& r); JoystickDev& operator=(const JoystickDev&) = delete; + JoystickDev& operator=(JoystickDev&& r); ~JoystickDev(); }; diff --git a/Src/JoystickOrderDialog.cpp b/Src/JoystickOrderDialog.cpp new file mode 100644 index 00000000..9a7c24c6 --- /dev/null +++ b/Src/JoystickOrderDialog.cpp @@ -0,0 +1,164 @@ +/**************************************************************** +BeebEm - BBC Micro and Master 128 Emulator +Copyright (C) 2021 Tadeusz Kijkowski + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +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. + +You should have received a copy of the GNU General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. +****************************************************************/ + +#include "JoystickOrderDialog.h" +#include "main.h" +#include "beebemrc.h" +#include "JoystickHandler.h" + +#include + +class JoystickOrderDialog +{ +public: + HWND m_hwndDlg{ nullptr }; + HWND m_hwndList{ nullptr }; + JoystickHandler* m_handler{ nullptr }; + bool m_showAll{ false }; + + void PopulateJoystickList() + { + SendMessage(m_hwndList, LB_RESETCONTENT, 0, 0); + int orderIndex = 0; + for (auto& order : m_handler->m_JoystickOrder) + { + int joyIndex = order.JoyIndex; + if (joyIndex != -1 || m_showAll) + { + int index = static_cast(SendMessage(m_hwndList, LB_ADDSTRING, 0, (LPARAM)order.Name.c_str())); + SendMessage(m_hwndList, LB_SETITEMDATA, index, (LPARAM)orderIndex); + } + ++orderIndex; + } + } + + void MoveSelected(int delta) + { + int listCount = static_cast(SendMessage(m_hwndList, LB_GETCOUNT, 0, 0)); + int curSel = SendMessage(m_hwndList, LB_GETCURSEL, 0, 0); + + if (curSel == LB_ERR || delta == 0) + return; + + if (delta < -1) + delta = -1; + else if (delta > 1) + delta = 1; + + int other = curSel + delta; + if (other < 0 || other >= listCount) + return; + + int curSelIndex = static_cast(SendMessage(m_hwndList, LB_GETITEMDATA, curSel, 0)); + int otherIndex = static_cast(SendMessage(m_hwndList, LB_GETITEMDATA, other, 0)); + + JoystickOrderEntry& curEntry = m_handler->m_JoystickOrder[curSelIndex]; + JoystickOrderEntry& otherEntry = m_handler->m_JoystickOrder[otherIndex]; + std::swap(curEntry, otherEntry); + + if (curEntry.JoyIndex != -1 && otherEntry.JoyIndex != -1) + { + std::swap(m_handler->m_JoystickDevs[curEntry.JoyIndex], m_handler->m_JoystickDevs[otherEntry.JoyIndex]); + std::swap(curEntry.JoyIndex, otherEntry.JoyIndex); + } + + if (curSel < other) + { + SendMessage(m_hwndList, LB_DELETESTRING, other, 0); + SendMessage(m_hwndList, LB_DELETESTRING, curSel, 0); + + SendMessage(m_hwndList, LB_INSERTSTRING, curSel, (LPARAM)curEntry.Name.c_str()); + SendMessage(m_hwndList, LB_SETITEMDATA, curSel, (LPARAM)curSelIndex); + + SendMessage(m_hwndList, LB_INSERTSTRING, other, (LPARAM)otherEntry.Name.c_str()); + SendMessage(m_hwndList, LB_SETITEMDATA, other, (LPARAM)otherIndex); + } + else + { + SendMessage(m_hwndList, LB_DELETESTRING, curSel, 0); + SendMessage(m_hwndList, LB_DELETESTRING, other, 0); + + SendMessage(m_hwndList, LB_INSERTSTRING, other, (LPARAM)otherEntry.Name.c_str()); + SendMessage(m_hwndList, LB_SETITEMDATA, other, (LPARAM)otherIndex); + + SendMessage(m_hwndList, LB_INSERTSTRING, curSel, (LPARAM)curEntry.Name.c_str()); + SendMessage(m_hwndList, LB_SETITEMDATA, curSel, (LPARAM)curSelIndex); + } + + SendMessage(m_hwndList, LB_SETCURSEL, other, 0); + } + + INT_PTR DlgProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) + { + switch (message) + { + case WM_INITDIALOG: + m_hwndDlg = hwndDlg; + m_hwndList = GetDlgItem(hwndDlg, IDC_JOYSTICKLIST); + PopulateJoystickList(); + return TRUE; + + case WM_NOTIFY: + if (wParam == IDC_JOYSTICKSPIN) + { + LPNMUPDOWN lpnmud = (LPNMUPDOWN)lParam; + if (lpnmud->hdr.code == UDN_DELTAPOS) + { + MoveSelected(lpnmud->iDelta); + lpnmud->iDelta = 0; + return TRUE; + } + } + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + case IDCANCEL: + EndDialog(hwndDlg, wParam); + return TRUE; + case IDC_JOYSTICKSHOWALL: + m_showAll = IsDlgButtonChecked(hwndDlg, IDC_JOYSTICKSHOWALL); + PopulateJoystickList(); + return TRUE; + } + } + return FALSE; + } + + JoystickOrderDialog(JoystickHandler* handler) : m_handler(handler) {}; +}; + +INT_PTR CALLBACK JoystickOrderDlgProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (message == WM_INITDIALOG) + SetWindowLongPtr(hwndDlg, DWLP_USER, lParam); + + JoystickOrderDialog* dlg = reinterpret_cast(GetWindowLongPtr(hwndDlg, DWLP_USER)); + if (!dlg) + return FALSE; + return dlg->DlgProc(hwndDlg, message, wParam, lParam); +} + +void ShowJoystickOrderDialog(HWND hwndParent, JoystickHandler* handler) +{ + JoystickOrderDialog dlg{ handler }; + DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_JOYSTICKORDER), hwndParent, JoystickOrderDlgProc, reinterpret_cast(&dlg)); +} diff --git a/Src/JoystickOrderDialog.h b/Src/JoystickOrderDialog.h new file mode 100644 index 00000000..c7afdadf --- /dev/null +++ b/Src/JoystickOrderDialog.h @@ -0,0 +1,30 @@ +/**************************************************************** +BeebEm - BBC Micro and Master 128 Emulator +Copyright (C) 2021 Tadeusz Kijkowski + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +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. + +You should have received a copy of the GNU General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. +****************************************************************/ + +#ifndef JOYSTICKORDERDIALOG_H +#define JOYSTICKORDERDIALOG_H + +#include + +struct JoystickHandler; + +void ShowJoystickOrderDialog(HWND hwndParent, JoystickHandler* handler); + +#endif diff --git a/Src/beebemrc.h b/Src/beebemrc.h index c6121b7a..7a13d69e 100644 --- a/Src/beebemrc.h +++ b/Src/beebemrc.h @@ -1,24 +1,3 @@ -/**************************************************************** -BeebEm - BBC Micro and Master 128 Emulator -Copyright (C) 1994 Nigel Magnay -Copyright (C) 1997 Mike Wyatt - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -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. - -You should have received a copy of the GNU General Public -License along with this program; if not, write to the Free -Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -Boston, MA 02110-1301, USA. -****************************************************************/ - //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by BeebEm.rc @@ -109,6 +88,7 @@ Boston, MA 02110-1301, USA. #define IDR_ACCELERATORS 115 #define IDD_ROMCONFIG 116 #define IDD_SELECT_KEY 117 +#define IDD_JOYSTICKORDER 118 #define IDC_DEBUGBREAK 1010 #define IDC_DEBUGINFO 1015 #define IDC_DEBUGCOMMAND 1016 @@ -189,6 +169,9 @@ Boston, MA 02110-1301, USA. #define IDC_ASSIGNED_KEYS 1090 #define IDC_ASSIGNED_KEYS_LBL 1091 #define IDK_RESET_MAPPING 1092 +#define IDC_JOYSTICKLIST 1093 +#define IDC_JOYSTICKSPIN 1094 +#define IDC_JOYSTICKSHOWALL 1095 #define IDM_ABOUT 40001 #define IDM_DISC 40002 #define IDM_LOADDISC0 40002 @@ -451,6 +434,7 @@ Boston, MA 02110-1301, USA. #define IDM_JOY2_DIGITAL_MOUSESTICK 40312 #define IDM_JOY2_PRIMARY 40313 #define IDM_JOY2_SECONDARY1 40314 +#define IDM_JOYSTICKORDER 40317 #define IDM_CAPTUREMOUSE 40318 #define IDC_STATIC -1 @@ -459,9 +443,9 @@ Boston, MA 02110-1301, USA. #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 118 +#define _APS_NEXT_RESOURCE_VALUE 119 #define _APS_NEXT_COMMAND_VALUE 40319 -#define _APS_NEXT_CONTROL_VALUE 1093 +#define _APS_NEXT_CONTROL_VALUE 1096 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index 8631757b..5443f0b7 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -79,6 +79,7 @@ Boston, MA 02110-1301, USA. #include "Master512CoPro.h" #include "FolderSelectDialog.h" #include "DebugTrace.h" +#include "JoystickOrderDialog.h" using namespace Gdiplus; @@ -3309,6 +3310,12 @@ void BeebWin::HandleCommand(int MenuId) ProcessAutoloadJoystickMapCommand(); break; + case IDM_JOYSTICKORDER: + ShowJoystickOrderDialog(m_hWnd, m_JoystickHandler.get()); + UpdateJoystickMenu(); + InitJoystick(); + break; + case IDM_FREEZEINACTIVE: m_FreezeWhenInactive = !m_FreezeWhenInactive; CheckMenuItem(IDM_FREEZEINACTIVE, m_FreezeWhenInactive); From 0ce00c0ca509f975c7c93e3aca1e56453507af8f Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Tue, 2 Mar 2021 23:07:30 +0100 Subject: [PATCH 33/38] Restored copyright notice in beebemrc.h --- Src/beebemrc.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Src/beebemrc.h b/Src/beebemrc.h index 7a13d69e..f5be0857 100644 --- a/Src/beebemrc.h +++ b/Src/beebemrc.h @@ -1,3 +1,21 @@ +/**************************************************************** +BeebEm - BBC Micro and Master 128 Emulator +Copyright (C) 1994 Nigel Magnay +Copyright (C) 1997 Mike Wyatt +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +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. +You should have received a copy of the GNU General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. +****************************************************************/ + //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by BeebEm.rc From 9950ee82593cdf7276d8b3909a1b9664603c9cb8 Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Tue, 2 Mar 2021 23:10:23 +0100 Subject: [PATCH 34/38] Restored copyright notice in beebemrc.h --- Src/beebemrc.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Src/beebemrc.h b/Src/beebemrc.h index f5be0857..d0d3538d 100644 --- a/Src/beebemrc.h +++ b/Src/beebemrc.h @@ -2,14 +2,17 @@ BeebEm - BBC Micro and Master 128 Emulator Copyright (C) 1994 Nigel Magnay Copyright (C) 1997 Mike Wyatt + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + 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. + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, From 19597fa3e959e2546569d50e840fed4cf82de2c3 Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Wed, 3 Mar 2021 07:43:36 +0100 Subject: [PATCH 35/38] Limited use of auto keyword to make code more readable and removed unneeded backward compatiblity for jmap files --- Src/JoystickHandler.cpp | 22 ++++++++++------------ Src/JoystickOrderDialog.cpp | 6 +++--- Src/SelectKeyDialog.cpp | 32 ++------------------------------ Src/userkybd.cpp | 8 ++++---- 4 files changed, 19 insertions(+), 49 deletions(-) diff --git a/Src/JoystickHandler.cpp b/Src/JoystickHandler.cpp index 341c9481..62e15677 100644 --- a/Src/JoystickHandler.cpp +++ b/Src/JoystickHandler.cpp @@ -102,7 +102,7 @@ bool JoystickOrderEntry::from_string(const std::string& str) { Name = ""; - auto split = str.find('#'); + size_t split = str.find('#'); if (split != std::string::npos) { ++split; @@ -328,7 +328,7 @@ void JoystickDev::EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi) if (pdidoi->dwType & DIDFT_BUTTON) { - auto instance = DIDFT_GETINSTANCE(pdidoi->dwType); + WORD instance = DIDFT_GETINSTANCE(pdidoi->dwType); if (instance < JOYSTICK_MAX_BTNS) m_PresentButtons |= setbit(instance); } @@ -716,7 +716,7 @@ void BeebWin::UpdateJoystickMenu() for (int pcIdx = 0; pcIdx < _countof(JoystickMenuIdsType::Joysticks); ++pcIdx) { - auto* dev = m_JoystickHandler->GetDev(pcIdx); + JoystickDev* dev = m_JoystickHandler->GetDev(pcIdx); if (dev) { SetMenuItemText(JoystickMenuIds[bbcIdx].Joysticks[pcIdx], dev->DisplayString()); @@ -799,7 +799,7 @@ void BeebWin::ProcessAutoloadJoystickMapCommand(void) /****************************************************************************/ void BeebWin::ResetJoystick() { - for (auto& dev : m_JoystickHandler->m_JoystickDevs) + for (JoystickDev& dev : m_JoystickHandler->m_JoystickDevs) { if (dev.m_Captured) { @@ -1092,8 +1092,7 @@ void BeebWin::TranslateOrSendKey(int vkey, bool keyUp) /****************************************************************************/ void BeebWin::TranslateJoystick(int joyId) { - auto& handler = m_JoystickHandler; - auto* joyDev = handler->GetDev(joyId); + JoystickDev* joyDev = m_JoystickHandler->GetDev(joyId); bool success = false; DWORD buttons = 0; @@ -1151,7 +1150,7 @@ void BeebWin::TranslateJoystick(int joyId) // Joystick to keyboard mapping if (joyId < NUM_PC_JOYSTICKS && m_JoystickToKeys && success) { - auto axes = joyDev->GetAxesState(m_JoystickToKeysThreshold); + DWORD axes = joyDev->GetAxesState(m_JoystickToKeysThreshold); TranslateAxes(joyId, axes); TranslateJoystickButtons(joyId, buttons); joyDev->m_JoystickToKeysActive = true; @@ -1256,7 +1255,7 @@ void BeebWin::ResetJoystickMap() void BeebWin::ResetJoyMap(JoyMap* joymap) { // Initialize all input to unassigned - for (auto& mapping : *joymap) + for (KeyPair& mapping : *joymap) { mapping[0].row = mapping[1].row = UNASSIGNED_ROW; mapping[0].col = mapping[1].col = 0; @@ -1607,7 +1606,7 @@ static void EraseNthValue(Preferences& preferences, const char* format, int idx) /****************************************************************************/ void BeebWin::WriteJoystickOrder() { - auto& order = m_JoystickHandler->m_JoystickOrder; + std::vector& order = m_JoystickHandler->m_JoystickOrder; for (UINT idx = 0; idx < MAX_JOYSTICK_ORDER; ++idx) { if (idx < order.size()) @@ -1627,7 +1626,6 @@ void BeebWin::ReadJoystickPreferences() { DWORD dword; bool flag; - auto& handler = m_JoystickHandler; /* Clear joystick configuration */ for (int bbcIdx = 0; bbcIdx < NUM_BBC_JOYSTICKS; ++bbcIdx) @@ -1708,7 +1706,7 @@ void BeebWin::ReadJoystickPreferences() else m_JoystickSensitivity = 1.0; - handler->m_JoystickOrder.clear(); + m_JoystickHandler->m_JoystickOrder.clear(); for (int idx = 0; idx < MAX_JOYSTICK_ORDER; ++idx) { std::string value; @@ -1716,7 +1714,7 @@ void BeebWin::ReadJoystickPreferences() if (!GetNthStringValue(m_Preferences, CFG_OPTIONS_JOYSTICK_ORDER, idx + 1, value) || !entry.from_string(value)) break; - handler->m_JoystickOrder.push_back(entry); + m_JoystickHandler->m_JoystickOrder.push_back(entry); } } diff --git a/Src/JoystickOrderDialog.cpp b/Src/JoystickOrderDialog.cpp index 9a7c24c6..26b69f6f 100644 --- a/Src/JoystickOrderDialog.cpp +++ b/Src/JoystickOrderDialog.cpp @@ -37,12 +37,12 @@ class JoystickOrderDialog { SendMessage(m_hwndList, LB_RESETCONTENT, 0, 0); int orderIndex = 0; - for (auto& order : m_handler->m_JoystickOrder) + for (JoystickOrderEntry& entry : m_handler->m_JoystickOrder) { - int joyIndex = order.JoyIndex; + int joyIndex = entry.JoyIndex; if (joyIndex != -1 || m_showAll) { - int index = static_cast(SendMessage(m_hwndList, LB_ADDSTRING, 0, (LPARAM)order.Name.c_str())); + int index = static_cast(SendMessage(m_hwndList, LB_ADDSTRING, 0, (LPARAM)entry.Name.c_str())); SendMessage(m_hwndList, LB_SETITEMDATA, index, (LPARAM)orderIndex); } ++orderIndex; diff --git a/Src/SelectKeyDialog.cpp b/Src/SelectKeyDialog.cpp index 2d6151bb..d4f4e71a 100644 --- a/Src/SelectKeyDialog.cpp +++ b/Src/SelectKeyDialog.cpp @@ -346,7 +346,7 @@ static std::string toupper(const std::string& src) { std::string result = src; - for (auto& c : result) + for (char& c : result) { c = static_cast(toupper(c)); } @@ -375,33 +375,5 @@ int SelectKeyDialog::JoyVKeyByName(const char* Name) if (iter != nameToVKeyMap.end()) return iter->second; - // Read axis number - if (uname.substr(0, 3) != "JOY") - return -1; - uname = uname.substr(3); - char* endp; - long joyIdx = strtol(uname.c_str(), &endp, 10); - if (joyIdx < 1 || joyIdx > NUM_PC_JOYSTICKS || !endp || endp == uname.c_str()) - return -1; - uname = uname.substr(endp - uname.c_str()); - - if (uname.substr(0, 4) != "AXIS") - return -1; - uname = uname.substr(4); - long axis = strtol(uname.c_str(), &endp, 10); - if (axis < 1 || axis > JOYSTICK_MAX_AXES || !endp || endp == uname.c_str()) - return -1; - uname = uname.substr(endp - uname.c_str()); - - int pos = 0; - if (uname.size() != 1) - return -1; - if (uname[0] == '-') - pos = 0; - else if (uname[0] == '+') - pos = 1; - else - return -1; - - return BEEB_VKEY_JOY_START + (joyIdx - 1) * (JOYSTICK_MAX_AXES + JOYSTICK_MAX_BTNS) + (axis - 1) * 2 + pos; + return -1; } diff --git a/Src/userkybd.cpp b/Src/userkybd.cpp index 6353092b..0277700b 100644 --- a/Src/userkybd.cpp +++ b/Src/userkybd.cpp @@ -199,7 +199,7 @@ const BBCKey* GetBBCKeyByResId(int ctrlId) { resIdToKeyMapType keyMap{}; - for (auto& theKey : BBCKeys) + for (const BBCKey& theKey : BBCKeys) keyMap[theKey.ctrlId] = &theKey; return keyMap; } (); @@ -222,7 +222,7 @@ const BBCKey* GetBBCKeyByName(const std::string& name) { nameToKeyMapType keyMap{}; - for (auto& theKey : BBCKeys) + for (const BBCKey& theKey : BBCKeys) keyMap[theKey.name] = &theKey; for (auto& alias : BBCKeyAliases) @@ -250,7 +250,7 @@ const BBCKey* GetBBCKeyByRowAndCol(int row, int col) { posToKeyMapType keyMap{}; - for (auto& theKey : BBCKeys) + for (const BBCKey& theKey : BBCKeys) keyMap[posPair{ theKey.row, theKey.column }] = &theKey; return keyMap; } (); @@ -759,7 +759,7 @@ static void UpdateAssignedKeysCount(int row, int col, int change, bool redrawCol static void RedrawAllKeys() { - for (auto& key : BBCKeys) + for (const BBCKey& key : BBCKeys) { HWND keyCtrl = GetDlgItem(hwndUserKeyboard, key.ctrlId); SetKeyColour(GetKeyColour(key.ctrlId), keyCtrl); From 77b8faefd268ad8a0462c8755953cfbfb4c48488 Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Wed, 3 Mar 2021 12:26:21 +0100 Subject: [PATCH 36/38] Fixed enabling mousestick --- Src/JoystickHandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/JoystickHandler.cpp b/Src/JoystickHandler.cpp index 62e15677..518a5155 100644 --- a/Src/JoystickHandler.cpp +++ b/Src/JoystickHandler.cpp @@ -636,9 +636,9 @@ void BeebWin::UpdateJoystickConfig(int bbcIdx) m_JoystickConfig[bbcIdx].PCStick = MenuIdToStick(bbcIdx, m_MenuIdSticks[bbcIdx]); m_JoystickConfig[bbcIdx].PCAxes = MenuIdToAxes(bbcIdx, m_MenuIdAxes[bbcIdx]); m_JoystickConfig[bbcIdx].AnalogMousestick = - (static_cast(m_MenuIdAxes[bbcIdx]) == JoystickMenuIds[bbcIdx].AnalogMousestick); + (static_cast(m_MenuIdSticks[bbcIdx]) == JoystickMenuIds[bbcIdx].AnalogMousestick); m_JoystickConfig[bbcIdx].DigitalMousestick = - (static_cast(m_MenuIdAxes[bbcIdx]) == JoystickMenuIds[bbcIdx].DigitalMousestick); + (static_cast(m_MenuIdSticks[bbcIdx]) == JoystickMenuIds[bbcIdx].DigitalMousestick); } /****************************************************************************/ From c1ddf853a3b391532b40f0cff5b64087725c97b2 Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Sat, 22 May 2021 00:57:41 +0200 Subject: [PATCH 37/38] Move joystick configuration to separate menu - Move joystick configuration to "Joysticks" menu - Update menus.html and keyboard.html --- Help/keyboard.html | 4 +- Help/menus.html | 209 ++++++++++++++++++++++++++++++--------------- Src/BeebEm.rc | 68 ++++++++------- 3 files changed, 175 insertions(+), 106 deletions(-) diff --git a/Help/keyboard.html b/Help/keyboard.html index 4f6b432c..902ca957 100644 --- a/Help/keyboard.html +++ b/Help/keyboard.html @@ -241,7 +241,7 @@

Mapping PC Joystick to BBC Keys

To enable translating PC joystick actions to BBC keys, use menu item "Options → Joystick To Keyboard → Enable Joystick To Keyboard". - BeemEm supports mapping up to two joysticks or gamepads to BBC keys, + BeemEm supports mapping up to four joysticks or gamepads to BBC keys, with each joystick actions (button presses or stick movements) mapped separately.

@@ -275,7 +275,7 @@

Mapping PC Joystick to BBC Keys

"Options → Joystick To Keyboard → Save Joystick Mapping...". You can save your mapping as the default user joystick mapping file (DefaultUser.jmap) or save to a different file. If you are creating - a game-specific joystick mapping, you can save the mapping in the same + a game-specific joystick mapping, save the mapping in the same directory and with the same name as your game disk image, with extension changed to ".jmap". This will make BeebEm automatically use the joystick mapping for that disk image, if that is enabled with menu item diff --git a/Help/menus.html b/Help/menus.html index 1bcd8a04..8fb6165d 100644 --- a/Help/menus.html +++ b/Help/menus.html @@ -534,6 +534,144 @@

AMX Menu

+

Joysticks Menu

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Primary JoystickSwitch on or off primary Beeb analogue joystick support + and select which PC device the emulator should use for the primary + Beeb analogue joystick. Click on the currently selected device + again to disable primary Beeb analogue joystick. +
First PC JoystickUse first PC joystick or gamepad. + First and third PC joystick buttons are mapped to the primary Beeb + joystick fire button. Second and fourth PC joystick buttons are mapped to + the secondary Beeb joystick fire button, unless the secondary Beeb + joystick support is separately enabled and mapped to another contoller. + The "First PC Joystick" menu text is replaced with actual joystick or + gamepad model name, if it is known to the emulator. + Calibrate the joystick through the Windows control panel. +
Second PC JoystickUse second PC joystick or gamepad. + First and third PC joystick buttons are mapped to the primary Beeb + joystick firs button. Second and fourth PC joystick buttons are mapped to + the secondary Beeb joystick fire button, unless the secondary Beeb + joystick support is separately enabled and mapped to another controller. + The "Second PC Joystick" menu text is replaced with actual joystick or + gamepad model name, if it is known to the emulator. +
Analogue MousestickMap Mouse position to analogue Beeb joystick position. + Allows you to use the mouse as a joystick. The left mouse button + controls the joystick 1 fire button and the right mouse button controls + the joystick 2 fire button, unless the secondary Beeb joystick support + is separately enabled. +
Digital MousestickMap Mouse movements to digital Beeb joystick movements. + The left mouse button controls the joystick 1 fire button and the right + mouse button controls the joystick 2 fire button, unless the secondary + Beeb joystick is separately enabled. +
Primary Stick / Right ThumbstickSelect between primary Joystick/Thumbstick and secondary + Joystick/Thumbstick on a gamepad or other controller with more than one + analogue sticks. In other words select between primary and secondary + set of axes. + If both primary and secondary Beeb analogue joysticks are mapped to two + different sticks on the same PC contoller, joystick button mapping + changes so that first and third PC joystick buttons are mapped + to the first Beeb joystick fire button, and second and fourth PC joystick + buttons are mapped to the second Beeb joystick fire button. +
Secondary JoystickSwitch on or off secondary Beeb analogue joystick support + and select which PC device the emulator should use for the secondary + Beeb analogue joystick. Click on the currently selected device + again to disable secondary Beeb analogue joystick. + Available devices selection is the same as for primary Beeb analogue + joystick. +
Enable Joystick To KeyboardSwitch on or off mapping PC joystick to BBC keyboard. + See the Keyboard Mappings section. +
Autoload Joystick MappingSwitch on or off automatically loading joystick mapping + files for disk or tape images. +
Define Joystick MappingAllows you to configure joystick to keyboard mapping. + See the Keyboard Mappings section. +
Reset Joystick MappingClears joystick to keyboard mapping table.
Load Joystick MappingLoads joystick to keyboard mapping from a file. +
Save Joystick MappingSaves joystick to keyboard mapping to a file. +
Rescan JoysticksRepeat scanning of available PC joysticks and + gamepads and retry joystick initialization. You can use this + option if you have connected your USB gamepad after starting + BeebEm. +
Change Joystick OrderChange assigned joystick order. This can be useful + if you have more than two joysticks to map Beeb analogue + joystick to other than first two PC joysticks or to switch PC joysticks + mapped to Beeb keyboard without having to remap each key. +
+

Hardware Menu

@@ -698,77 +836,6 @@

Hardware Menu

Options Menu

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - @@ -664,7 +676,7 @@

Joysticks Menu

-
JoystickSwitch on or off PC/Beeb analogue joystick support. - Calibrate the joystick through the Windows control panel. -
Analogue MousestickSwitch on or off the mapping of Mouse position to - analogue Beeb joystick position. Allows you to use the mouse - as a joystick. The left mouse button controls the joystick 1 - fire button and the right mouse button controls the joystick 2 - fire button. -
Digital MousestickSwitch on or off the mapping of Mouse movements to - digital Beeb joystick movements. The left mouse button controls - the joystick 1 fire button and the right mouse button controls - the joystick 2 fire button. -
Enable Joystick To KeyboardSwitch on or off mapping PC joystick to BBC keyboard. - See the Keyboard Mappings section. -
Autoload Joystick MappingSwitch on or off automatically loading joystick mapping - files for disk or tape images. -
Define Joystick MappingAllows you to configure joystick to keyboard mapping. - See the Keyboard Mappings section. -
Reset Joystick MappingClears joystick to keyboard mapping table.
Load Joystick MappingLoads joystick to keyboard mapping from a file. -
Save Joystick MappingSaves joystick to keyboard mapping to a file. -
Reinitialize JoystickRetries joystick initialization. You can use - this if you connected your USB gamepad after starting BeebEm. -
Freeze when inactive When selected BeebEm will freeze when you switch diff --git a/Src/BeebEm.rc b/Src/BeebEm.rc index 8fa07dcd..bce3e2cf 100644 --- a/Src/BeebEm.rc +++ b/Src/BeebEm.rc @@ -662,6 +662,41 @@ BEGIN MENUITEM "Adjust -30%", IDM_AMX_ADJUSTM30 MENUITEM "Adjust -50%", IDM_AMX_ADJUSTM50 END + POPUP "&Joysticks" + BEGIN + POPUP "Primary Joystick" + BEGIN + MENUITEM "First PC &Joystick", IDM_JOYSTICK + MENUITEM "Second PC Joystick", IDM_JOY1_PCJOY2 + MENUITEM "&Analogue Mousestick", IDM_ANALOGUE_MOUSESTICK + MENUITEM "&Digital Mousestick", IDM_DIGITAL_MOUSESTICK + MENUITEM SEPARATOR + MENUITEM "Primary Stick", IDM_JOY1_PRIMARY + MENUITEM "Right Thumbstick", IDM_JOY1_SECONDARY1 + END + POPUP "Secondary Joystick" + BEGIN + MENUITEM "First PC &Joystick", IDM_JOY2_PCJOY1 + MENUITEM "Second PC Joystick", IDM_JOY2_PCJOY2 + MENUITEM "&Analogue Mousestick", IDM_JOY2_ANALOGUE_MOUSESTICK + MENUITEM "&Digital Mousestick", IDM_JOY2_DIGITAL_MOUSESTICK + MENUITEM SEPARATOR + MENUITEM "Primary Stick", IDM_JOY2_PRIMARY + MENUITEM "Right Thumbstick", IDM_JOY2_SECONDARY1 + END + POPUP "Joystick To Keyboard" + BEGIN + MENUITEM "Enable Joystick To Keyboard", IDM_JOYSTICK_TO_KEYS + MENUITEM "Autoload Joystick Mapping", IDM_AUTOLOADJOYMAP + MENUITEM SEPARATOR + MENUITEM "Define Joystick Mapping", IDM_DEFINEJOYMAP + MENUITEM "Reset Joystick Mapping", IDM_RESETJOYMAP + MENUITEM "Load Joystick Mapping...", IDM_LOADJOYMAP + MENUITEM "Save Joystick Mapping...", IDM_SAVEJOYMAP + END + MENUITEM "Rescan Joysticks", IDM_INIT_JOYSTICK + MENUITEM "Change Joystick Order", IDM_JOYSTICKORDER + END POPUP "Hard&ware" BEGIN POPUP "&BBC Model" @@ -728,39 +763,6 @@ BEGIN END POPUP "&Options" BEGIN - POPUP "Joystick" - BEGIN - MENUITEM "First PC &Joystick", IDM_JOYSTICK - MENUITEM "Second PC Joystick", IDM_JOY1_PCJOY2 - MENUITEM "&Analogue Mousestick", IDM_ANALOGUE_MOUSESTICK - MENUITEM "&Digital Mousestick", IDM_DIGITAL_MOUSESTICK - MENUITEM SEPARATOR - MENUITEM "Primary Stick", IDM_JOY1_PRIMARY - MENUITEM "Right Thumbstick", IDM_JOY1_SECONDARY1 - END - POPUP "Second Joystick" - BEGIN - MENUITEM "First PC &Joystick", IDM_JOY2_PCJOY1 - MENUITEM "Second PC Joystick", IDM_JOY2_PCJOY2 - MENUITEM "&Analogue Mousestick", IDM_JOY2_ANALOGUE_MOUSESTICK - MENUITEM "&Digital Mousestick", IDM_JOY2_DIGITAL_MOUSESTICK - MENUITEM SEPARATOR - MENUITEM "Primary Stick", IDM_JOY2_PRIMARY - MENUITEM "Right Thumbstick", IDM_JOY2_SECONDARY1 - END - POPUP "Joystick To Keyboard" - BEGIN - MENUITEM "Enable Joystick To Keyboard", IDM_JOYSTICK_TO_KEYS - MENUITEM "Autoload Joystick Mapping", IDM_AUTOLOADJOYMAP - MENUITEM SEPARATOR - MENUITEM "Define Joystick Mapping", IDM_DEFINEJOYMAP - MENUITEM "Reset Joystick Mapping", IDM_RESETJOYMAP - MENUITEM "Load Joystick Mapping...", IDM_LOADJOYMAP - MENUITEM "Save Joystick Mapping...", IDM_SAVEJOYMAP - END - MENUITEM "Rescan Joysticks", IDM_INIT_JOYSTICK - MENUITEM "Change Joystick Order", IDM_JOYSTICKORDER - MENUITEM SEPARATOR MENUITEM "&Freeze when inactive", IDM_FREEZEINACTIVE MENUITEM "&Hide Cursor", IDM_HIDECURSOR MENUITEM SEPARATOR From 0f489c30691641a77725de12d964dd58495ad4b4 Mon Sep 17 00:00:00 2001 From: Tadek Kijkowski Date: Sat, 22 May 2021 02:35:59 +0200 Subject: [PATCH 38/38] Add joystick sensitivity and threshold to menu - Add joystick sensitivity to menu - Add joystick to keyboard threshold to menu - Update menus.html --- Help/menus.html | 18 +++++++++++++++--- Src/BeebEm.rc | 14 ++++++++++++++ Src/JoystickHandler.cpp | 30 +++++++++++++++++++---------- Src/JoystickOrderDialog.cpp | 2 +- Src/beebemrc.h | 10 +++++++++- Src/beebwin.cpp | 38 +++++++++++++++++++++++++++++++++++++ 6 files changed, 97 insertions(+), 15 deletions(-) diff --git a/Help/menus.html b/Help/menus.html index 8fb6165d..4d3dbd5a 100644 --- a/Help/menus.html +++ b/Help/menus.html @@ -622,6 +622,11 @@

Joysticks Menu

ThresholdMinimum PC joystick deflection to generate BBC key press.
Autoload Joystick Mapping Switch on or off automatically loading joystick mapping @@ -653,10 +658,17 @@

Joysticks Menu

Joystick SensitivityJoystick sensitivity factor applied when mapping + PC Joystick or Mouse position to Beeb joystick. +
Rescan JoysticksRepeat scanning of available PC joysticks and - gamepads and retry joystick initialization. You can use this + Repeats scanning of available PC joysticks and + gamepads and retries joysticks initialization. You can use this option if you have connected your USB gamepad after starting BeebEm.
Change Joystick OrderChange assigned joystick order. This can be useful + Changes assigned joystick order. This can be useful if you have more than two joysticks to map Beeb analogue joystick to other than first two PC joysticks or to switch PC joysticks mapped to Beeb keyboard without having to remap each key. diff --git a/Src/BeebEm.rc b/Src/BeebEm.rc index bce3e2cf..24b904ec 100644 --- a/Src/BeebEm.rc +++ b/Src/BeebEm.rc @@ -687,6 +687,13 @@ BEGIN POPUP "Joystick To Keyboard" BEGIN MENUITEM "Enable Joystick To Keyboard", IDM_JOYSTICK_TO_KEYS + POPUP "Threshold" + BEGIN + MENUITEM "12.5%", IDM_JOY_KEY_THRESHOLD_12_5 + MENUITEM "25%", IDM_JOY_KEY_THRESHOLD_25 + MENUITEM "50%", IDM_JOY_KEY_THRESHOLD_50 + MENUITEM "75%", IDM_JOY_KEY_THRESHOLD_75 + END MENUITEM "Autoload Joystick Mapping", IDM_AUTOLOADJOYMAP MENUITEM SEPARATOR MENUITEM "Define Joystick Mapping", IDM_DEFINEJOYMAP @@ -694,6 +701,13 @@ BEGIN MENUITEM "Load Joystick Mapping...", IDM_LOADJOYMAP MENUITEM "Save Joystick Mapping...", IDM_SAVEJOYMAP END + POPUP "Joystick Sensitivity" + BEGIN + MENUITEM "50%", IDM_JOY_SENSITIVITY_50 + MENUITEM "100%", IDM_JOY_SENSITIVITY_100 + MENUITEM "200%", IDM_JOY_SENSITIVITY_200 + MENUITEM "300%", IDM_JOY_SENSITIVITY_300 + END MENUITEM "Rescan Joysticks", IDM_INIT_JOYSTICK MENUITEM "Change Joystick Order", IDM_JOYSTICKORDER END diff --git a/Src/JoystickHandler.cpp b/Src/JoystickHandler.cpp index 518a5155..850af780 100644 --- a/Src/JoystickHandler.cpp +++ b/Src/JoystickHandler.cpp @@ -733,6 +733,16 @@ void BeebWin::UpdateJoystickMenu() for (int axesIdx = 0; axesIdx < _countof(JoystickMenuIdsType::Axes); ++axesIdx) EnableMenuItem(JoystickMenuIds[bbcIdx].Axes[axesIdx], EnableAxes); } + + CheckMenuItem(IDM_JOY_SENSITIVITY_50, m_JoystickSensitivity == 0.5); + CheckMenuItem(IDM_JOY_SENSITIVITY_100, m_JoystickSensitivity == 1.0); + CheckMenuItem(IDM_JOY_SENSITIVITY_200, m_JoystickSensitivity == 2.0); + CheckMenuItem(IDM_JOY_SENSITIVITY_300, m_JoystickSensitivity == 3.0); + + CheckMenuItem(IDM_JOY_KEY_THRESHOLD_12_5, m_JoystickToKeysThreshold == 4096); + CheckMenuItem(IDM_JOY_KEY_THRESHOLD_25, m_JoystickToKeysThreshold == 8192); + CheckMenuItem(IDM_JOY_KEY_THRESHOLD_50, m_JoystickToKeysThreshold == 16384); + CheckMenuItem(IDM_JOY_KEY_THRESHOLD_75, m_JoystickToKeysThreshold == 24756); } /****************************************************************************/ @@ -981,23 +991,23 @@ void BeebWin::ScaleMousestick(unsigned int x, unsigned int y) { for (int index = 0; index < 2; ++index) { - int XPos = (m_XWinSize - x) * 65535 / m_XWinSize; - int YPos = (m_YWinSize - y) * 65535 / m_YWinSize; - if (m_JoystickConfig[index].AnalogMousestick) { - JoystickX[index] = XPos; - JoystickY[index] = YPos; + ScaleJoystick(index, x, y, 0, 0, m_XWinSize, m_YWinSize); } else if (m_JoystickConfig[index].DigitalMousestick) { - const int Threshold = 2000; + const int Threshold = 4000; + + /* Keep 32768.0 double to convert from unsigned int */ + double XPos = (((m_XWinSize - x) * 65535 / m_XWinSize) - 32768.0) * m_JoystickSensitivity; + double YPos = (((m_YWinSize - y) * 65535 / m_YWinSize) - 32768.0) * m_JoystickSensitivity; - if (XPos < 32768 - Threshold) + if (XPos < -Threshold) { JoystickX[index] = 0; } - else if (XPos > 32768 + Threshold) + else if (XPos > Threshold) { JoystickX[index] = 65535; } @@ -1006,11 +1016,11 @@ void BeebWin::ScaleMousestick(unsigned int x, unsigned int y) JoystickX[index] = 32768; } - if (YPos < 32768 - Threshold) + if (YPos < -Threshold) { JoystickY[index] = 0; } - else if (YPos > 32768 + Threshold) + else if (YPos > Threshold) { JoystickY[index] = 65535; } diff --git a/Src/JoystickOrderDialog.cpp b/Src/JoystickOrderDialog.cpp index 26b69f6f..ddfd7a64 100644 --- a/Src/JoystickOrderDialog.cpp +++ b/Src/JoystickOrderDialog.cpp @@ -135,7 +135,7 @@ class JoystickOrderDialog EndDialog(hwndDlg, wParam); return TRUE; case IDC_JOYSTICKSHOWALL: - m_showAll = IsDlgButtonChecked(hwndDlg, IDC_JOYSTICKSHOWALL); + m_showAll = (IsDlgButtonChecked(hwndDlg, IDC_JOYSTICKSHOWALL) == BST_CHECKED); PopulateJoystickList(); return TRUE; } diff --git a/Src/beebemrc.h b/Src/beebemrc.h index d0d3538d..50059b47 100644 --- a/Src/beebemrc.h +++ b/Src/beebemrc.h @@ -457,6 +457,14 @@ Boston, MA 02110-1301, USA. #define IDM_JOY2_SECONDARY1 40314 #define IDM_JOYSTICKORDER 40317 #define IDM_CAPTUREMOUSE 40318 +#define IDM_JOY_SENSITIVITY_50 40319 +#define IDM_JOY_SENSITIVITY_100 40320 +#define IDM_JOY_SENSITIVITY_200 40321 +#define IDM_JOY_SENSITIVITY_300 40322 +#define IDM_JOY_KEY_THRESHOLD_12_5 40323 +#define IDM_JOY_KEY_THRESHOLD_25 40324 +#define IDM_JOY_KEY_THRESHOLD_50 40325 +#define IDM_JOY_KEY_THRESHOLD_75 40326 #define IDC_STATIC -1 // Next default values for new objects @@ -465,7 +473,7 @@ Boston, MA 02110-1301, USA. #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 119 -#define _APS_NEXT_COMMAND_VALUE 40319 +#define _APS_NEXT_COMMAND_VALUE 40327 #define _APS_NEXT_CONTROL_VALUE 1096 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/Src/beebwin.cpp b/Src/beebwin.cpp index 5443f0b7..0df240cd 100644 --- a/Src/beebwin.cpp +++ b/Src/beebwin.cpp @@ -3316,6 +3316,44 @@ void BeebWin::HandleCommand(int MenuId) InitJoystick(); break; + case IDM_JOY_SENSITIVITY_50: + case IDM_JOY_SENSITIVITY_100: + case IDM_JOY_SENSITIVITY_200: + case IDM_JOY_SENSITIVITY_300: + if (MenuId == IDM_JOY_SENSITIVITY_50) { + m_JoystickSensitivity = 0.5; + } + else if (MenuId == IDM_JOY_SENSITIVITY_200) { + m_JoystickSensitivity = 2.0; + } + else if (MenuId == IDM_JOY_SENSITIVITY_300) { + m_JoystickSensitivity = 3.0; + } + else { + m_JoystickSensitivity = 1.0; + } + UpdateJoystickMenu(); + break; + + case IDM_JOY_KEY_THRESHOLD_12_5: + case IDM_JOY_KEY_THRESHOLD_25: + case IDM_JOY_KEY_THRESHOLD_50: + case IDM_JOY_KEY_THRESHOLD_75: + if (MenuId == IDM_JOY_KEY_THRESHOLD_25) { + m_JoystickToKeysThreshold = 8192; + } + else if (MenuId == IDM_JOY_KEY_THRESHOLD_50) { + m_JoystickToKeysThreshold = 16384; + } + else if (MenuId == IDM_JOY_KEY_THRESHOLD_75) { + m_JoystickToKeysThreshold = 24576; + } + else { + m_JoystickToKeysThreshold = 4096; + } + UpdateJoystickMenu(); + break; + case IDM_FREEZEINACTIVE: m_FreezeWhenInactive = !m_FreezeWhenInactive; CheckMenuItem(IDM_FREEZEINACTIVE, m_FreezeWhenInactive);