mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
396 lines
14 KiB
C++
396 lines
14 KiB
C++
#include "pch.h"
|
|
#include "Shared/ShortcutKeyHandler.h"
|
|
#include "Shared/SystemActionManager.h"
|
|
#include "Shared/EmuSettings.h"
|
|
#include "Shared/KeyManager.h"
|
|
#include "Shared/Video/VideoDecoder.h"
|
|
#include "Shared/Emulator.h"
|
|
#include "Shared/RewindManager.h"
|
|
#include "Shared/NotificationManager.h"
|
|
#include "Shared/SaveStateManager.h"
|
|
#include "Shared/Movies/MovieManager.h"
|
|
#include "Shared/BaseControlManager.h"
|
|
#include "Shared/Interfaces/IBarcodeReader.h"
|
|
#include "Shared/Interfaces/ITapeRecorder.h"
|
|
#include "Netplay/GameClient.h"
|
|
|
|
ShortcutKeyHandler::ShortcutKeyHandler(Emulator* emu)
|
|
{
|
|
_emu = emu;
|
|
|
|
_keySetIndex = 0;
|
|
_isKeyUp = false;
|
|
_isKeyboardConnected = false;
|
|
_repeatStarted = false;
|
|
_needRepeat = false;
|
|
|
|
_stopThread = false;
|
|
_thread = std::thread([=]() {
|
|
while(!_stopThread) {
|
|
ProcessKeys();
|
|
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(50));
|
|
}
|
|
});
|
|
}
|
|
|
|
ShortcutKeyHandler::~ShortcutKeyHandler()
|
|
{
|
|
_stopThread = true;
|
|
_thread.join();
|
|
}
|
|
|
|
bool ShortcutKeyHandler::IsKeyPressed(EmulatorShortcut shortcut)
|
|
{
|
|
//When running while a keyboard is plugged into the console, disable all keyboard
|
|
//shortcut keys. The pause shortcut is always enabled, allowing it to be used to
|
|
//pause normally, which allows other shortcuts to be used (while paused)
|
|
bool blockKeyboardKeys = shortcut != EmulatorShortcut::Pause && _isKeyboardConnected && !_isPaused;
|
|
|
|
KeyCombination keyComb = _emu->GetSettings()->GetShortcutKey(shortcut, _keySetIndex);
|
|
vector<KeyCombination> supersets = _emu->GetSettings()->GetShortcutSupersets(shortcut, _keySetIndex);
|
|
for(KeyCombination &superset : supersets) {
|
|
if(IsKeyPressed(superset, blockKeyboardKeys)) {
|
|
//A superset is pressed, ignore this subset
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//No supersets are pressed, check if all matching keys are pressed
|
|
return IsKeyPressed(keyComb, blockKeyboardKeys);
|
|
}
|
|
|
|
bool ShortcutKeyHandler::IsKeyPressed(KeyCombination comb, bool blockKeyboardKeys)
|
|
{
|
|
int keyCount = (comb.Key1 ? 1 : 0) + (comb.Key2 ? 1 : 0) + (comb.Key3 ? 1 : 0);
|
|
|
|
if(keyCount == 0 || _pressedKeys.empty()) {
|
|
return false;
|
|
}
|
|
|
|
bool mergeCtrlAltShift = keyCount > 1;
|
|
|
|
return IsKeyPressed(comb.Key1, mergeCtrlAltShift, blockKeyboardKeys) &&
|
|
(comb.Key2 == 0 || IsKeyPressed(comb.Key2, mergeCtrlAltShift, blockKeyboardKeys)) &&
|
|
(comb.Key3 == 0 || IsKeyPressed(comb.Key3, mergeCtrlAltShift, blockKeyboardKeys));
|
|
}
|
|
|
|
bool ShortcutKeyHandler::IsKeyPressed(uint16_t keyCode, bool mergeCtrlAltShift, bool blockKeyboardKeys)
|
|
{
|
|
if(blockKeyboardKeys && keyCode < IKeyManager::BaseMouseButtonIndex) {
|
|
return false;
|
|
}
|
|
|
|
if(keyCode >= 116 && keyCode <= 121 && mergeCtrlAltShift) {
|
|
//Left/right ctrl/alt/shift
|
|
//Return true if either the left or right key is pressed
|
|
return KeyManager::IsKeyPressed(keyCode | 1) || KeyManager::IsKeyPressed(keyCode & ~0x01);
|
|
}
|
|
|
|
return KeyManager::IsKeyPressed(keyCode);
|
|
}
|
|
|
|
bool ShortcutKeyHandler::DetectKeyPress(EmulatorShortcut shortcut)
|
|
{
|
|
if(IsKeyPressed(shortcut)) {
|
|
bool newlyPressed = _prevKeysDown[_keySetIndex].find((uint32_t)shortcut) == _prevKeysDown[_keySetIndex].end();
|
|
_keysDown[_keySetIndex].emplace((uint32_t)shortcut);
|
|
|
|
if(newlyPressed && !_isKeyUp) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ShortcutKeyHandler::DetectKeyRelease(EmulatorShortcut shortcut)
|
|
{
|
|
if(!IsKeyPressed(shortcut)) {
|
|
if(_prevKeysDown[_keySetIndex].find((uint32_t)shortcut) != _prevKeysDown[_keySetIndex].end()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ShortcutKeyHandler::ProcessRunSingleFrame()
|
|
{
|
|
_runSingleFrameRepeatTimer.Reset();
|
|
_needRepeat = true;
|
|
_emu->PauseOnNextFrame();
|
|
}
|
|
|
|
bool ShortcutKeyHandler::IsShortcutAllowed(EmulatorShortcut shortcut, uint32_t shortcutParam)
|
|
{
|
|
bool isRunning = _emu->IsRunning();
|
|
bool isNetplayClient = _emu->GetGameClient()->Connected();
|
|
bool isMoviePlaying = _emu->GetMovieManager()->Playing();
|
|
bool isMovieRecording = _emu->GetMovieManager()->Recording();
|
|
bool isMovieActive = isMoviePlaying || isMovieRecording;
|
|
|
|
switch(shortcut) {
|
|
case EmulatorShortcut::ToggleRewind:
|
|
case EmulatorShortcut::Rewind:
|
|
case EmulatorShortcut::RewindTenSecs:
|
|
case EmulatorShortcut::RewindOneMin:
|
|
return isRunning && !isNetplayClient && !isMovieRecording;
|
|
|
|
case EmulatorShortcut::IncreaseSpeed:
|
|
case EmulatorShortcut::DecreaseSpeed:
|
|
case EmulatorShortcut::MaxSpeed:
|
|
return !isNetplayClient;
|
|
|
|
case EmulatorShortcut::Pause:
|
|
return isRunning && !isNetplayClient;
|
|
|
|
case EmulatorShortcut::Reset:
|
|
case EmulatorShortcut::ExecReset:
|
|
case EmulatorShortcut::PowerCycle:
|
|
case EmulatorShortcut::ExecPowerCycle:
|
|
case EmulatorShortcut::ReloadRom:
|
|
case EmulatorShortcut::ExecReloadRom:
|
|
return isRunning && !isNetplayClient && !isMoviePlaying;
|
|
|
|
case EmulatorShortcut::PowerOff:
|
|
case EmulatorShortcut::ExecPowerOff:
|
|
return isRunning && !isNetplayClient;
|
|
|
|
case EmulatorShortcut::TakeScreenshot:
|
|
return isRunning;
|
|
|
|
case EmulatorShortcut::ToggleCheats:
|
|
return !isNetplayClient && !isMovieActive;
|
|
|
|
case EmulatorShortcut::SelectSaveSlot1: case EmulatorShortcut::SelectSaveSlot2: case EmulatorShortcut::SelectSaveSlot3: case EmulatorShortcut::SelectSaveSlot4: case EmulatorShortcut::SelectSaveSlot5:
|
|
case EmulatorShortcut::SelectSaveSlot6: case EmulatorShortcut::SelectSaveSlot7: case EmulatorShortcut::SelectSaveSlot8: case EmulatorShortcut::SelectSaveSlot9: case EmulatorShortcut::SelectSaveSlot10:
|
|
case EmulatorShortcut::SaveStateSlot1: case EmulatorShortcut::SaveStateSlot2: case EmulatorShortcut::SaveStateSlot3: case EmulatorShortcut::SaveStateSlot4: case EmulatorShortcut::SaveStateSlot5:
|
|
case EmulatorShortcut::SaveStateSlot6: case EmulatorShortcut::SaveStateSlot7: case EmulatorShortcut::SaveStateSlot8: case EmulatorShortcut::SaveStateSlot9: case EmulatorShortcut::SaveStateSlot10:
|
|
case EmulatorShortcut::MoveToNextStateSlot:
|
|
case EmulatorShortcut::MoveToPreviousStateSlot:
|
|
case EmulatorShortcut::SaveStateDialog:
|
|
case EmulatorShortcut::SaveStateToFile:
|
|
case EmulatorShortcut::SaveState:
|
|
return isRunning;
|
|
|
|
case EmulatorShortcut::LoadStateSlot1: case EmulatorShortcut::LoadStateSlot2: case EmulatorShortcut::LoadStateSlot3: case EmulatorShortcut::LoadStateSlot4: case EmulatorShortcut::LoadStateSlot5:
|
|
case EmulatorShortcut::LoadStateSlot6: case EmulatorShortcut::LoadStateSlot7: case EmulatorShortcut::LoadStateSlot8: case EmulatorShortcut::LoadStateSlot9: case EmulatorShortcut::LoadStateSlot10:
|
|
case EmulatorShortcut::LoadStateSlotAuto:
|
|
case EmulatorShortcut::LoadStateDialog:
|
|
case EmulatorShortcut::LoadStateFromFile:
|
|
case EmulatorShortcut::LoadState:
|
|
case EmulatorShortcut::LoadLastSession:
|
|
return isRunning && !isNetplayClient && !isMovieActive;
|
|
|
|
case EmulatorShortcut::InputBarcode:
|
|
if(isRunning && !isNetplayClient && !isMoviePlaying) {
|
|
shared_ptr<IConsole> console = _emu->GetConsole();
|
|
if(console) {
|
|
return console->GetControlManager()->GetControlDevice<IBarcodeReader>() != nullptr;
|
|
}
|
|
}
|
|
return false;
|
|
|
|
case EmulatorShortcut::RecordTape:
|
|
case EmulatorShortcut::StopRecordTape:
|
|
case EmulatorShortcut::LoadTape: {
|
|
if(isRunning && !isNetplayClient && !isMoviePlaying) {
|
|
shared_ptr<IConsole> console = _emu->GetConsole();
|
|
if(console) {
|
|
shared_ptr<ITapeRecorder> recorder = console->GetControlManager()->GetControlDevice<ITapeRecorder>();
|
|
if(recorder) {
|
|
bool recording = recorder->IsRecording();
|
|
if(recording) {
|
|
return shortcut == EmulatorShortcut::StopRecordTape;
|
|
} else {
|
|
return shortcut != EmulatorShortcut::StopRecordTape;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ShortcutState state = _emu->IsShortcutAllowed(shortcut, shortcutParam);
|
|
if(state == ShortcutState::Default) {
|
|
return shortcut < EmulatorShortcut::InputBarcode;
|
|
} else {
|
|
return state == ShortcutState::Enabled ? true : false;
|
|
}
|
|
}
|
|
|
|
void ShortcutKeyHandler::ProcessShortcutPressed(EmulatorShortcut shortcut, uint32_t shortcutParam)
|
|
{
|
|
if(!IsShortcutAllowed(shortcut, shortcutParam)) {
|
|
return;
|
|
}
|
|
|
|
EmuSettings* settings = _emu->GetSettings();
|
|
|
|
switch(shortcut) {
|
|
case EmulatorShortcut::Pause:
|
|
if(_emu->IsPaused()) {
|
|
_emu->Resume();
|
|
} else {
|
|
_emu->Pause();
|
|
}
|
|
break;
|
|
|
|
case EmulatorShortcut::ExecReset: _emu->GetSystemActionManager()->Reset(); break;
|
|
case EmulatorShortcut::ExecPowerCycle: _emu->GetSystemActionManager()->PowerCycle(); break;
|
|
case EmulatorShortcut::ExecReloadRom: _emu->ReloadRom(false); break;
|
|
case EmulatorShortcut::ExecPowerOff: _emu->Stop(true); break;
|
|
|
|
case EmulatorShortcut::FastForward: settings->SetFlag(EmulationFlags::Turbo); break;
|
|
case EmulatorShortcut::ToggleFastForward:
|
|
if(settings->CheckFlag(EmulationFlags::Turbo)) {
|
|
settings->ClearFlag(EmulationFlags::Turbo);
|
|
} else {
|
|
settings->SetFlag(EmulationFlags::Turbo);
|
|
}
|
|
break;
|
|
|
|
case EmulatorShortcut::SelectSaveSlot1: case EmulatorShortcut::SelectSaveSlot2: case EmulatorShortcut::SelectSaveSlot3: case EmulatorShortcut::SelectSaveSlot4: case EmulatorShortcut::SelectSaveSlot5:
|
|
case EmulatorShortcut::SelectSaveSlot6: case EmulatorShortcut::SelectSaveSlot7: case EmulatorShortcut::SelectSaveSlot8: case EmulatorShortcut::SelectSaveSlot9: case EmulatorShortcut::SelectSaveSlot10:
|
|
_emu->GetSaveStateManager()->SelectSaveSlot((int)shortcut - (int)EmulatorShortcut::SelectSaveSlot1 + 1);
|
|
break;
|
|
|
|
case EmulatorShortcut::SaveStateSlot1: case EmulatorShortcut::SaveStateSlot2: case EmulatorShortcut::SaveStateSlot3: case EmulatorShortcut::SaveStateSlot4: case EmulatorShortcut::SaveStateSlot5:
|
|
case EmulatorShortcut::SaveStateSlot6: case EmulatorShortcut::SaveStateSlot7: case EmulatorShortcut::SaveStateSlot8: case EmulatorShortcut::SaveStateSlot9: case EmulatorShortcut::SaveStateSlot10:
|
|
_emu->GetSaveStateManager()->SaveState((int)shortcut - (int)EmulatorShortcut::SaveStateSlot1 + 1);
|
|
break;
|
|
|
|
case EmulatorShortcut::LoadStateSlot1: case EmulatorShortcut::LoadStateSlot2: case EmulatorShortcut::LoadStateSlot3: case EmulatorShortcut::LoadStateSlot4: case EmulatorShortcut::LoadStateSlot5:
|
|
case EmulatorShortcut::LoadStateSlot6: case EmulatorShortcut::LoadStateSlot7: case EmulatorShortcut::LoadStateSlot8: case EmulatorShortcut::LoadStateSlot9: case EmulatorShortcut::LoadStateSlot10:
|
|
case EmulatorShortcut::LoadStateSlotAuto:
|
|
_emu->GetSaveStateManager()->LoadState((int)shortcut - (int)EmulatorShortcut::LoadStateSlot1 + 1);
|
|
break;
|
|
|
|
case EmulatorShortcut::MoveToNextStateSlot: _emu->GetSaveStateManager()->MoveToNextSlot(); break;
|
|
case EmulatorShortcut::MoveToPreviousStateSlot: _emu->GetSaveStateManager()->MoveToPreviousSlot(); break;
|
|
case EmulatorShortcut::SaveState: _emu->GetSaveStateManager()->SaveState(); break;
|
|
case EmulatorShortcut::LoadState: _emu->GetSaveStateManager()->LoadState(); break;
|
|
|
|
case EmulatorShortcut::RunSingleFrame: ProcessRunSingleFrame(); break;
|
|
|
|
case EmulatorShortcut::ToggleRewind:
|
|
if(_emu->GetRewindManager()->IsRewinding()) {
|
|
_emu->GetRewindManager()->StopRewinding();
|
|
} else {
|
|
_emu->GetRewindManager()->StartRewinding();
|
|
}
|
|
break;
|
|
|
|
case EmulatorShortcut::Rewind: _emu->GetRewindManager()->StartRewinding(); break;
|
|
case EmulatorShortcut::RewindTenSecs: _emu->GetRewindManager()->RewindSeconds(10); break;
|
|
case EmulatorShortcut::RewindOneMin: _emu->GetRewindManager()->RewindSeconds(60); break;
|
|
|
|
default:
|
|
//Anything else is managed by the UI
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ShortcutKeyHandler::ProcessShortcutReleased(EmulatorShortcut shortcut, uint32_t shortcutParam)
|
|
{
|
|
if(!IsShortcutAllowed(shortcut, shortcutParam)) {
|
|
return;
|
|
}
|
|
|
|
EmuSettings* settings = _emu->GetSettings();
|
|
switch(shortcut) {
|
|
case EmulatorShortcut::FastForward: settings->ClearFlag(EmulationFlags::Turbo); break;
|
|
case EmulatorShortcut::Rewind: _emu->GetRewindManager()->StopRewinding(); break;
|
|
|
|
case EmulatorShortcut::RunSingleFrame:
|
|
_repeatStarted = false;
|
|
_needRepeat = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ShortcutKeyHandler::CheckMappedKeys()
|
|
{
|
|
for(uint64_t i = 0; i < (uint64_t)EmulatorShortcut::ShortcutCount; i++) {
|
|
EmulatorShortcut shortcut = (EmulatorShortcut)i;
|
|
if(DetectKeyPress(shortcut)) {
|
|
if(!IsShortcutAllowed(shortcut, 0)) {
|
|
continue;
|
|
}
|
|
|
|
ExecuteShortcutParams params = {};
|
|
params.Shortcut = shortcut;
|
|
_emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::ExecuteShortcut, ¶ms);
|
|
} else if(DetectKeyRelease(shortcut)) {
|
|
ExecuteShortcutParams params = {};
|
|
params.Shortcut = shortcut;
|
|
_emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::ReleaseShortcut, ¶ms);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShortcutKeyHandler::ProcessKeys()
|
|
{
|
|
if(!_emu->GetSettings()->IsInputEnabled()) {
|
|
return;
|
|
}
|
|
|
|
auto lock = _lock.AcquireSafe();
|
|
KeyManager::RefreshKeyState();
|
|
|
|
_isKeyboardConnected = _emu->IsKeyboardConnected();
|
|
_isPaused = _emu->IsPaused();
|
|
|
|
_pressedKeys = KeyManager::GetPressedKeys();
|
|
_isKeyUp = _pressedKeys.size() < _lastPressedKeys.size();
|
|
|
|
bool noChange = false;
|
|
if(_pressedKeys.size() == _lastPressedKeys.size()) {
|
|
noChange = true;
|
|
for(size_t i = 0; i < _pressedKeys.size(); i++) {
|
|
if(_pressedKeys[i] != _lastPressedKeys[i]) {
|
|
noChange = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!noChange) {
|
|
//Only run this if the keys have changed
|
|
for(int i = 0; i < 2; i++) {
|
|
_keysDown[i].clear();
|
|
_keySetIndex = i;
|
|
CheckMappedKeys();
|
|
_prevKeysDown[i] = _keysDown[i];
|
|
}
|
|
|
|
_lastPressedKeys = _pressedKeys;
|
|
}
|
|
|
|
if(_needRepeat) {
|
|
double elapsedMs = _runSingleFrameRepeatTimer.GetElapsedMS();
|
|
if((_repeatStarted && elapsedMs >= 50) || (!_repeatStarted && elapsedMs >= 500)) {
|
|
//Over 500ms has elapsed since the key was first pressed, or over 50ms since repeat mode started (20fps)
|
|
//In this case, run another frame and pause again.
|
|
_repeatStarted = true;
|
|
ProcessRunSingleFrame();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShortcutKeyHandler::ProcessNotification(ConsoleNotificationType type, void* parameter)
|
|
{
|
|
switch(type) {
|
|
case ConsoleNotificationType::ExecuteShortcut: {
|
|
ExecuteShortcutParams p = *(ExecuteShortcutParams*)parameter;
|
|
ProcessShortcutPressed(p.Shortcut, p.Param);
|
|
break;
|
|
}
|
|
|
|
case ConsoleNotificationType::ReleaseShortcut: {
|
|
ExecuteShortcutParams p = *(ExecuteShortcutParams*)parameter;
|
|
ProcessShortcutReleased(p.Shortcut, p.Param);
|
|
break;
|
|
}
|
|
}
|
|
}
|