Separate frontend for mouse/gamepad, libretro support in future?

This commit is contained in:
Andrey Konoplyankin 2021-04-17 23:27:04 +03:00
parent 4bc5beb704
commit 3cc64faea5
12 changed files with 491 additions and 413 deletions

View file

@ -21,6 +21,8 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
28896568262B64CD00139ABE /* MouseFrontendSwitch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28896566262B64CD00139ABE /* MouseFrontendSwitch.cpp */; };
28896570262B6EDD00139ABE /* GamepadFrontendSwitch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2889656E262B6EDD00139ABE /* GamepadFrontendSwitch.cpp */; };
28AD4A752606120A009314C6 /* glad.c in Sources */ = {isa = PBXBuildFile; fileRef = 28AD4A722606120A009314C6 /* glad.c */; };
3602C3B7245D903000368900 /* HostButton.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3602C3B5245D903000368900 /* HostButton.cpp */; };
3602C3BA245DB3C800368900 /* AppListWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3602C3B8245DB3C800368900 /* AppListWindow.cpp */; };
@ -140,6 +142,12 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
28896563262B628700139ABE /* MouseFrontend.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MouseFrontend.hpp; sourceTree = "<group>"; };
28896566262B64CD00139ABE /* MouseFrontendSwitch.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MouseFrontendSwitch.cpp; sourceTree = "<group>"; };
28896567262B64CD00139ABE /* MouseFrontendSwitch.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MouseFrontendSwitch.hpp; sourceTree = "<group>"; };
2889656B262B6E0100139ABE /* GamepadFrontend.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = GamepadFrontend.hpp; sourceTree = "<group>"; };
2889656E262B6EDD00139ABE /* GamepadFrontendSwitch.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = GamepadFrontendSwitch.cpp; sourceTree = "<group>"; };
2889656F262B6EDD00139ABE /* GamepadFrontendSwitch.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = GamepadFrontendSwitch.hpp; sourceTree = "<group>"; };
28AD4A712606120A009314C6 /* glad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = glad.h; sourceTree = "<group>"; };
28AD4A722606120A009314C6 /* glad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = glad.c; sourceTree = "<group>"; };
28AD4A742606120A009314C6 /* khrplatform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = khrplatform.h; sourceTree = "<group>"; };
@ -446,10 +454,16 @@
362041A125D94D7700D21EE3 /* StreamControlsController.hpp */,
3620418B25D7F04400D21EE3 /* MouseController.cpp */,
3620418C25D7F04400D21EE3 /* MouseController.hpp */,
28896563262B628700139ABE /* MouseFrontend.hpp */,
28896566262B64CD00139ABE /* MouseFrontendSwitch.cpp */,
28896567262B64CD00139ABE /* MouseFrontendSwitch.hpp */,
3620419625D85B5F00D21EE3 /* KeyboardController.cpp */,
3620419725D85B5F00D21EE3 /* KeyboardController.hpp */,
362041A925D9BE4900D21EE3 /* GamepadController.cpp */,
362041AA25D9BE4900D21EE3 /* GamepadController.hpp */,
2889656B262B6E0100139ABE /* GamepadFrontend.hpp */,
2889656E262B6EDD00139ABE /* GamepadFrontendSwitch.cpp */,
2889656F262B6EDD00139ABE /* GamepadFrontendSwitch.hpp */,
3689D6DB249154F90008CB75 /* GamepadMapper.cpp */,
3689D6DC249154F90008CB75 /* GamepadMapper.hpp */,
);
@ -995,11 +1009,13 @@
3652F075245C292B001FABF3 /* VideoStream.c in Sources */,
3652EFE0245B3B00001FABF3 /* imageview.cpp in Sources */,
3652EFDB245B3B00001FABF3 /* texture.cpp in Sources */,
28896568262B64CD00139ABE /* MouseFrontendSwitch.cpp in Sources */,
36A0C03D2461F03C0083289C /* Settings.cpp in Sources */,
36BFCCF82479725900245D40 /* main.cpp in Sources */,
3652F079245C292B001FABF3 /* RtpReorderQueue.c in Sources */,
36BFCCF12479723E00245D40 /* xml.cpp in Sources */,
3652F065245C292B001FABF3 /* list.c in Sources */,
28896570262B6EDD00139ABE /* GamepadFrontendSwitch.cpp in Sources */,
3620418D25D7F04400D21EE3 /* MouseController.cpp in Sources */,
36DFE0CD2459FA3F00FC51CE /* nanogui_resources.cpp in Sources */,
36DDACF824929919001133D1 /* InputSettingsWindow.cpp in Sources */,

View file

