mirror of
https://github.com/SourMesen/Mesen2.git
synced 2024-05-11 09:05:33 -04:00
MacOS: Handle keyboard outside of Avalonia (#38)
This commit is contained in:
parent
4b949021f5
commit
e00e669334
|
@ -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
63
MacOS/MacOSKeyManager.h
Normal 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
146
MacOS/MacOSKeyManager.mm
Normal 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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -184,4 +184,4 @@ void WindowsKeyManager::SetDisabled(bool disabled)
|
|||
void WindowsKeyManager::ResetKeyState()
|
||||
{
|
||||
memset(_keyState, 0, sizeof(_keyState));
|
||||
}
|
||||
}
|
||||
|
|
22
makefile
22
makefile
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue