flycast/core/input/keyboard_device.h

489 lines
9.7 KiB
C++

/*
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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "types.h"
#include "gamepad_device.h"
#include "rend/gui.h"
#include <memory>
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(DC_AXIS_LT, 9); // F
set_button(DC_AXIS_RT, 25); // V
set_button(EMU_BTN_MENU, 43); // TAB
set_button(EMU_BTN_FFORWARD, 44); // Space
set_button(DC_AXIS_UP, 12); // I
set_button(DC_AXIS_DOWN, 14); // K
set_button(DC_AXIS_LEFT, 13); // J
set_button(DC_AXIS_RIGHT, 15); // L
set_button(DC_BTN_D, 4); // Q (Coin)
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<InputMapping> getDefaultMapping() override {
return std::make_shared<KeyboardInputMapping>();
}
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 <typename Keycode>
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 <typename Keycode>
void KeyboardDeviceTemplate<Keycode>::keyboard_input(Keycode keycode, bool pressed, int modifier_keys)
{
u8 dc_keycode = convert_keycode(keycode);
// 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;
}
const int port = maple_port();
if (port >= 0 && port < (int)ARRAY_SIZE(kb_shift))
kb_shift[port] = _modifier_keys;
if (dc_keycode != 0 && dc_keycode < 0xE0)
{
gui_keyboard_key(dc_keycode, pressed, _modifier_keys);
if (port >= 0 && port < (int)ARRAY_SIZE(kb_key))
{
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;
}
}
if (gui_keyboard_captured())
{
// chat: disable the keyboard controller. Only accept emu keys (menu, escape...)
set_maple_port(-1);
gamepad_btn_input(dc_keycode, pressed);
set_maple_port(port);
}
// Do not map keyboard keys to gamepad buttons unless the GUI is open
// or the corresponding maple device (if any) isn't a keyboard
else 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);
}