@ -2,263 +2,78 @@
#include "Limelight.h"
#include <nanogui/nanogui.h>
#include <nanogui/opengl.h>
#include <GLFW/glfw3.h>
#include <map>
#include <vector>
static inline bool pad_is_equal(PadState& state, PadState& other) {
return padGetButtons(&state) == padGetButtons(&other) &&
padGetStickPos(&state, 0).x == padGetStickPos(&other, 0).x &&
padGetStickPos(&state, 0).y == padGetStickPos(&other, 0).y &&
padGetStickPos(&state, 1).x == padGetStickPos(&other, 1).x &&
padGetStickPos(&state, 1).y == padGetStickPos(&other, 1).y;
}
static inline int pad_button_number(HidNpadButton button) {
static std::map<HidNpadButton, int> map = {
{ HidNpadButton_A, 0 },
{ HidNpadButton_B, 1 },
{ HidNpadButton_X, 2 },
{ HidNpadButton_Y, 3 },
{ HidNpadButton_StickL, 4 },
{ HidNpadButton_StickR, 5 },
{ HidNpadButton_L, 6 },
{ HidNpadButton_R, 7 },
{ HidNpadButton_ZL, 8 },
{ HidNpadButton_ZR, 9 },
{ HidNpadButton_Plus, 10 },
{ HidNpadButton_Minus, 11 },
{ HidNpadButton_Left, 12 },
{ HidNpadButton_Up, 13 },
{ HidNpadButton_Right, 14 },
{ HidNpadButton_Down, 15 }
};
return map[button];
}
static inline int pad_button_to_glfw_button(int button) {
static std::map<int, int> map = {
{ pad_button_number(HidNpadButton_A), NANOGUI_GAMEPAD_BUTTON_A },
{ pad_button_number(HidNpadButton_B), NANOGUI_GAMEPAD_BUTTON_B },
{ pad_button_number(HidNpadButton_X), NANOGUI_GAMEPAD_BUTTON_X },
{ pad_button_number(HidNpadButton_Y), NANOGUI_GAMEPAD_BUTTON_Y },
{ pad_button_number(HidNpadButton_StickL), NANOGUI_GAMEPAD_BUTTON_LEFT_THUMB },
{ pad_button_number(HidNpadButton_StickR), NANOGUI_GAMEPAD_BUTTON_RIGHT_THUMB },
{ pad_button_number(HidNpadButton_L), NANOGUI_GAMEPAD_BUTTON_LEFT_BUMPER },
{ pad_button_number(HidNpadButton_R), NANOGUI_GAMEPAD_BUTTON_RIGHT_BUMPER },
{ pad_button_number(HidNpadButton_Plus), NANOGUI_GAMEPAD_BUTTON_START },
{ pad_button_number(HidNpadButton_Minus), NANOGUI_GAMEPAD_BUTTON_BACK },
{ pad_button_number(HidNpadButton_Up), NANOGUI_GAMEPAD_BUTTON_DPAD_UP },
{ pad_button_number(HidNpadButton_Down), NANOGUI_GAMEPAD_BUTTON_DPAD_DOWN },
{ pad_button_number(HidNpadButton_Left), NANOGUI_GAMEPAD_BUTTON_DPAD_LEFT },
{ pad_button_number(HidNpadButton_Right), NANOGUI_GAMEPAD_BUTTON_DPAD_RIGHT }
};
if (map.find(button) != map.end()) {
return map[button];
}
return -1;
}
static inline int pad_button_to_moonlight_button(int button) {
static std::map<int, int> map = {
{ pad_button_number(HidNpadButton_A), A_FLAG },
{ pad_button_number(HidNpadButton_B), B_FLAG },
{ pad_button_number(HidNpadButton_X), X_FLAG },
{ pad_button_number(HidNpadButton_Y), Y_FLAG },
{ pad_button_number(HidNpadButton_StickL), LS_CLK_FLAG },
{ pad_button_number(HidNpadButton_StickR), RS_CLK_FLAG },
{ pad_button_number(HidNpadButton_L), LB_FLAG },
{ pad_button_number(HidNpadButton_R), RB_FLAG },
{ pad_button_number(HidNpadButton_Plus), PLAY_FLAG },
{ pad_button_number(HidNpadButton_Minus), BACK_FLAG },
{ pad_button_number(HidNpadButton_Up), UP_FLAG },
{ pad_button_number(HidNpadButton_Down), DOWN_FLAG },
{ pad_button_number(HidNpadButton_Left), LEFT_FLAG },
{ pad_button_number(HidNpadButton_Right), RIGHT_FLAG }
};
return map[button];
}
static inline void padUpdateOverKeyboard(PadState* pad) {
static int glfw_keyboard_state[GLFW_KEY_LAST] = {0};
static std::map<int, HidNpadButton> keyboard_map = {
{ GLFW_KEY_UP, HidNpadButton_Up },
{ GLFW_KEY_DOWN, HidNpadButton_Down },
{ GLFW_KEY_LEFT, HidNpadButton_Left },
{ GLFW_KEY_RIGHT, HidNpadButton_Right },
{ GLFW_KEY_A, HidNpadButton_A },
{ GLFW_KEY_B, HidNpadButton_B },
{ GLFW_KEY_X, HidNpadButton_X },
{ GLFW_KEY_Y, HidNpadButton_Y },
{ GLFW_KEY_Q, HidNpadButton_L },
{ GLFW_KEY_E, HidNpadButton_R },
{ GLFW_KEY_Z, HidNpadButton_ZL },
{ GLFW_KEY_C, HidNpadButton_ZR },
{ GLFW_KEY_MINUS, HidNpadButton_Minus },
{ GLFW_KEY_EQUAL, HidNpadButton_Plus },
{ GLFW_KEY_N, HidNpadButton_StickL },
{ GLFW_KEY_M, HidNpadButton_StickR }
};
static std::map<int, std::vector<int>> axis_map = {
{ GLFW_KEY_T, { 0, 1, 1 } },
{ GLFW_KEY_G, { 0, 1, -1 } },
{ GLFW_KEY_F, { 0, 0, -1 } },
{ GLFW_KEY_H, { 0, 0, 1 } },
{ GLFW_KEY_I, { 1, 1, 1 } },
{ GLFW_KEY_K, { 1, 1, -1 } },
{ GLFW_KEY_J, { 1, 0, -1 } },
{ GLFW_KEY_L, { 1, 0, 1 } }
};
for (auto &pair: keyboard_map) {
int is_pressed = glfwGetKey(glfwGetCurrentContext(), pair.first);
if (glfw_keyboard_state[pair.first] != is_pressed) {
glfw_keyboard_state[pair.first] = is_pressed;
if (is_pressed) {
pad->buttons_cur |= pair.second;
} else {
pad->buttons_cur &= ~pair.second;
}
static inline void gamepad_callback(short& buttonFlags, int moonlight_button, int nanogui_button, int index, int* nanogui_gamepad_state) {
if (buttonFlags & moonlight_button) {
if (nanogui_gamepad_state[index] == 0) {
nanogui_gamepad_state[index] = 1;
nanogui::gamepad_button_callback_event(0, nanogui_button, 1);
}
}
for (auto &pair: axis_map) {
int is_pressed = glfwGetKey(glfwGetCurrentContext(), pair.first);
if (glfw_keyboard_state[pair.first] != is_pressed) {
glfw_keyboard_state[pair.first] = is_pressed;
if (pair.second[1] == 0) {
pad->sticks[pair.second[0]].x = is_pressed ? pair.second[2] * 0x7FFF : 0;
} else {
pad->sticks[pair.second[0]].y = is_pressed ? pair.second[2] * 0x7FFF : 0;
}
} else {
if (nanogui_gamepad_state[index] == 1) {
nanogui_gamepad_state[index] = 0;
nanogui::gamepad_button_callback_event(0, nanogui_button, 0);
}
}
}
void GamepadController::init() {
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&m_pad_state);
hidInitializeVibrationDevices(m_vibration_device_handles[0], 2, HidNpadIdType_Handheld, HidNpadStyleTag_NpadHandheld);
hidInitializeVibrationDevices(m_vibration_device_handles[1], 2, HidNpadIdType_No1, HidNpadStyleTag_NpadJoyDual);
void GamepadController::init(GamepadFrontend* frontend) {
m_frontend = frontend;
}
void GamepadController::handle_gamepad() {
PadState new_state = m_pad_state;
PadState prev_state = m_pad_state;
m_frontend->handle_gamepad();
#ifdef __SWITCH__
padUpdate(&new_state);
#else
padUpdateOverKeyboard(&new_state);
#endif
auto state = m_frontend->gamepad_state();
if (set_new_pad_state(new_state)) {
u64 prev_buttons = padGetButtons(&prev_state);
u64 buttons = padGetButtons(&m_pad_state);
for (int i = 0; i < 28; i++) {
bool is_pressed = buttons & BIT(i);
int glfw_button = pad_button_to_glfw_button(i);
if (glfw_button == -1) {
continue;
}
if ((prev_buttons & BIT(i)) != is_pressed) {
nanogui::gamepad_button_callback_event(0, glfw_button, is_pressed);
}
if (is_pressed) {
m_gamepad_state.buttonFlags |= pad_button_to_moonlight_button(i);
} else {
m_gamepad_state.buttonFlags &= ~pad_button_to_moonlight_button(i);
}
}
m_gamepad_state.leftTrigger = (buttons & HidNpadButton_ZL) ? 0xFF : 0;
m_gamepad_state.rightTrigger = (buttons & HidNpadButton_ZR) ? 0xFF : 0;
if ((prev_buttons & HidNpadButton_ZL) != (buttons & HidNpadButton_ZL)) {
nanogui::gamepad_analog_callback_event(0, NANOGUI_GAMEPAD_AXIS_LEFT_TRIGGER, (buttons & HidNpadButton_ZL) ? 1 : 0);
}
if ((prev_buttons & HidNpadButton_ZR) != (buttons & HidNpadButton_ZR)) {
nanogui::gamepad_analog_callback_event(0, NANOGUI_GAMEPAD_AXIS_RIGHT_TRIGGER, (buttons & HidNpadButton_ZR) ? 1 : 0);
}
m_gamepad_state.leftStickX = m_pad_state.sticks[0].x;
m_gamepad_state.leftStickY = m_pad_state.sticks[0].y;
m_gamepad_state.rightStickX = m_pad_state.sticks[1].x;
m_gamepad_state.rightStickY = m_pad_state.sticks[1].y;
if (prev_state.sticks[0].x != m_pad_state.sticks[0].x) {
nanogui::gamepad_analog_callback_event(0, NANOGUI_GAMEPAD_AXIS_LEFT_X, (float)m_pad_state.sticks[0].x / 0x7FFF);
}
if (prev_state.sticks[0].y != m_pad_state.sticks[0].y) {
nanogui::gamepad_analog_callback_event(0, NANOGUI_GAMEPAD_AXIS_LEFT_Y, (float)m_pad_state.sticks[0].y / 0x7FFF);
}
if (prev_state.sticks[1].x != m_pad_state.sticks[1].x) {
nanogui::gamepad_analog_callback_event(0, NANOGUI_GAMEPAD_AXIS_RIGHT_X, (float)m_pad_state.sticks[1].x / 0x7FFF);
}
if (prev_state.sticks[1].y != m_pad_state.sticks[1].y) {
nanogui::gamepad_analog_callback_event(0, NANOGUI_GAMEPAD_AXIS_RIGHT_Y, (float)m_pad_state.sticks[1].y / 0x7FFF);
}
if (state.buttonFlags != m_gamepad_state.buttonFlags) {
gamepad_callback(state.buttonFlags, A_FLAG, NANOGUI_GAMEPAD_BUTTON_A, 0, m_nanogui_gamepad_state);
gamepad_callback(state.buttonFlags, B_FLAG, NANOGUI_GAMEPAD_BUTTON_B, 1, m_nanogui_gamepad_state);
gamepad_callback(state.buttonFlags, X_FLAG, NANOGUI_GAMEPAD_BUTTON_X, 2, m_nanogui_gamepad_state);
gamepad_callback(state.buttonFlags, Y_FLAG, NANOGUI_GAMEPAD_BUTTON_Y, 3, m_nanogui_gamepad_state);
gamepad_callback(state.buttonFlags, LEFT_FLAG, NANOGUI_GAMEPAD_BUTTON_DPAD_LEFT, 4, m_nanogui_gamepad_state);
gamepad_callback(state.buttonFlags, RIGHT_FLAG, NANOGUI_GAMEPAD_BUTTON_DPAD_RIGHT, 5, m_nanogui_gamepad_state);
gamepad_callback(state.buttonFlags, UP_FLAG, NANOGUI_GAMEPAD_BUTTON_DPAD_UP, 6, m_nanogui_gamepad_state);
gamepad_callback(state.buttonFlags, DOWN_FLAG, NANOGUI_GAMEPAD_BUTTON_DPAD_DOWN, 7, m_nanogui_gamepad_state);
gamepad_callback(state.buttonFlags, BACK_FLAG, NANOGUI_GAMEPAD_BUTTON_BACK, 8, m_nanogui_gamepad_state);
gamepad_callback(state.buttonFlags, PLAY_FLAG, NANOGUI_GAMEPAD_BUTTON_START, 9, m_nanogui_gamepad_state);
gamepad_callback(state.buttonFlags, LB_FLAG, NANOGUI_GAMEPAD_BUTTON_LEFT_BUMPER, 10, m_nanogui_gamepad_state);
gamepad_callback(state.buttonFlags, RB_FLAG, NANOGUI_GAMEPAD_BUTTON_RIGHT_BUMPER, 11, m_nanogui_gamepad_state);
gamepad_callback(state.buttonFlags, LS_CLK_FLAG, NANOGUI_GAMEPAD_BUTTON_LEFT_THUMB, 12, m_nanogui_gamepad_state);
gamepad_callback(state.buttonFlags, RS_CLK_FLAG, NANOGUI_GAMEPAD_BUTTON_RIGHT_THUMB, 13, m_nanogui_gamepad_state);
}
}
bool GamepadController::set_new_pad_state(PadState& state) {
if (!pad_is_equal(m_pad_state, state)) {
m_pad_state = state;
return true;
if (state.leftStickX != m_gamepad_state.leftStickX) {
nanogui::gamepad_analog_callback_event(0, NANOGUI_GAMEPAD_AXIS_LEFT_X, (float)state.leftStickX / 0x7FFF);
}
return false;
if (state.leftStickY != m_gamepad_state.leftStickY) {
nanogui::gamepad_analog_callback_event(0, NANOGUI_GAMEPAD_AXIS_LEFT_Y, (float)state.leftStickY / 0x7FFF);
}
if (state.rightStickX != m_gamepad_state.rightStickX) {
nanogui::gamepad_analog_callback_event(0, NANOGUI_GAMEPAD_AXIS_RIGHT_X, (float)state.rightStickX / 0x7FFF);
}
if (state.rightStickY != m_gamepad_state.rightStickY) {
nanogui::gamepad_analog_callback_event(0, NANOGUI_GAMEPAD_AXIS_RIGHT_Y, (float)state.rightStickY / 0x7FFF);
}
if (state.leftTrigger != m_gamepad_state.leftTrigger) {
nanogui::gamepad_analog_callback_event(0, NANOGUI_GAMEPAD_AXIS_LEFT_TRIGGER, state.leftTrigger ? 1 : 0);
}
if (state.rightTrigger != m_gamepad_state.rightTrigger) {
nanogui::gamepad_analog_callback_event(0, NANOGUI_GAMEPAD_AXIS_RIGHT_TRIGGER, state.rightTrigger ? 1 : 0);
}
m_gamepad_state = state;
}
void GamepadController::handle_rumple(unsigned short controller, unsigned short low_freq_motor, unsigned short high_freq_motor) {
if (controller == 0) {
float low = (float)low_freq_motor / 0xFFFF;
float high = (float)high_freq_motor / 0xFFFF;
memset(m_vibration_values, 0, sizeof(m_vibration_values));
m_vibration_values[0].amp_low = low;
m_vibration_values[0].freq_low = low * 50;
m_vibration_values[0].amp_high = high;
m_vibration_values[0].freq_high = high * 100;
m_vibration_values[1].amp_low = low;
m_vibration_values[1].freq_low = low * 50;
m_vibration_values[1].amp_high = high;
m_vibration_values[1].freq_high = high * 100;
int target_device = padIsHandheld(&m_pad_state) ? 0 : 1;
hidSendVibrationValues(m_vibration_device_handles[target_device], m_vibration_values, 2);
}
m_frontend->handle_rumple(controller, low_freq_motor, high_freq_motor);
}
void GamepadController::stop_rumple() {
HidVibrationValue stop;
memset(&stop, 0, sizeof(HidVibrationValue));
stop.freq_low = 160.0f;
stop.freq_high = 320.0f;
memcpy(&m_vibration_values[0], &stop, sizeof(HidVibrationValue));
memcpy(&m_vibration_values[1], &stop, sizeof(HidVibrationValue));
int target_device = padIsHandheld(&m_pad_state) ? 0 : 1;
hidSendVibrationValues(m_vibration_device_handles[target_device], m_vibration_values, 2);
hidSendVibrationValues(m_vibration_device_handles[1 - target_device], m_vibration_values, 2);
m_frontend->stop_rumple();
}

View file

@ -1,31 +1,10 @@
#include "Singleton.hpp"
#include <switch.h>
#include "GamepadFrontend.hpp"
#pragma once
// Moonlight ready gamepad
struct GamepadState {
short buttonFlags;
unsigned char leftTrigger;
unsigned char rightTrigger;
short leftStickX;
short leftStickY;
short rightStickX;
short rightStickY;
bool is_equal(GamepadState other) {
return buttonFlags == other.buttonFlags &&
leftTrigger == other.leftTrigger &&
rightTrigger == other.rightTrigger &&
leftStickX == other.leftStickX &&
leftStickY == other.leftStickY &&
rightStickX == other.rightStickX &&
rightStickY == other.rightStickY;
}
};
class GamepadController: public Singleton<GamepadController> {
public:
void init();
void init(GamepadFrontend* frontend);
void handle_gamepad();
@ -37,11 +16,7 @@ public:
}
private:
PadState m_pad_state;
GamepadState m_gamepad_state = {0};
bool set_new_pad_state(PadState& state);
HidVibrationDeviceHandle m_vibration_device_handles[2][2];
HidVibrationValue m_vibration_values[2];
int m_nanogui_gamepad_state[14] = {0};
GamepadFrontend* m_frontend;
};

View file

@ -0,0 +1,30 @@
#pragma once
// Moonlight ready gamepad
struct GamepadState {
short buttonFlags;
unsigned char leftTrigger;
unsigned char rightTrigger;
short leftStickX;
short leftStickY;
short rightStickX;
short rightStickY;
bool is_equal(GamepadState other) {
return buttonFlags == other.buttonFlags &&
leftTrigger == other.leftTrigger &&
rightTrigger == other.rightTrigger &&
leftStickX == other.leftStickX &&
leftStickY == other.leftStickY &&
rightStickX == other.rightStickX &&
rightStickY == other.rightStickY;
}
};
class GamepadFrontend {
public:
virtual void handle_gamepad() = 0;
virtual void handle_rumple(unsigned short controller, unsigned short low_freq_motor, unsigned short high_freq_motor) = 0;
virtual void stop_rumple() = 0;
virtual GamepadState gamepad_state() const = 0;
};

View file

@ -0,0 +1,202 @@
#include "GamepadFrontendSwitch.hpp"
#include "Limelight.h"
#include <nanogui/opengl.h>
#include <GLFW/glfw3.h>
#include <map>
#include <vector>
static inline bool pad_is_equal(PadState& state, PadState& other) {
return padGetButtons(&state) == padGetButtons(&other) &&
padGetStickPos(&state, 0).x == padGetStickPos(&other, 0).x &&
padGetStickPos(&state, 0).y == padGetStickPos(&other, 0).y &&
padGetStickPos(&state, 1).x == padGetStickPos(&other, 1).x &&
padGetStickPos(&state, 1).y == padGetStickPos(&other, 1).y;
}
static inline int pad_button_number(HidNpadButton button) {
static std::map<HidNpadButton, int> map = {
{ HidNpadButton_A, 0 },
{ HidNpadButton_B, 1 },
{ HidNpadButton_X, 2 },
{ HidNpadButton_Y, 3 },
{ HidNpadButton_StickL, 4 },
{ HidNpadButton_StickR, 5 },
{ HidNpadButton_L, 6 },
{ HidNpadButton_R, 7 },
{ HidNpadButton_ZL, 8 },
{ HidNpadButton_ZR, 9 },
{ HidNpadButton_Plus, 10 },
{ HidNpadButton_Minus, 11 },
{ HidNpadButton_Left, 12 },
{ HidNpadButton_Up, 13 },
{ HidNpadButton_Right, 14 },
{ HidNpadButton_Down, 15 }
};
return map[button];
}
static inline int pad_button_to_moonlight_button(int button) {
static std::map<int, int> map = {
{ pad_button_number(HidNpadButton_A), A_FLAG },
{ pad_button_number(HidNpadButton_B), B_FLAG },
{ pad_button_number(HidNpadButton_X), X_FLAG },
{ pad_button_number(HidNpadButton_Y), Y_FLAG },
{ pad_button_number(HidNpadButton_StickL), LS_CLK_FLAG },
{ pad_button_number(HidNpadButton_StickR), RS_CLK_FLAG },
{ pad_button_number(HidNpadButton_L), LB_FLAG },
{ pad_button_number(HidNpadButton_R), RB_FLAG },
{ pad_button_number(HidNpadButton_Plus), PLAY_FLAG },
{ pad_button_number(HidNpadButton_Minus), BACK_FLAG },
{ pad_button_number(HidNpadButton_Up), UP_FLAG },
{ pad_button_number(HidNpadButton_Down), DOWN_FLAG },
{ pad_button_number(HidNpadButton_Left), LEFT_FLAG },
{ pad_button_number(HidNpadButton_Right), RIGHT_FLAG }
};
return map[button];
}
static inline void padUpdateOverKeyboard(PadState* pad) {
static int glfw_keyboard_state[GLFW_KEY_LAST] = {0};
static std::map<int, HidNpadButton> keyboard_map = {
{ GLFW_KEY_UP, HidNpadButton_Up },
{ GLFW_KEY_DOWN, HidNpadButton_Down },
{ GLFW_KEY_LEFT, HidNpadButton_Left },
{ GLFW_KEY_RIGHT, HidNpadButton_Right },
{ GLFW_KEY_A, HidNpadButton_A },
{ GLFW_KEY_B, HidNpadButton_B },
{ GLFW_KEY_X, HidNpadButton_X },
{ GLFW_KEY_Y, HidNpadButton_Y },
{ GLFW_KEY_Q, HidNpadButton_L },
{ GLFW_KEY_E, HidNpadButton_R },
{ GLFW_KEY_Z, HidNpadButton_ZL },
{ GLFW_KEY_C, HidNpadButton_ZR },
{ GLFW_KEY_MINUS, HidNpadButton_Minus },
{ GLFW_KEY_EQUAL, HidNpadButton_Plus },
{ GLFW_KEY_N, HidNpadButton_StickL },
{ GLFW_KEY_M, HidNpadButton_StickR }
};
static std::map<int, std::vector<int>> axis_map = {
{ GLFW_KEY_T, { 0, 1, 1 } },
{ GLFW_KEY_G, { 0, 1, -1 } },
{ GLFW_KEY_F, { 0, 0, -1 } },
{ GLFW_KEY_H, { 0, 0, 1 } },
{ GLFW_KEY_I, { 1, 1, 1 } },
{ GLFW_KEY_K, { 1, 1, -1 } },
{ GLFW_KEY_J, { 1, 0, -1 } },
{ GLFW_KEY_L, { 1, 0, 1 } }
};
for (auto &pair: keyboard_map) {
int is_pressed = glfwGetKey(glfwGetCurrentContext(), pair.first);
if (glfw_keyboard_state[pair.first] != is_pressed) {
glfw_keyboard_state[pair.first] = is_pressed;
if (is_pressed) {
pad->buttons_cur |= pair.second;
} else {
pad->buttons_cur &= ~pair.second;
}
}
}
for (auto &pair: axis_map) {
int is_pressed = glfwGetKey(glfwGetCurrentContext(), pair.first);
if (glfw_keyboard_state[pair.first] != is_pressed) {
glfw_keyboard_state[pair.first] = is_pressed;
if (pair.second[1] == 0) {
pad->sticks[pair.second[0]].x = is_pressed ? pair.second[2] * 0x7FFF : 0;
} else {
pad->sticks[pair.second[0]].y = is_pressed ? pair.second[2] * 0x7FFF : 0;
}
}
}
}
GamepadFrontendSwitch::GamepadFrontendSwitch() {
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&m_pad_state);
hidInitializeVibrationDevices(m_vibration_device_handles[0], 2, HidNpadIdType_Handheld, HidNpadStyleTag_NpadHandheld);
hidInitializeVibrationDevices(m_vibration_device_handles[1], 2, HidNpadIdType_No1, HidNpadStyleTag_NpadJoyDual);
}
void GamepadFrontendSwitch::handle_gamepad() {
PadState state = m_pad_state;
#ifdef __SWITCH__
padUpdate(&state);
#else
padUpdateOverKeyboard(&state);
#endif
if (set_new_pad_state(state)) {
u64 buttons = padGetButtons(&m_pad_state);
for (int i = 0; i < 28; i++) {
bool is_pressed = buttons & BIT(i);
if (is_pressed) {
m_gamepad_state.buttonFlags |= pad_button_to_moonlight_button(i);
} else {
m_gamepad_state.buttonFlags &= ~pad_button_to_moonlight_button(i);
}
}
m_gamepad_state.leftTrigger = (buttons & HidNpadButton_ZL) ? 0xFF : 0;
m_gamepad_state.rightTrigger = (buttons & HidNpadButton_ZR) ? 0xFF : 0;
m_gamepad_state.leftStickX = m_pad_state.sticks[0].x;
m_gamepad_state.leftStickY = m_pad_state.sticks[0].y;
m_gamepad_state.rightStickX = m_pad_state.sticks[1].x;
m_gamepad_state.rightStickY = m_pad_state.sticks[1].y;
}
}
void GamepadFrontendSwitch::handle_rumple(unsigned short controller, unsigned short low_freq_motor, unsigned short high_freq_motor) {
if (controller == 0) {
float low = (float)low_freq_motor / 0xFFFF;
float high = (float)high_freq_motor / 0xFFFF;
memset(m_vibration_values, 0, sizeof(m_vibration_values));
m_vibration_values[0].amp_low = low;
m_vibration_values[0].freq_low = low * 50;
m_vibration_values[0].amp_high = high;
m_vibration_values[0].freq_high = high * 100;
m_vibration_values[1].amp_low = low;
m_vibration_values[1].freq_low = low * 50;
m_vibration_values[1].amp_high = high;
m_vibration_values[1].freq_high = high * 100;
int target_device = padIsHandheld(&m_pad_state) ? 0 : 1;
hidSendVibrationValues(m_vibration_device_handles[target_device], m_vibration_values, 2);
}
}
void GamepadFrontendSwitch::stop_rumple() {
HidVibrationValue stop;
memset(&stop, 0, sizeof(HidVibrationValue));
stop.freq_low = 160.0f;
stop.freq_high = 320.0f;
memcpy(&m_vibration_values[0], &stop, sizeof(HidVibrationValue));
memcpy(&m_vibration_values[1], &stop, sizeof(HidVibrationValue));
int target_device = padIsHandheld(&m_pad_state) ? 0 : 1;
hidSendVibrationValues(m_vibration_device_handles[target_device], m_vibration_values, 2);
hidSendVibrationValues(m_vibration_device_handles[1 - target_device], m_vibration_values, 2);
}
bool GamepadFrontendSwitch::set_new_pad_state(PadState& state) {
if (!pad_is_equal(m_pad_state, state)) {
m_pad_state = state;
return true;
}
return false;
}

View file

@ -0,0 +1,26 @@
#include "GamepadFrontend.hpp"
#include <switch.h>
#pragma once
class GamepadFrontendSwitch: public GamepadFrontend {
public:
GamepadFrontendSwitch();
void handle_gamepad();
void handle_rumple(unsigned short controller, unsigned short low_freq_motor, unsigned short high_freq_motor);
void stop_rumple();
GamepadState gamepad_state() const {
return m_gamepad_state;
}
private:
PadState m_pad_state = {0};
GamepadState m_gamepad_state = {0};
HidVibrationDeviceHandle m_vibration_device_handles[2][2];
HidVibrationValue m_vibration_values[2];
bool set_new_pad_state(PadState& state);
};

View file

@ -3,84 +3,36 @@
#include <nanogui/opengl.h>
#include <GLFW/glfw3.h>
void MouseController::init(GLFWwindow* window) {
hidInitializeMouse();
hidInitializeTouchScreen();
glfwSetCursorPosCallback(window, [](auto _, double x, double y) {
MouseController::instance().handle_mouse_move(x, y);
});
glfwSetMouseButtonCallback(window, [](auto _, int button, int action, int modifiers) {
MouseController::instance().handle_mouse_buttons(button, action, modifiers);
});
glfwSetScrollCallback(window, [](auto _, double x, double y) {
MouseController::instance().handle_mouse_scroll(x, y);
});
void MouseController::init(MouseFrontend* frontend) {
m_frontend = frontend;
}
void MouseController::handle_mouse() {
HidMouseState hid_mouse_state;
m_frontend->handle_mouse();
if (hidGetMouseStates(&hid_mouse_state, 1) && (hid_mouse_state.attributes & HidMouseAttribute_IsConnected)) {
if (m_hid_mouse_state.x != hid_mouse_state.x || m_hid_mouse_state.y != hid_mouse_state.y || m_hid_mouse_state.buttons != hid_mouse_state.buttons || m_hid_mouse_state.wheel_delta_x != hid_mouse_state.wheel_delta_x) {
m_hid_mouse_is_used = true;
m_hid_mouse_state = hid_mouse_state;
MouseState state;
state.x = hid_mouse_state.x;
state.y = hid_mouse_state.y;
state.l_pressed = hid_mouse_state.buttons & HidMouseButton_Left;
state.m_pressed = hid_mouse_state.buttons & HidMouseButton_Middle;
state.r_pressed = hid_mouse_state.buttons & HidMouseButton_Right;
state.scroll_y = (double)hid_mouse_state.wheel_delta_x / 100; // Why wheel_delta_x?
if (m_mouse_state.x != state.x || m_mouse_state.y != state.y) {
nanogui::cursor_pos_callback_event((double)state.x, (double)state.y);
}
if (m_mouse_state.l_pressed != state.l_pressed) {
nanogui::mouse_button_callback_event(NANOGUI_MOUSE_BUTTON_1, state.l_pressed ? NANOGUI_PRESS : NANOGUI_RELEASE, 0);
}
if (m_mouse_state.r_pressed != state.r_pressed) {
nanogui::mouse_button_callback_event(NANOGUI_MOUSE_BUTTON_2, state.r_pressed ? NANOGUI_PRESS : NANOGUI_RELEASE, 0);
}
if (m_mouse_state.scroll_y != state.scroll_y) {
nanogui::scroll_callback_event(0, state.scroll_y);
}
set_new_mouse_state(state);
}
} else {
m_hid_mouse_is_used = false;
auto state = m_frontend->mouse_state();
if (m_mouse_state.x != state.x || m_mouse_state.y != state.y) {
nanogui::cursor_pos_callback_event((double)state.x, (double)state.y);
}
// Simulate mouse wheel with two finger movements...
HidTouchScreenState hid_touch_screen_state;
if (hidGetTouchScreenStates(&hid_touch_screen_state, 1) && hid_touch_screen_state.count == 2) {
double y = (hid_touch_screen_state.touches[0].y + hid_touch_screen_state.touches[1].y) / 2;
if (m_last_touch_y != 0 && m_last_touch_y != y) {
auto state = m_mouse_state;
state.scroll_y = (m_last_touch_y - y) / 10;
if (set_new_mouse_state(state)) {
// Invert scroll_y for make scroll like on an iOS/Android :C
nanogui::scroll_callback_event(0, -state.scroll_y);
}
}
m_last_touch_y = y;
} else {
m_last_touch_y = 0;
if (m_mouse_state.l_pressed != state.l_pressed) {
nanogui::mouse_button_callback_event(NANOGUI_MOUSE_BUTTON_1, state.l_pressed ? NANOGUI_PRESS : NANOGUI_RELEASE, 0);
}
if (m_mouse_state.r_pressed != state.r_pressed) {
nanogui::mouse_button_callback_event(NANOGUI_MOUSE_BUTTON_2, state.r_pressed ? NANOGUI_PRESS : NANOGUI_RELEASE, 0);
}
if (m_mouse_state.scroll_y != state.scroll_y) {
nanogui::scroll_callback_event(0, state.scroll_y);
}
m_mouse_state = state;
}
void MouseController::draw_cursor(Application *app) {
if (m_hid_mouse_is_used && m_draw_cursor_for_hid_mouse) {
if (m_frontend->hid_mouse_is_used() && m_draw_cursor_for_hid_mouse) {
auto ctx = app->nvg_context();
nvgSave(ctx);
nvgReset(ctx);
@ -96,50 +48,3 @@ void MouseController::draw_cursor(Application *app) {
app->nvg_flush();
}
}
void MouseController::handle_mouse_move(double x, double y) {
m_hid_mouse_is_used = false;
auto state = m_mouse_state;
state.x = (int)x;
state.y = (int)y;
if (set_new_mouse_state(state)) {
nanogui::cursor_pos_callback_event(x, y);
}
}
void MouseController::handle_mouse_buttons(int button, int action, int modifiers) {
m_hid_mouse_is_used = false;
auto state = m_mouse_state;
if (button == NANOGUI_MOUSE_BUTTON_1) {
state.l_pressed = action == NANOGUI_PRESS;
} else if (button == NANOGUI_MOUSE_BUTTON_2) {
state.r_pressed = action == NANOGUI_PRESS;
}
if (set_new_mouse_state(state)) {
nanogui::mouse_button_callback_event(button, action, modifiers);
}
}
void MouseController::handle_mouse_scroll(int x, int y) {
m_hid_mouse_is_used = false;
auto state = m_mouse_state;
state.scroll_y = y;
if (set_new_mouse_state(state)) {
nanogui::scroll_callback_event(x, y);
}
}
bool MouseController::set_new_mouse_state(MouseState& state) {
if (!m_mouse_state.is_equal(state)) {
m_mouse_state = state;
return true;
}
return false;
}

View file

@ -1,28 +1,12 @@
#include "Singleton.hpp"
#include <switch.h>
#include "MouseFrontend.hpp"
#pragma once
class Application;
struct GLFWwindow;
struct MouseState {
int x;
int y;
double scroll_y;
bool l_pressed;
bool m_pressed;
bool r_pressed;
bool is_equal(MouseState& other) {
return x == other.x && y == other.y && scroll_y == other.scroll_y &&
l_pressed == other.l_pressed && m_pressed == other.m_pressed &&
r_pressed == other.r_pressed;
}
};
class MouseController: public Singleton<MouseController> {
public:
void init(GLFWwindow* window);
void init(MouseFrontend* frontend);
void handle_mouse();
void draw_cursor(Application* app);
@ -31,7 +15,7 @@ public:
}
bool hid_mouse_is_used() const {
return m_hid_mouse_is_used;
return m_frontend->hid_mouse_is_used();
}
void set_draw_cursor_for_hid_mouse(bool draw_cursor_for_hid_mouse) {
@ -40,13 +24,7 @@ public:
private:
MouseState m_mouse_state = {0};
HidMouseState m_hid_mouse_state = {0};
bool m_hid_mouse_is_used = false;
bool m_draw_cursor_for_hid_mouse = true;
double m_last_touch_y = 0;
MouseFrontend* m_frontend;
void handle_mouse_move(double x, double y);
void handle_mouse_buttons(int button, int action, int modifiers);
void handle_mouse_scroll(int x, int y);
bool set_new_mouse_state(MouseState& state);
bool m_draw_cursor_for_hid_mouse = true;
};

View file

@ -0,0 +1,17 @@
#pragma once
struct MouseState {
int x;
int y;
double scroll_y;
bool l_pressed;
bool m_pressed;
bool r_pressed;
};
class MouseFrontend {
public:
virtual void handle_mouse() = 0;
virtual MouseState mouse_state() const = 0;
virtual bool hid_mouse_is_used() const = 0;
};

View file

@ -0,0 +1,81 @@
#include "MouseFrontendSwitch.hpp"
#include <nanogui/opengl.h>
#include <GLFW/glfw3.h>
static MouseFrontendSwitch* m_frontend = NULL;
MouseFrontendSwitch::MouseFrontendSwitch(GLFWwindow* window) {
m_frontend = this;
hidInitializeMouse();
hidInitializeTouchScreen();
glfwSetCursorPosCallback(window, [](auto _, double x, double y) {
m_frontend->handle_mouse_move(x, y);
});
glfwSetMouseButtonCallback(window, [](auto _, int button, int action, int modifiers) {
m_frontend->handle_mouse_buttons(button, action, modifiers);
});
glfwSetScrollCallback(window, [](auto _, double x, double y) {
m_frontend->handle_mouse_scroll(x, y);
});
}
void MouseFrontendSwitch::handle_mouse() {
HidMouseState hid_mouse_state;
if (hidGetMouseStates(&hid_mouse_state, 1) && (hid_mouse_state.attributes & HidMouseAttribute_IsConnected)) {
if (m_hid_mouse_state.x != hid_mouse_state.x || m_hid_mouse_state.y != hid_mouse_state.y || m_hid_mouse_state.buttons != hid_mouse_state.buttons || m_hid_mouse_state.wheel_delta_x != hid_mouse_state.wheel_delta_x) {
m_hid_mouse_is_used = true;
m_hid_mouse_state = hid_mouse_state;
m_mouse_state.x = hid_mouse_state.x;
m_mouse_state.y = hid_mouse_state.y;
m_mouse_state.l_pressed = hid_mouse_state.buttons & HidMouseButton_Left;
m_mouse_state.m_pressed = hid_mouse_state.buttons & HidMouseButton_Middle;
m_mouse_state.r_pressed = hid_mouse_state.buttons & HidMouseButton_Right;
m_mouse_state.scroll_y = (double)hid_mouse_state.wheel_delta_x / 100; // Why wheel_delta_x?
}
} else {
m_hid_mouse_is_used = false;
}
// Simulate mouse wheel with two finger movements...
HidTouchScreenState hid_touch_screen_state;
if (hidGetTouchScreenStates(&hid_touch_screen_state, 1) && hid_touch_screen_state.count == 2) {
double y = (hid_touch_screen_state.touches[0].y + hid_touch_screen_state.touches[1].y) / 2;
if (m_last_touch_y != 0 && m_last_touch_y != y) {
m_mouse_state.scroll_y = (m_last_touch_y - y) / 10;
}
m_last_touch_y = y;
} else {
m_last_touch_y = 0;
}
}
void MouseFrontendSwitch::handle_mouse_move(double x, double y) {
m_hid_mouse_is_used = false;
m_mouse_state.x = (int)x;
m_mouse_state.y = (int)y;
}
void MouseFrontendSwitch::handle_mouse_buttons(int button, int action, int modifiers) {
m_hid_mouse_is_used = false;
if (button == NANOGUI_MOUSE_BUTTON_1) {
m_mouse_state.l_pressed = action == NANOGUI_PRESS;
} else if (button == NANOGUI_MOUSE_BUTTON_2) {
m_mouse_state.r_pressed = action == NANOGUI_PRESS;
}
}
void MouseFrontendSwitch::handle_mouse_scroll(int x, int y) {
m_hid_mouse_is_used = false;
m_mouse_state.scroll_y = y;
}

View file

@ -0,0 +1,31 @@
#include "MouseFrontend.hpp"
#include <switch.h>
#pragma once
struct GLFWwindow;
class MouseFrontendSwitch: public MouseFrontend {
public:
MouseFrontendSwitch(GLFWwindow* window);
void handle_mouse();
MouseState mouse_state() const {
return m_mouse_state;
}
bool hid_mouse_is_used() const {
return m_hid_mouse_is_used;
}
private:
MouseState m_mouse_state = {0};
HidMouseState m_hid_mouse_state = {0};
bool m_hid_mouse_is_used = false;
bool m_draw_cursor_for_hid_mouse = true;
double m_last_touch_y = 0;
void handle_mouse_move(double x, double y);
void handle_mouse_buttons(int button, int action, int modifiers);
void handle_mouse_scroll(int x, int y);
};

View file

@ -6,8 +6,10 @@
#include "GameStreamClient.hpp"
#include "Logger.hpp"
#include "MouseController.hpp"
#include "MouseFrontendSwitch.hpp"
#include "KeyboardController.hpp"
#include "GamepadController.hpp"
#include "GamepadFrontendSwitch.hpp"
#include <glad/glad.h>
#include <switch.h>
#include <GLFW/glfw3.h>
@ -63,9 +65,9 @@ int main(int argc, const char * argv[]) {
GameStreamClient::instance().start();
MouseController::instance().init(window);
MouseController::instance().init(new MouseFrontendSwitch(window));
KeyboardController::instance().init(window);
GamepadController::instance().init();
GamepadController::instance().init(new GamepadFrontendSwitch());
nanogui::init();
nanogui::ref<Application> app = new Application(Size(m_width, m_height), Size(m_fb_width, m_fb_height));