MacOS: Handle keyboard outside of Avalonia (#38)

This commit is contained in:
Angelo 2024-01-16 12:45:00 +01:00 committed by GitHub
parent 4b949021f5
commit e00e669334
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 244 additions and 9 deletions

View file

@ -23,6 +23,9 @@
#include "Windows/Renderer.h"
#include "Windows/SoundManager.h"
#include "Windows/WindowsKeyManager.h"
#elif __APPLE__
#include "Linux/SdlSoundManager.h"
#include "MacOS/MacOSKeyManager.h"
#else
#include "Linux/SdlRenderer.h"
#include "Linux/SdlSoundManager.h"
@ -104,6 +107,8 @@ extern "C" {
if(!noInput) {
#ifdef _WIN32
_keyManager.reset(new WindowsKeyManager(_emu.get(), (HWND)_windowHandle));
#elif __APPLE__
_keyManager.reset(new MacOSKeyManager(_emu.get()));
#else
_keyManager.reset(new LinuxKeyManager(_emu.get()));
#endif

63
MacOS/MacOSKeyManager.h Normal file
View file

@ -0,0 +1,63 @@
#pragma once
#include <unordered_map>
#include <vector>
#include <thread>
#include "Utilities/AutoResetEvent.h"
#include "Shared/Interfaces/IKeyManager.h"
#include "Shared/KeyDefinitions.h"
class Emulator;
class MacOSKeyManager : public IKeyManager
{
private:
static constexpr int BaseMouseButtonIndex = 0x200;
static constexpr int BaseGamepadIndex = 0x1000;
Emulator* _emu;
vector<KeyDefinition> _keyDefinitions;
bool _keyState[0x205];
std::unordered_map<uint16_t, string> _keyNames;
std::unordered_map<string, uint16_t> _keyCodes;
bool _disableAllKeys;
void* _eventMonitor;
//Mapping of MacOS keycodes to Avalonia keycodes
uint16_t _keyCodeMap[128] = {
44, 62, 47, 49, 51, 50, 69, 67, 46, 65,
154, 45, 60, 66, 48, 61, 68, 63, 35, 36,
37, 38, 40, 39, 141, 43, 41, 143, 42, 34,
151, 58, 64, 149, 52, 59, 6, 55, 53, 152,
54, 140, 150, 142, 145, 57, 56, 144, 3, 18,
146, 2, 6, 13, 71, 70, 116, 8, 120, 118,
117, 121, 119, 0, 106, 88, 0, 84, 0, 85,
0, 5, 131, 130, 129, 89, 6, 0, 87, 107,
108, 141, 74, 75, 76, 77, 78, 79, 80, 81,
109, 82, 83, 150, 154, 148, 94, 95, 96, 92,
97, 98, 12, 100, 9, 102, 105, 103, 0, 99,
72, 101, 0, 104, 31, 22, 19, 32, 93, 21,
91, 20, 90, 23, 25, 26, 24, 0
};
void HandleModifiers(uint32_t flags);
public:
MacOSKeyManager(Emulator* emu);
virtual ~MacOSKeyManager();
void RefreshState();
bool IsKeyPressed(uint16_t key);
bool IsMouseButtonPressed(MouseButton button);
std::vector<uint16_t> GetPressedKeys();
string GetKeyName(uint16_t key);
uint16_t GetKeyCode(string keyName);
void UpdateDevices();
bool SetKeyState(uint16_t scanCode, bool state);
void ResetKeyState();
void SetDisabled(bool disabled);
};

146
MacOS/MacOSKeyManager.mm Normal file
View file

@ -0,0 +1,146 @@
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#include <algorithm>
#include "MacOSKeyManager.h"
//The MacOS SDK defines a global function 'Debugger', colliding with Mesen's Debugger class
//Redefine it temporarily so the headers don't cause compilation errors due to this
#define Debugger MesenDebugger
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
#include "Shared/KeyDefinitions.h"
#include "Shared/SettingTypes.h"
#undef Debugger
MacOSKeyManager::MacOSKeyManager(Emulator* emu)
{
_emu = emu;
ResetKeyState();
_keyDefinitions = KeyDefinition::GetSharedKeyDefinitions();
for(KeyDefinition &keyDef : _keyDefinitions) {
_keyNames[keyDef.keyCode] = keyDef.name;
_keyCodes[keyDef.name] = keyDef.keyCode;
}
_disableAllKeys = false;
_eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:(NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged)
handler:^ NSEvent* (NSEvent* event) {
if(_emu->GetSettings()->CheckFlag(EmulationFlags::InBackground)) {
//Allow UI to handle key-events when main window is not in focus
return event;
}
if([event type] == NSEventTypeKeyDown && ([event modifierFlags] & NSEventModifierFlagCommand) != 0) {
//Pass through command-based keydown events so cmd+Q etc still works
return event;
}
if([event type] == NSEventTypeFlagsChanged) {
HandleModifiers((uint32_t) [event modifierFlags]);
} else {
uint16_t mappedKeyCode = [event keyCode] >= 128 ? 0 : _keyCodeMap[[event keyCode]];
_keyState[mappedKeyCode] = ([event type] == NSEventTypeKeyDown);
}
return nil;
}];
}
MacOSKeyManager::~MacOSKeyManager()
{
[NSEvent removeMonitor:(id) _eventMonitor];
}
void MacOSKeyManager::HandleModifiers(uint32_t flags)
{
_keyState[116] = (flags & NX_DEVICELSHIFTKEYMASK) != 0; //Left shift
_keyState[117] = (flags & NX_DEVICERSHIFTKEYMASK) != 0; //Right shift
_keyState[118] = (flags & NX_DEVICELCTLKEYMASK) != 0; //Left ctrl
_keyState[119] = (flags & NX_DEVICERCTLKEYMASK) != 0; //Right ctrl
_keyState[120] = (flags & NX_DEVICELALTKEYMASK) != 0; //Left alt/option
_keyState[121] = (flags & NX_DEVICERALTKEYMASK) != 0; //Right alt/option
_keyState[70] = (flags & NX_DEVICELCMDKEYMASK) != 0; //Left cmd
_keyState[71] = (flags & NX_DEVICERCMDKEYMASK) != 0; //Right cmd
}
void MacOSKeyManager::RefreshState()
{
//TODO: NOT IMPLEMENTED YET
//Only needed to detect poll controller input
}
bool MacOSKeyManager::IsKeyPressed(uint16_t key)
{
if(_disableAllKeys || key == 0) {
return false;
}
if(key < 0x205) {
return _keyState[key] != 0;
}
return false;
}
bool MacOSKeyManager::IsMouseButtonPressed(MouseButton button)
{
return _keyState[MacOSKeyManager::BaseMouseButtonIndex + (int)button];
}
vector<uint16_t> MacOSKeyManager::GetPressedKeys()
{
vector<uint16_t> pressedKeys;
for(int i = 0; i < 0x205; i++) {
if(_keyState[i]) {
pressedKeys.push_back(i);
}
}
return pressedKeys;
}
string MacOSKeyManager::GetKeyName(uint16_t key)
{
auto keyDef = _keyNames.find(key);
if(keyDef != _keyNames.end()) {
return keyDef->second;
}
return "";
}
uint16_t MacOSKeyManager::GetKeyCode(string keyName)
{
auto keyDef = _keyCodes.find(keyName);
if(keyDef != _keyCodes.end()) {
return keyDef->second;
}
return 0;
}
void MacOSKeyManager::UpdateDevices()
{
//TODO: NOT IMPLEMENTED YET
//Only needed to detect newly plugged in devices
}
bool MacOSKeyManager::SetKeyState(uint16_t scanCode, bool state)
{
if(scanCode < 0x205 && _keyState[scanCode] != state) {
_keyState[scanCode] = state;
return true;
}
return false;
}
void MacOSKeyManager::ResetKeyState()
{
memset(_keyState, 0, sizeof(_keyState));
}
void MacOSKeyManager::SetDisabled(bool disabled)
{
_disableAllKeys = disabled;
}

View file

@ -562,6 +562,11 @@ namespace Mesen.Windows
return;
}
if(OperatingSystem.IsMacOS()) {
//Keyhandler handles key internally on macOS
return;
}
if(e.Key != Key.None) {
if(_isLinux && _pendingKeyUpEvents.TryGetValue(e.Key, out IDisposable? cancelTimer)) {
//Cancel any pending key up event
@ -576,15 +581,15 @@ namespace Mesen.Windows
//Prevent menu/window from handling these keys to avoid issue with custom shortcuts
e.Handled = true;
}
if(OperatingSystem.IsMacOS() && !IsModifierKey(e.Key) && e.KeyModifiers != KeyModifiers.Meta) {
//Prevent alert sound on macOS
e.Handled = true;
}
}
private void OnPreviewKeyUp(object? sender, KeyEventArgs e)
{
if(OperatingSystem.IsMacOS()) {
//Keyhandler handles key internally on macOS
return;
}
if(e.Key != Key.None) {
if(_isLinux) {
//Process keyup events after 1ms on Linux to prevent key repeat from triggering key up/down repeatedly

View file

@ -184,4 +184,4 @@ void WindowsKeyManager::SetDisabled(bool disabled)
void WindowsKeyManager::ResetKeyState()
{
memset(_keyState, 0, sizeof(_keyState));
}
}

View file

@ -94,7 +94,12 @@ ifneq ($(STATICLINK),false)
LINKOPTIONS += -static-libgcc -static-libstdc++
endif
CXXFLAGS = -fPIC -Wall --std=c++17 $(MESENFLAGS) $(SDL2INC) -I $(realpath ./) -I $(realpath ./Core) -I $(realpath ./Utilities) -I $(realpath ./Linux)
ifeq ($(MESENOS),osx)
LINKOPTIONS += -framework Foundation -framework Cocoa
endif
CXXFLAGS = -fPIC -Wall --std=c++17 $(MESENFLAGS) $(SDL2INC) -I $(realpath ./) -I $(realpath ./Core) -I $(realpath ./Utilities) -I $(realpath ./Linux) -I $(realpath ./MacOS)
OBJCXXFLAGS = $(CXXFLAGS) -framework Foundation -framework Cocoa
CFLAGS = -fPIC -Wall $(MESENFLAGS)
OBJFOLDER := obj.$(MESENPLATFORM)
@ -125,6 +130,13 @@ SEVENZIPOBJ := $(SEVENZIPSRC:.c=.o)
LUASRC := $(shell find Lua -name '*.c')
LUAOBJ := $(LUASRC:.c=.o)
ifeq ($(MESENOS),osx)
MACOSSRC := $(shell find MacOS -name '*.mm')
else
MACOSSRC :=
endif
MACOSOBJ := $(MACOSSRC:.mm=.o)
DLLSRC := $(shell find InteropDLL -name '*.cpp')
DLLOBJ := $(DLLSRC:.cpp=.o)
@ -168,10 +180,13 @@ pgohelper: InteropDLL/$(OBJFOLDER)/$(SHAREDLIB)
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
InteropDLL/$(OBJFOLDER)/$(SHAREDLIB): $(SEVENZIPOBJ) $(LUAOBJ) $(UTILOBJ) $(COREOBJ) $(LIBEVDEVOBJ) $(LINUXOBJ) $(DLLOBJ)
%.o: %.mm
$(CXX) $(OBJCXXFLAGS) -c $< -o $@
InteropDLL/$(OBJFOLDER)/$(SHAREDLIB): $(SEVENZIPOBJ) $(LUAOBJ) $(UTILOBJ) $(COREOBJ) $(LIBEVDEVOBJ) $(LINUXOBJ) $(DLLOBJ) $(MACOSOBJ)
mkdir -p bin
mkdir -p InteropDLL/$(OBJFOLDER)
$(CXX) $(CXXFLAGS) $(LINKOPTIONS) $(LINKCHECKUNRESOLVED) -shared -o $(SHAREDLIB) $(DLLOBJ) $(SEVENZIPOBJ) $(LUAOBJ) $(LINUXOBJ) $(LIBEVDEVOBJ) $(UTILOBJ) $(COREOBJ) $(SDL2INC) -pthread $(FSLIB) $(SDL2LIB) $(LIBEVDEVLIB)
$(CXX) $(CXXFLAGS) $(LINKOPTIONS) $(LINKCHECKUNRESOLVED) -shared -o $(SHAREDLIB) $(DLLOBJ) $(SEVENZIPOBJ) $(LUAOBJ) $(LINUXOBJ) $(MACOSOBJ) $(LIBEVDEVOBJ) $(UTILOBJ) $(COREOBJ) $(SDL2INC) -pthread $(FSLIB) $(SDL2LIB) $(LIBEVDEVLIB)
cp $(SHAREDLIB) bin/pgohelperlib.so
mv $(SHAREDLIB) InteropDLL/$(OBJFOLDER)
@ -187,4 +202,5 @@ clean:
rm -r -f $(LINUXOBJ) $(LIBEVDEVOBJ)
rm -r -f $(SEVENZIPOBJ)
rm -r -f $(LUAOBJ)
rm -r -f $(MACOSOBJ)
rm -r -f $(DLLOBJ)