mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-03-27 05:37:16 +00:00
Map session to gamepads
This commit is contained in:
parent
e0721aa104
commit
92f51622cc
@ -6,12 +6,68 @@ extern "C" {
|
||||
#include <moonlight-common-c/src/Input.h>
|
||||
}
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include "main.h"
|
||||
#include "config.h"
|
||||
#include "input.h"
|
||||
#include "utility.h"
|
||||
#include "platform/common.h"
|
||||
#include "thread_pool.h"
|
||||
|
||||
namespace input {
|
||||
|
||||
constexpr auto MAX_GAMEPADS = std::min((std::size_t)platf::MAX_GAMEPADS, sizeof(std::int16_t)*8);
|
||||
enum class button_state_e {
|
||||
NONE,
|
||||
DOWN,
|
||||
UP
|
||||
};
|
||||
|
||||
template<std::size_t N>
|
||||
int alloc_id(std::bitset<N> &gamepad_mask) {
|
||||
for(int x = 0; x < gamepad_mask.size(); ++x) {
|
||||
if(!gamepad_mask[x]) {
|
||||
gamepad_mask[x] = true;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
void free_id(std::bitset<N> &gamepad_mask, int id) {
|
||||
gamepad_mask[id] = false;
|
||||
}
|
||||
|
||||
struct gamepad_t {
|
||||
gamepad_t() : gamepad_state {}, back_timeout_id {}, id { -1 }, back_button_state { button_state_e::NONE } {}
|
||||
platf::gamepad_state_t gamepad_state;
|
||||
|
||||
util::ThreadPool::task_id_t back_timeout_id;
|
||||
|
||||
int id;
|
||||
|
||||
// When emulating the HOME button, we may need to artificially release the back button.
|
||||
// Afterwards, the gamepad state on sunshine won't match the state on Moonlight.
|
||||
// To prevent Sunshine from sending erronious input data to the active application,
|
||||
// Sunshine forces the button to be in a specific state until the gamepad state matches that of
|
||||
// Moonlight once more.
|
||||
button_state_e back_button_state;
|
||||
};
|
||||
|
||||
struct input_t {
|
||||
input_t() : active_gamepad_state {}, gamepads (MAX_GAMEPADS) { }
|
||||
|
||||
std::uint16_t active_gamepad_state;
|
||||
std::vector<gamepad_t> gamepads;
|
||||
};
|
||||
|
||||
static std::unordered_map<short, bool> key_press {};
|
||||
static std::array<std::uint8_t, 5> mouse_press {};
|
||||
|
||||
static platf::input_t platf_input;
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
void print(PNV_MOUSE_MOVE_PACKET packet) {
|
||||
@ -102,18 +158,18 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MOUSE_BUTTON_PACKET packet
|
||||
display_cursor = true;
|
||||
|
||||
auto button = util::endian::big(packet->button);
|
||||
if(button > 0 && button < input->mouse_press.size()) {
|
||||
input->mouse_press[button] = packet->action != BUTTON_RELEASED;
|
||||
if(button > 0 && button < mouse_press.size()) {
|
||||
mouse_press[button] = packet->action != BUTTON_RELEASED;
|
||||
}
|
||||
|
||||
platf::button_mouse(input->input, button, packet->action == BUTTON_RELEASED);
|
||||
platf::button_mouse(platf_input, button, packet->action == BUTTON_RELEASED);
|
||||
}
|
||||
|
||||
void passthrough(std::shared_ptr<input_t> &input, PNV_KEYBOARD_PACKET packet) {
|
||||
auto constexpr BUTTON_RELEASED = 0x04;
|
||||
|
||||
input->key_press[packet->keyCode] = packet->keyAction != BUTTON_RELEASED;
|
||||
platf::keyboard(input->input, packet->keyCode & 0x00FF, packet->keyAction == BUTTON_RELEASED);
|
||||
key_press[packet->keyCode] = packet->keyAction != BUTTON_RELEASED;
|
||||
platf::keyboard(platf_input, packet->keyCode & 0x00FF, packet->keyAction == BUTTON_RELEASED);
|
||||
}
|
||||
|
||||
void passthrough(platf::input_t &input, PNV_SCROLL_PACKET packet) {
|
||||
@ -122,22 +178,58 @@ void passthrough(platf::input_t &input, PNV_SCROLL_PACKET packet) {
|
||||
platf::scroll(input, util::endian::big(packet->scrollAmt1));
|
||||
}
|
||||
|
||||
void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET packet) {
|
||||
auto xorGamepadMask = input->active_gamepad_state ^ packet->activeGamepadMask;
|
||||
int updateGamepads(std::vector<gamepad_t> &gamepads, std::int16_t old_state, std::int16_t new_state) {
|
||||
static std::bitset<platf::MAX_GAMEPADS> gamepadMask {};
|
||||
|
||||
for(int x = 0; x < platf::MAX_GAMEPADS; ++x) {
|
||||
auto xorGamepadMask = old_state ^ new_state;
|
||||
if (!xorGamepadMask) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(int x = 0; x < sizeof(std::int16_t) * 8; ++x) {
|
||||
if((xorGamepadMask >> x) & 1) {
|
||||
if((input->active_gamepad_state >> x) & 1) {
|
||||
platf::gamepad(input->input, x, platf::gamepad_state_t {});
|
||||
platf::free_gamepad(input->input, x);
|
||||
auto &gamepad = gamepads[x];
|
||||
|
||||
if((old_state >> x) & 1) {
|
||||
if (gamepad.id < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
platf::gamepad(platf_input, gamepad.id, platf::gamepad_state_t{});
|
||||
platf::free_gamepad(platf_input, gamepad.id);
|
||||
|
||||
free_id(gamepadMask, gamepad.id);
|
||||
|
||||
gamepad.id = -1;
|
||||
}
|
||||
else if(platf::alloc_gamepad(input->input, x)) {
|
||||
// allocating a gamepad failed: solution: ignore gamepads
|
||||
// The implementations of platf::alloc_gamepad already have logging
|
||||
return;
|
||||
else {
|
||||
auto id = alloc_id(gamepadMask);
|
||||
|
||||
if(id < 0) {
|
||||
// Out of gamepads
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(platf::alloc_gamepad(platf_input, id)) {
|
||||
free_id(gamepadMask, id);
|
||||
// allocating a gamepad failed: solution: ignore gamepads
|
||||
// The implementations of platf::alloc_gamepad already has logging
|
||||
return -1;
|
||||
}
|
||||
|
||||
gamepad.id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET packet) {
|
||||
if(updateGamepads(input->gamepads, input->active_gamepad_state, packet->activeGamepadMask)) {
|
||||
return;
|
||||
}
|
||||
|
||||
input->active_gamepad_state = packet->activeGamepadMask;
|
||||
|
||||
if(packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads.size()) {
|
||||
@ -147,11 +239,19 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET pa
|
||||
}
|
||||
|
||||
if(!((input->active_gamepad_state >> packet->controllerNumber) & 1)) {
|
||||
BOOST_LOG(warning) << "ControllerNumber ["sv << packet->controllerNumber << "] not allocated"sv;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto &gamepad = input->gamepads[packet->controllerNumber];
|
||||
|
||||
// If this gamepad has not been initialized, ignore it.
|
||||
// This could happen when platf::alloc_gamepad fails
|
||||
if(gamepad.id < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
display_cursor = false;
|
||||
|
||||
std::uint16_t bf = packet->buttonFlags;
|
||||
@ -188,7 +288,6 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET pa
|
||||
|
||||
if (platf::BACK & bf) {
|
||||
if (platf::BACK & bf_new) {
|
||||
|
||||
// Don't emulate home button if timeout < 0
|
||||
if(config::input.back_button_timeout >= 0ms) {
|
||||
gamepad.back_timeout_id = task_pool.pushDelayed([input, controller=packet->controllerNumber]() {
|
||||
@ -199,15 +298,15 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET pa
|
||||
// Force the back button up
|
||||
gamepad.back_button_state = button_state_e::UP;
|
||||
state.buttonFlags &= ~platf::BACK;
|
||||
platf::gamepad(input->input, controller, state);
|
||||
platf::gamepad(platf_input, gamepad.id, state);
|
||||
|
||||
// Press Home button
|
||||
state.buttonFlags |= platf::HOME;
|
||||
platf::gamepad(input->input, controller, state);
|
||||
platf::gamepad(platf_input, gamepad.id, state);
|
||||
|
||||
// Release Home button
|
||||
state.buttonFlags &= ~platf::HOME;
|
||||
platf::gamepad(input->input, controller, state);
|
||||
platf::gamepad(platf_input, gamepad.id, state);
|
||||
|
||||
gamepad.back_timeout_id = nullptr;
|
||||
}, config::input.back_button_timeout).task_id;
|
||||
@ -219,7 +318,7 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET pa
|
||||
}
|
||||
}
|
||||
|
||||
platf::gamepad(input->input, packet->controllerNumber, gamepad_state);
|
||||
platf::gamepad(platf_input, gamepad.id, gamepad_state);
|
||||
|
||||
gamepad.gamepad_state = gamepad_state;
|
||||
}
|
||||
@ -231,7 +330,7 @@ void passthrough_helper(std::shared_ptr<input_t> input, std::vector<std::uint8_t
|
||||
|
||||
switch(input_type) {
|
||||
case PACKET_TYPE_MOUSE_MOVE:
|
||||
passthrough(input->input, (PNV_MOUSE_MOVE_PACKET)payload);
|
||||
passthrough(platf_input, (PNV_MOUSE_MOVE_PACKET)payload);
|
||||
break;
|
||||
case PACKET_TYPE_MOUSE_BUTTON:
|
||||
passthrough(input, (PNV_MOUSE_BUTTON_PACKET)payload);
|
||||
@ -240,7 +339,7 @@ void passthrough_helper(std::shared_ptr<input_t> input, std::vector<std::uint8_t
|
||||
{
|
||||
char *tmp_input = (char*)payload + 4;
|
||||
if(tmp_input[0] == 0x0A) {
|
||||
passthrough(input->input, (PNV_SCROLL_PACKET)payload);
|
||||
passthrough(platf_input, (PNV_SCROLL_PACKET)payload);
|
||||
}
|
||||
else {
|
||||
passthrough(input, (PNV_KEYBOARD_PACKET)payload);
|
||||
@ -254,35 +353,15 @@ void passthrough_helper(std::shared_ptr<input_t> input, std::vector<std::uint8_t
|
||||
}
|
||||
}
|
||||
|
||||
void reset_helper(std::shared_ptr<input_t> input) {
|
||||
for(auto &[key_press, key_down] : input->key_press) {
|
||||
if(key_down) {
|
||||
key_down = false;
|
||||
platf::keyboard(input->input, key_press & 0x00FF, true);
|
||||
}
|
||||
}
|
||||
|
||||
auto &mouse_press = input->mouse_press;
|
||||
for(int x = 0; x < mouse_press.size(); ++x) {
|
||||
if(mouse_press[x]) {
|
||||
mouse_press[x] = false;
|
||||
|
||||
platf::button_mouse(input->input, x + 1, true);
|
||||
}
|
||||
}
|
||||
|
||||
NV_MULTI_CONTROLLER_PACKET fake_packet {};
|
||||
passthrough(input, &fake_packet);
|
||||
}
|
||||
|
||||
void passthrough(std::shared_ptr<input_t> &input, std::vector<std::uint8_t> &&input_data) {
|
||||
task_pool.push(passthrough_helper, input, util::cmove(input_data));
|
||||
}
|
||||
|
||||
void reset(std::shared_ptr<input_t> &input) {
|
||||
task_pool.push(reset_helper, input);
|
||||
void init() {
|
||||
platf_input = platf::input();
|
||||
}
|
||||
|
||||
input_t::input_t() : mouse_press {}, input { platf::input() }, active_gamepad_state {}, gamepads (platf::MAX_GAMEPADS) {}
|
||||
gamepad_t::gamepad_t() : gamepad_state {}, back_timeout_id {}, back_button_state { button_state_e::NONE } {}
|
||||
std::shared_ptr<input_t> alloc() {
|
||||
return std::make_shared<input_t>();
|
||||
}
|
||||
}
|
||||
|
@ -5,45 +5,17 @@
|
||||
#ifndef SUNSHINE_INPUT_H
|
||||
#define SUNSHINE_INPUT_H
|
||||
|
||||
#include "platform/common.h"
|
||||
#include "thread_pool.h"
|
||||
|
||||
namespace input {
|
||||
enum class button_state_e {
|
||||
NONE,
|
||||
DOWN,
|
||||
UP
|
||||
};
|
||||
|
||||
struct gamepad_t {
|
||||
gamepad_t();
|
||||
platf::gamepad_state_t gamepad_state;
|
||||
|
||||
util::ThreadPool::task_id_t back_timeout_id;
|
||||
|
||||
|
||||
// When emulating the HOME button, we may need to artificially release the back button.
|
||||
// Afterwards, the gamepad state on sunshine won't match the state on Moonlight.
|
||||
// To prevent Sunshine from sending erronious input data to the active application,
|
||||
// Sunshine forces the button to be in a specific state until the gamepad state matches that of
|
||||
// Moonlight once more.
|
||||
button_state_e back_button_state;
|
||||
};
|
||||
struct input_t {
|
||||
input_t();
|
||||
|
||||
std::unordered_map<short, bool> key_press;
|
||||
std::array<std::uint8_t, 5> mouse_press;
|
||||
|
||||
platf::input_t input;
|
||||
|
||||
std::uint16_t active_gamepad_state;
|
||||
std::vector<gamepad_t> gamepads;
|
||||
};
|
||||
struct input_t;
|
||||
|
||||
void print(void *input);
|
||||
void passthrough(std::shared_ptr<input_t> &input, std::vector<std::uint8_t> &&input_data);
|
||||
void reset(std::shared_ptr<input_t> &input);
|
||||
|
||||
void init();
|
||||
|
||||
std::shared_ptr<input_t> alloc();
|
||||
}
|
||||
|
||||
#endif //SUNSHINE_INPUT_H
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
|
||||
#include "input.h"
|
||||
#include "nvhttp.h"
|
||||
#include "rtsp.h"
|
||||
#include "config.h"
|
||||
@ -133,6 +134,7 @@ int main(int argc, char *argv[]) {
|
||||
proc::proc = std::move(*proc_opt);
|
||||
|
||||
auto deinit_guard = platf::init();
|
||||
input::init();
|
||||
reed_solomon_init();
|
||||
|
||||
task_pool.start(1);
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
struct sockaddr;
|
||||
namespace platf {
|
||||
constexpr auto MAX_GAMEPADS = 2;
|
||||
constexpr auto MAX_GAMEPADS = 32;
|
||||
|
||||
constexpr std::uint16_t DPAD_UP = 0x0001;
|
||||
constexpr std::uint16_t DPAD_DOWN = 0x0002;
|
||||
|
@ -401,11 +401,6 @@ void cmd_play(rtsp_server_t *server, net::peer_t peer, msg_t &&req) {
|
||||
}
|
||||
|
||||
void rtpThread(std::shared_ptr<safe::signal_t> shutdown_event) {
|
||||
input = std::make_shared<input::input_t>();
|
||||
auto fg = util::fail_guard([&]() {
|
||||
input.reset();
|
||||
});
|
||||
|
||||
rtsp_server_t server(RTSP_SETUP_PORT);
|
||||
|
||||
server.map("OPTIONS"sv, &cmd_option);
|
||||
|
@ -167,6 +167,7 @@ struct broadcast_ctx_t {
|
||||
|
||||
struct session_t {
|
||||
config_t config;
|
||||
std::shared_ptr<input::input_t> input;
|
||||
|
||||
std::thread audioThread;
|
||||
std::thread videoThread;
|
||||
@ -202,7 +203,6 @@ struct session_t {
|
||||
int start_broadcast(broadcast_ctx_t &ctx);
|
||||
void end_broadcast(broadcast_ctx_t &ctx);
|
||||
|
||||
std::shared_ptr<input::input_t> input;
|
||||
|
||||
static auto broadcast = safe::make_shared<broadcast_ctx_t>(start_broadcast, end_broadcast);
|
||||
safe::signal_t broadcast_shutdown_event;
|
||||
@ -453,7 +453,7 @@ void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *se
|
||||
}
|
||||
|
||||
input::print(plaintext.data());
|
||||
input::passthrough(input, std::move(plaintext));
|
||||
input::passthrough(session->input, std::move(plaintext));
|
||||
});
|
||||
|
||||
while(!shutdown_event->peek()) {
|
||||
@ -855,6 +855,8 @@ void join(session_t &session) {
|
||||
}
|
||||
|
||||
void start(session_t &session, const std::string &addr_string) {
|
||||
session.input = input::alloc();
|
||||
|
||||
session.broadcast_ref = broadcast.ref();
|
||||
session.broadcast_ref->control_server.emplace_addr_to_session(addr_string, session);
|
||||
|
||||
|
@ -11,10 +11,6 @@
|
||||
#include "audio.h"
|
||||
#include "crypto.h"
|
||||
|
||||
namespace input {
|
||||
struct input_t;
|
||||
}
|
||||
|
||||
namespace stream {
|
||||
struct session_t;
|
||||
struct config_t {
|
||||
@ -41,7 +37,6 @@ void join(session_t &session);
|
||||
state_e state(session_t &session);
|
||||
}
|
||||
|
||||
extern std::shared_ptr<input::input_t> input;
|
||||
extern safe::signal_t broadcast_shutdown_event;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user