/* Copyright 2021 flyinghead This file is part of Flycast. Flycast 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. Flycast 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 Flycast. If not, see . */ #pragma once #include "types.h" #include "gamepad_device.h" #include "rend/gui.h" #include class KeyboardInputMapping : public InputMapping { public: KeyboardInputMapping() { name = "Keyboard"; set_button(DC_BTN_A, 27); // X set_button(DC_BTN_B, 6); // C set_button(DC_BTN_X, 22); // S set_button(DC_BTN_Y, 7); // D set_button(DC_DPAD_UP, 82); set_button(DC_DPAD_DOWN, 81); set_button(DC_DPAD_LEFT, 80); set_button(DC_DPAD_RIGHT, 79); set_button(DC_BTN_START, 40); // Return set_button(EMU_BTN_TRIGGER_LEFT, 9); // F set_button(EMU_BTN_TRIGGER_RIGHT, 25); // V set_button(EMU_BTN_MENU, 43); // TAB set_button(EMU_BTN_FFORWARD, 44); // Space dirty = false; } }; class KeyboardDevice : public GamepadDevice { protected: KeyboardDevice(int maple_port, const char* apiName, bool remappable = true) : GamepadDevice(maple_port, apiName, remappable) { _name = "Keyboard"; } std::shared_ptr getDefaultMapping() override { return std::make_shared(); } public: const char *get_button_name(u32 code) override { switch (code) { case 0x04: return "A"; case 0x05: return "B"; case 0x06: return "C"; case 0x07: return "D"; case 0x08: return "E"; case 0x09: return "F"; case 0x0A: return "G"; case 0x0B: return "H"; case 0x0C: return "I"; case 0x0D: return "J"; case 0x0E: return "K"; case 0x0F: return "L"; case 0x10: return "M"; case 0x11: return "N"; case 0x12: return "O"; case 0x13: return "P"; case 0x14: return "Q"; case 0x15: return "R"; case 0x16: return "S"; case 0x17: return "T"; case 0x18: return "U"; case 0x19: return "V"; case 0x1A: return "W"; case 0x1B: return "X"; case 0x1C: return "Y"; case 0x1D: return "Z"; case 0x1E: return "1"; case 0x1F: return "2"; case 0x20: return "3"; case 0x21: return "4"; case 0x22: return "5"; case 0x23: return "6"; case 0x24: return "7"; case 0x25: return "8"; case 0x26: return "9"; case 0x27: return "0"; case 0x28: return "Return"; case 0x29: return "Escape"; case 0x2A: return "Backspace"; case 0x2B: return "Tab"; case 0x2C: return "Space"; case 0x2D: return "-"; case 0x2E: return "="; case 0x2F: return "["; case 0x30: return "]"; case 0x31: return "\\"; case 0x32: return "#"; // non-US case 0x33: return ";"; case 0x34: return "'"; case 0x35: return "`"; case 0x36: return ","; case 0x37: return "."; case 0x38: return "/"; case 0x39: return "CapsLock"; case 0x3A: return "F1"; case 0x3B: return "F2"; case 0x3C: return "F3"; case 0x3D: return "F4"; case 0x3E: return "F5"; case 0x3F: return "F6"; case 0x40: return "F7"; case 0x41: return "F8"; case 0x42: return "F9"; case 0x43: return "F10"; case 0x44: return "F11"; case 0x45: return "F12"; case 0x46: return "PrintScreen"; case 0x47: return "ScrollLock"; case 0x48: return "Pause"; case 0x49: return "Insert"; case 0x4A: return "Home"; case 0x4B: return "Page Up"; case 0x4C: return "Delete"; case 0x4D: return "End"; case 0x4E: return "Page Down"; case 0x4F: return "Right"; case 0x50: return "Left"; case 0x51: return "Down"; case 0x52: return "Up"; case 0x53: return "NumLock"; case 0x54: return "Num /"; case 0x55: return "Num *"; case 0x56: return "Num -"; case 0x57: return "Num +"; case 0x58: return "Num Enter"; case 0x59: return "Num 1"; case 0x5A: return "Num 2"; case 0x5B: return "Num 3"; case 0x5C: return "Num 4"; case 0x5D: return "Num 5"; case 0x5E: return "Num 6"; case 0x5F: return "Num 7"; case 0x60: return "Num 8"; case 0x61: return "Num 9"; case 0x62: return "Num 0"; case 0x63: return "Num ."; case 0x64: return "\\"; // non-US case 0x65: return "Application"; case 0x66: return "Power"; case 0x67: return "Num ="; case 0x68: return "F13"; case 0x69: return "F14"; case 0x6A: return "F15"; case 0x6B: return "F16"; case 0x6C: return "F17"; case 0x6D: return "F18"; case 0x6E: return "F19"; case 0x6F: return "F20"; case 0x70: return "F21"; case 0x71: return "F22"; case 0x72: return "F23"; case 0x73: return "F24"; case 0x87: return "Int1"; case 0x88: return "Int2"; case 0x89: return "Yen"; case 0x8A: return "Int4"; case 0x8B: return "Int5"; case 0x8C: return "Int6"; case 0x8D: return "Int7"; case 0x8E: return "Int8"; case 0x8F: return "Int9"; case 0x90: return "Hangul"; case 0x91: return "Hanja"; case 0x92: return "Katakana"; case 0x93: return "Hiragana"; case 0x94: return "Zenkaku/Hankaku"; case 0x95: return "Lang6"; case 0x96: return "Lang7"; case 0x97: return "Lang8"; case 0x98: return "Lang9"; case 0xE0: return "Left Ctrl"; case 0xE1: return "Left Shift"; case 0xE2: return "Left Alt"; case 0xE3: return "Left Meta"; case 0xE4: return "Right Ctrl"; case 0xE5: return "Right Shift"; case 0xE6: return "Right Alt"; case 0xE7: return "Right Meta"; default: return nullptr; } } }; template class KeyboardDeviceTemplate : public KeyboardDevice { public: virtual void keyboard_input(Keycode keycode, bool pressed, int modifier_keys = 0); virtual ~KeyboardDeviceTemplate() = default; protected: KeyboardDeviceTemplate(int maple_port, const char *apiName, bool remappable = true) : KeyboardDevice(maple_port, apiName, remappable), _modifier_keys(0), _kb_used(0) {} virtual u8 convert_keycode(Keycode keycode) = 0; private: int _modifier_keys; u32 _kb_used; }; enum DCKeyboardModifiers { DC_KBMOD_LEFTCTRL = 0x01, DC_KBMOD_LEFTSHIFT = 0x02, DC_KBMOD_LEFTALT = 0x04, DC_KBMOD_LEFTGUI = 0x08, DC_KBMOD_RIGHTCTRL = 0x10, DC_KBMOD_RIGHTSHIFT = 0x20, DC_KBMOD_RIGHTALT = 0x40, DC_KBMOD_S2 = 0x80, }; extern u8 kb_key[4][6]; // normal keys pressed extern u8 kb_shift[4]; // modifier keys pressed (bitmask) static inline void setFlag(int& v, u32 bitmask, bool set) { if (set) v |= bitmask; else v &= ~bitmask; } template void KeyboardDeviceTemplate::keyboard_input(Keycode keycode, bool pressed, int modifier_keys) { const int port = maple_port(); if (port < 0 || port > (int)ARRAY_SIZE(kb_key)) return; u8 dc_keycode = convert_keycode(keycode); if (port < (int)ARRAY_SIZE(kb_key)) { // Some OSes (Mac OS) don't distinguish left and right modifier keys to we set them both. // But not for Alt since Right Alt is used as a special modifier keys on some international // keyboards. switch (dc_keycode) { case 0xE1: // Left Shift case 0xE5: // Right Shift setFlag(_modifier_keys, DC_KBMOD_LEFTSHIFT | DC_KBMOD_RIGHTSHIFT, pressed); break; case 0xE0: // Left Ctrl case 0xE4: // Right Ctrl setFlag(_modifier_keys, DC_KBMOD_LEFTCTRL | DC_KBMOD_RIGHTCTRL, pressed); break; case 0xE2: // Left Alt setFlag(_modifier_keys, DC_KBMOD_LEFTALT, pressed); break; case 0xE6: // Right Alt setFlag(_modifier_keys, DC_KBMOD_RIGHTALT, pressed); break; case 0xE7: // S2 special key setFlag(_modifier_keys, DC_KBMOD_S2, pressed); break; default: break; } kb_shift[port] = _modifier_keys; if (dc_keycode != 0 && dc_keycode < 0xE0) { if (pressed) { if (_kb_used < ARRAY_SIZE(kb_key[port])) { bool found = false; for (u32 i = 0; !found && i < _kb_used; i++) { if (kb_key[port][i] == dc_keycode) found = true; } if (!found) kb_key[port][_kb_used++] = dc_keycode; } } else { for (u32 i = 0; i < _kb_used; i++) { if (kb_key[port][i] == dc_keycode) { _kb_used--; for (u32 j = i; j < ARRAY_SIZE(kb_key[port]) - 1; j++) kb_key[port][j] = kb_key[port][j + 1]; kb_key[port][ARRAY_SIZE(kb_key[port]) - 1] = 0; break; } } } kb_shift[port] |= modifier_keys; } } // Do not map keyboard keys to gamepad buttons unless the GUI is open // or the corresponding maple device (if any) isn't a keyboard if (gui_is_open() || port == (int)ARRAY_SIZE(kb_key) || (settings.platform.system == DC_PLATFORM_DREAMCAST && config::MapleMainDevices[port] != MDT_Keyboard) || (settings.platform.system == DC_PLATFORM_NAOMI && settings.input.JammaSetup != JVS::Keyboard) || settings.platform.system == DC_PLATFORM_ATOMISWAVE) gamepad_btn_input(dc_keycode, pressed); }