Convert rumble_queue into a generic feedback_queue for gamepad messages

This commit is contained in:
Cameron Gutman
2023-07-08 15:23:27 -05:00
parent fd77230964
commit 309fcc4142
7 changed files with 278 additions and 82 deletions

View File

@@ -140,12 +140,12 @@ namespace input {
input_t( input_t(
safe::mail_raw_t::event_t<input::touch_port_t> touch_port_event, safe::mail_raw_t::event_t<input::touch_port_t> touch_port_event,
platf::rumble_queue_t rumble_queue): platf::feedback_queue_t feedback_queue):
shortcutFlags {}, shortcutFlags {},
active_gamepad_state {}, active_gamepad_state {},
gamepads(MAX_GAMEPADS), gamepads(MAX_GAMEPADS),
touch_port_event { std::move(touch_port_event) }, touch_port_event { std::move(touch_port_event) },
rumble_queue { std::move(rumble_queue) }, feedback_queue { std::move(feedback_queue) },
mouse_left_button_timeout {}, mouse_left_button_timeout {},
touch_port { { 0, 0, 0, 0 }, 0, 0, 1.0f } {} touch_port { { 0, 0, 0, 0 }, 0, 0, 1.0f } {}
@@ -156,7 +156,7 @@ namespace input {
std::vector<gamepad_t> gamepads; std::vector<gamepad_t> gamepads;
safe::mail_raw_t::event_t<input::touch_port_t> touch_port_event; safe::mail_raw_t::event_t<input::touch_port_t> touch_port_event;
platf::rumble_queue_t rumble_queue; platf::feedback_queue_t feedback_queue;
std::list<std::vector<uint8_t>> input_queue; std::list<std::vector<uint8_t>> input_queue;
std::mutex input_queue_lock; std::mutex input_queue_lock;
@@ -703,7 +703,7 @@ namespace input {
} }
int int
updateGamepads(std::vector<gamepad_t> &gamepads, std::int16_t old_state, std::int16_t new_state, const platf::rumble_queue_t &rumble_queue) { updateGamepads(std::vector<gamepad_t> &gamepads, std::int16_t old_state, std::int16_t new_state, const platf::feedback_queue_t &feedback_queue) {
auto xorGamepadMask = old_state ^ new_state; auto xorGamepadMask = old_state ^ new_state;
if (!xorGamepadMask) { if (!xorGamepadMask) {
return 0; return 0;
@@ -729,7 +729,7 @@ namespace input {
return -1; return -1;
} }
if (platf::alloc_gamepad(platf_input, id, {}, rumble_queue)) { if (platf::alloc_gamepad(platf_input, id, {}, feedback_queue)) {
free_id(gamepadMask, id); free_id(gamepadMask, id);
// allocating a gamepad failed: solution: ignore gamepads // allocating a gamepad failed: solution: ignore gamepads
// The implementations of platf::alloc_gamepad already has logging // The implementations of platf::alloc_gamepad already has logging
@@ -776,7 +776,7 @@ namespace input {
input->active_gamepad_state |= (1 << packet->controllerNumber); input->active_gamepad_state |= (1 << packet->controllerNumber);
// Allocate a new gamepad // Allocate a new gamepad
if (platf::alloc_gamepad(platf_input, packet->controllerNumber, arrival, input->rumble_queue)) { if (platf::alloc_gamepad(platf_input, packet->controllerNumber, arrival, input->feedback_queue)) {
free_id(gamepadMask, packet->controllerNumber); free_id(gamepadMask, packet->controllerNumber);
return; return;
} }
@@ -900,7 +900,7 @@ namespace input {
return; return;
} }
if (updateGamepads(input->gamepads, input->active_gamepad_state, packet->activeGamepadMask, input->rumble_queue)) { if (updateGamepads(input->gamepads, input->active_gamepad_state, packet->activeGamepadMask, input->feedback_queue)) {
return; return;
} }
@@ -1443,7 +1443,7 @@ namespace input {
alloc(safe::mail_t mail) { alloc(safe::mail_t mail) {
auto input = std::make_shared<input_t>( auto input = std::make_shared<input_t>(
mail->event<input::touch_port_t>(mail::touch_port), mail->event<input::touch_port_t>(mail::touch_port),
mail->queue<platf::rumble_t>(mail::rumble)); mail->queue<platf::gamepad_feedback_msg_t>(mail::gamepad_feedback));
// Workaround to ensure new frames will be captured when a client connects // Workaround to ensure new frames will be captured when a client connects
task_pool.pushDelayed([]() { task_pool.pushDelayed([]() {

View File

@@ -62,7 +62,7 @@ namespace mail {
// Local mail // Local mail
MAIL(touch_port); MAIL(touch_port);
MAIL(idr); MAIL(idr);
MAIL(rumble); MAIL(gamepad_feedback);
MAIL(hdr); MAIL(hdr);
#undef MAIL #undef MAIL

View File

@@ -72,17 +72,75 @@ namespace platf {
constexpr std::uint32_t TOUCHPAD_BUTTON = 0x100000; constexpr std::uint32_t TOUCHPAD_BUTTON = 0x100000;
constexpr std::uint32_t MISC_BUTTON = 0x200000; constexpr std::uint32_t MISC_BUTTON = 0x200000;
struct rumble_t { enum class gamepad_feedback_e {
KITTY_DEFAULT_CONSTR(rumble_t) rumble,
rumble_triggers,
set_motion_event_state,
set_rgb_led,
};
rumble_t(std::uint16_t id, std::uint16_t lowfreq, std::uint16_t highfreq): struct gamepad_feedback_msg_t {
id { id }, lowfreq { lowfreq }, highfreq { highfreq } {} static gamepad_feedback_msg_t
make_rumble(std::uint16_t id, std::uint16_t lowfreq, std::uint16_t highfreq) {
gamepad_feedback_msg_t msg;
msg.type = gamepad_feedback_e::rumble;
msg.id = id;
msg.data.rumble = { lowfreq, highfreq };
return msg;
}
static gamepad_feedback_msg_t
make_rumble_triggers(std::uint16_t id, std::uint16_t left, std::uint16_t right) {
gamepad_feedback_msg_t msg;
msg.type = gamepad_feedback_e::rumble_triggers;
msg.id = id;
msg.data.rumble_triggers = { left, right };
return msg;
}
static gamepad_feedback_msg_t
make_motion_event_state(std::uint16_t id, std::uint8_t motion_type, std::uint16_t report_rate) {
gamepad_feedback_msg_t msg;
msg.type = gamepad_feedback_e::set_motion_event_state;
msg.id = id;
msg.data.motion_event_state.motion_type = motion_type;
msg.data.motion_event_state.report_rate = report_rate;
return msg;
}
static gamepad_feedback_msg_t
make_rgb_led(std::uint16_t id, std::uint8_t r, std::uint8_t g, std::uint8_t b) {
gamepad_feedback_msg_t msg;
msg.type = gamepad_feedback_e::set_rgb_led;
msg.id = id;
msg.data.rgb_led = { r, g, b };
return msg;
}
gamepad_feedback_e type;
std::uint16_t id; std::uint16_t id;
union {
struct {
std::uint16_t lowfreq; std::uint16_t lowfreq;
std::uint16_t highfreq; std::uint16_t highfreq;
} rumble;
struct {
std::uint16_t left_trigger;
std::uint16_t right_trigger;
} rumble_triggers;
struct {
std::uint16_t report_rate;
std::uint8_t motion_type;
} motion_event_state;
struct {
std::uint8_t r;
std::uint8_t g;
std::uint8_t b;
} rgb_led;
} data;
}; };
using rumble_queue_t = safe::mail_raw_t::queue_t<rumble_t>;
using feedback_queue_t = safe::mail_raw_t::queue_t<gamepad_feedback_msg_t>;
namespace speaker { namespace speaker {
enum speaker_e { enum speaker_e {
@@ -525,11 +583,11 @@ namespace platf {
* @param input The input context. * @param input The input context.
* @param nr The assigned controller number. * @param nr The assigned controller number.
* @param metadata Controller metadata from client (empty if none provided). * @param metadata Controller metadata from client (empty if none provided).
* @param rumble_queue The queue for posting rumble messages to the client. * @param feedback_queue The queue for posting messages back to the client.
* @return 0 on success. * @return 0 on success.
*/ */
int int
alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue); alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue);
void void
free_gamepad(input_t &input, int nr); free_gamepad(input_t &input, int nr);

View File

@@ -133,7 +133,7 @@ namespace platf {
} }
}); });
using mail_evdev_t = std::tuple<int, uinput_t::pointer, rumble_queue_t, pollfd_t>; using mail_evdev_t = std::tuple<int, uinput_t::pointer, feedback_queue_t, pollfd_t>;
struct keycode_t { struct keycode_t {
std::uint32_t keycode; std::uint32_t keycode;
@@ -452,7 +452,7 @@ namespace platf {
public: public:
KITTY_DEFAULT_CONSTR_MOVE(effect_t) KITTY_DEFAULT_CONSTR_MOVE(effect_t)
effect_t(int gamepadnr, uinput_t::pointer dev, rumble_queue_t &&q): effect_t(int gamepadnr, uinput_t::pointer dev, feedback_queue_t &&q):
gamepadnr { gamepadnr }, dev { dev }, rumble_queue { std::move(q) }, gain { 0xFFFF }, id_to_data {} {} gamepadnr { gamepadnr }, dev { dev }, rumble_queue { std::move(q) }, gain { 0xFFFF }, id_to_data {} {}
class data_t { class data_t {
@@ -634,7 +634,7 @@ namespace platf {
// Used as ID for adding/removinf devices from evdev notifications // Used as ID for adding/removinf devices from evdev notifications
uinput_t::pointer dev; uinput_t::pointer dev;
rumble_queue_t rumble_queue; feedback_queue_t rumble_queue;
int gain; int gain;
@@ -774,11 +774,11 @@ namespace platf {
* @brief Creates a new virtual gamepad. * @brief Creates a new virtual gamepad.
* @param nr The assigned controller number. * @param nr The assigned controller number.
* @param metadata Controller metadata from client (empty if none provided). * @param metadata Controller metadata from client (empty if none provided).
* @param rumble_queue The queue for posting rumble messages to the client. * @param feedback_queue The queue for posting messages back to the client.
* @return 0 on success. * @return 0 on success.
*/ */
int int
alloc_gamepad(int nr, const gamepad_arrival_t &metadata, rumble_queue_t &&rumble_queue) { alloc_gamepad(int nr, const gamepad_arrival_t &metadata, feedback_queue_t &&feedback_queue) {
TUPLE_2D_REF(input, gamepad_state, gamepads[nr]); TUPLE_2D_REF(input, gamepad_state, gamepads[nr]);
int err = libevdev_uinput_create_from_device(gamepad_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &input); int err = libevdev_uinput_create_from_device(gamepad_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &input);
@@ -803,7 +803,7 @@ namespace platf {
rumble_ctx->rumble_queue_queue.raise( rumble_ctx->rumble_queue_queue.raise(
nr, nr,
input.get(), input.get(),
std::move(rumble_queue), std::move(feedback_queue),
pollfd_t { pollfd_t {
dup(libevdev_uinput_get_fd(input.get())), dup(libevdev_uinput_get_fd(input.get())),
(std::int16_t) POLLIN, (std::int16_t) POLLIN,
@@ -1048,9 +1048,9 @@ namespace platf {
TUPLE_2D(weak, strong, effect.rumble(now)); TUPLE_2D(weak, strong, effect.rumble(now));
if (old_weak != weak || old_strong != strong) { if (old_weak != weak || old_strong != strong) {
BOOST_LOG(debug) << "Sending haptic feedback: lowfreq [0x"sv << util::hex(weak).to_string_view() << "]: highfreq [0x"sv << util::hex(strong).to_string_view() << ']'; BOOST_LOG(debug) << "Sending haptic feedback: lowfreq [0x"sv << util::hex(strong).to_string_view() << "]: highfreq [0x"sv << util::hex(weak).to_string_view() << ']';
effect.rumble_queue->raise(effect.gamepadnr, weak, strong); effect.rumble_queue->raise(gamepad_feedback_msg_t::make_rumble(effect.gamepadnr, strong, weak));
} }
} }
} }
@@ -1492,12 +1492,12 @@ namespace platf {
* @param input The input context. * @param input The input context.
* @param nr The assigned controller number. * @param nr The assigned controller number.
* @param metadata Controller metadata from client (empty if none provided). * @param metadata Controller metadata from client (empty if none provided).
* @param rumble_queue The queue for posting rumble messages to the client. * @param feedback_queue The queue for posting messages back to the client.
* @return 0 on success. * @return 0 on success.
*/ */
int int
alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue) { alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) {
return ((input_raw_t *) input.get())->alloc_gamepad(nr, metadata, std::move(rumble_queue)); return ((input_raw_t *) input.get())->alloc_gamepad(nr, metadata, std::move(feedback_queue));
} }
void void

View File

@@ -293,11 +293,11 @@ const KeyCodeMap kKeyCodesMap[] = {
* @param input The input context. * @param input The input context.
* @param nr The assigned controller number. * @param nr The assigned controller number.
* @param metadata Controller metadata from client (empty if none provided). * @param metadata Controller metadata from client (empty if none provided).
* @param rumble_queue The queue for posting rumble messages to the client. * @param feedback_queue The queue for posting messages back to the client.
* @return 0 on success. * @return 0 on success.
*/ */
int int
alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue) { alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) {
BOOST_LOG(info) << "alloc_gamepad: Gamepad not yet implemented for MacOS."sv; BOOST_LOG(info) << "alloc_gamepad: Gamepad not yet implemented for MacOS."sv;
return -1; return -1;
} }

View File

@@ -44,12 +44,15 @@ namespace platf {
struct gamepad_context_t { struct gamepad_context_t {
target_t gp; target_t gp;
rumble_queue_t rumble_queue; feedback_queue_t feedback_queue;
union { union {
XUSB_REPORT x360; XUSB_REPORT x360;
DS4_REPORT_EX ds4; DS4_REPORT_EX ds4;
} report; } report;
gamepad_feedback_msg_t last_rumble;
gamepad_feedback_msg_t last_rgb_led;
}; };
constexpr float EARTH_G = 9.80665f; constexpr float EARTH_G = 9.80665f;
@@ -181,12 +184,12 @@ namespace platf {
/** /**
* @brief Attaches a new gamepad. * @brief Attaches a new gamepad.
* @param nr The gamepad index. * @param nr The gamepad index.
* @param rumble_queue The queue to publish rumble packets. * @param feedback_queue The queue for posting messages back to the client.
* @param gp_type The type of gamepad. * @param gp_type The type of gamepad.
* @return 0 on success. * @return 0 on success.
*/ */
int int
alloc_gamepad_internal(int nr, rumble_queue_t &rumble_queue, VIGEM_TARGET_TYPE gp_type) { alloc_gamepad_internal(int nr, feedback_queue_t &feedback_queue, VIGEM_TARGET_TYPE gp_type) {
auto &gamepad = gamepads[nr]; auto &gamepad = gamepads[nr];
assert(!gamepad.gp); assert(!gamepad.gp);
@@ -212,7 +215,7 @@ namespace platf {
return -1; return -1;
} }
gamepad.rumble_queue = std::move(rumble_queue); gamepad.feedback_queue = std::move(feedback_queue);
if (gp_type == Xbox360Wired) { if (gp_type == Xbox360Wired) {
status = vigem_target_x360_register_notification(client.get(), gamepad.gp.get(), x360_notify, this); status = vigem_target_x360_register_notification(client.get(), gamepad.gp.get(), x360_notify, this);
@@ -247,19 +250,52 @@ namespace platf {
} }
/** /**
* @brief Pass rumble data back to the host. * @brief Pass rumble data back to the client.
* @param target The gamepad. * @param target The gamepad.
* @param smallMotor The small motor.
* @param largeMotor The large motor. * @param largeMotor The large motor.
* @param smallMotor The small motor.
*/ */
void void
rumble(target_t::pointer target, std::uint8_t smallMotor, std::uint8_t largeMotor) { rumble(target_t::pointer target, std::uint8_t largeMotor, std::uint8_t smallMotor) {
for (int x = 0; x < gamepads.size(); ++x) { for (int x = 0; x < gamepads.size(); ++x) {
auto &gamepad = gamepads[x]; auto &gamepad = gamepads[x];
if (gamepad.gp.get() == target) { if (gamepad.gp.get() == target) {
gamepad.rumble_queue->raise(x, ((std::uint16_t) smallMotor) << 8, ((std::uint16_t) largeMotor) << 8); // Convert from 8-bit to 16-bit values
uint16_t normalizedLargeMotor = largeMotor << 8;
uint16_t normalizedSmallMotor = smallMotor << 8;
// Don't resend duplicate rumble data
if (normalizedSmallMotor != gamepad.last_rumble.data.rumble.highfreq ||
normalizedLargeMotor != gamepad.last_rumble.data.rumble.lowfreq) {
gamepad_feedback_msg_t msg = gamepad_feedback_msg_t::make_rumble(x, normalizedLargeMotor, normalizedSmallMotor);
gamepad.feedback_queue->raise(msg);
gamepad.last_rumble = msg;
}
return;
}
}
}
/**
* @brief Pass RGB LED data back to the client.
* @param target The gamepad.
* @param r The red channel.
* @param g The red channel.
* @param b The red channel.
*/
void
set_rgb_led(target_t::pointer target, std::uint8_t r, std::uint8_t g, std::uint8_t b) {
for (int x = 0; x < gamepads.size(); ++x) {
auto &gamepad = gamepads[x];
if (gamepad.gp.get() == target) {
// Don't resend duplicate RGB data
if (r != gamepad.last_rgb_led.data.rgb_led.r || g != gamepad.last_rgb_led.data.rgb_led.g || b != gamepad.last_rgb_led.data.rgb_led.b) {
gamepad_feedback_msg_t msg = gamepad_feedback_msg_t::make_rgb_led(x, r, g, b);
gamepad.feedback_queue->raise(msg);
gamepad.last_rgb_led = msg;
}
return; return;
} }
} }
@@ -299,7 +335,7 @@ namespace platf {
<< "largeMotor: "sv << (int) largeMotor << std::endl << "largeMotor: "sv << (int) largeMotor << std::endl
<< "smallMotor: "sv << (int) smallMotor; << "smallMotor: "sv << (int) smallMotor;
task_pool.push(&vigem_t::rumble, (vigem_t *) userdata, target, smallMotor, largeMotor); task_pool.push(&vigem_t::rumble, (vigem_t *) userdata, target, largeMotor, smallMotor);
} }
void CALLBACK void CALLBACK
@@ -307,13 +343,17 @@ namespace platf {
client_t::pointer client, client_t::pointer client,
target_t::pointer target, target_t::pointer target,
std::uint8_t largeMotor, std::uint8_t smallMotor, std::uint8_t largeMotor, std::uint8_t smallMotor,
DS4_LIGHTBAR_COLOR /* led_color */, DS4_LIGHTBAR_COLOR led_color,
void *userdata) { void *userdata) {
BOOST_LOG(debug) BOOST_LOG(debug)
<< "largeMotor: "sv << (int) largeMotor << std::endl << "largeMotor: "sv << (int) largeMotor << std::endl
<< "smallMotor: "sv << (int) smallMotor; << "smallMotor: "sv << (int) smallMotor << std::endl
<< "LED: "sv << util::hex(led_color.Red).to_string_view() << ' '
<< util::hex(led_color.Green).to_string_view() << ' '
<< util::hex(led_color.Blue).to_string_view() << std::endl;
task_pool.push(&vigem_t::rumble, (vigem_t *) userdata, target, smallMotor, largeMotor); task_pool.push(&vigem_t::rumble, (vigem_t *) userdata, target, largeMotor, smallMotor);
task_pool.push(&vigem_t::set_rgb_led, (vigem_t *) userdata, target, led_color.Red, led_color.Green, led_color.Blue);
} }
struct input_raw_t { struct input_raw_t {
@@ -554,11 +594,11 @@ namespace platf {
* @param input The input context. * @param input The input context.
* @param nr The assigned controller number. * @param nr The assigned controller number.
* @param metadata Controller metadata from client (empty if none provided). * @param metadata Controller metadata from client (empty if none provided).
* @param rumble_queue The queue for posting rumble messages to the client. * @param feedback_queue The queue for posting messages back to the client.
* @return 0 on success. * @return 0 on success.
*/ */
int int
alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue) { alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) {
auto raw = (input_raw_t *) input.get(); auto raw = (input_raw_t *) input.get();
if (!raw->vigem) { if (!raw->vigem) {
@@ -596,7 +636,7 @@ namespace platf {
selectedGamepadType = Xbox360Wired; selectedGamepadType = Xbox360Wired;
} }
return raw->vigem->alloc_gamepad_internal(nr, rumble_queue, selectedGamepadType); return raw->vigem->alloc_gamepad_internal(nr, feedback_queue, selectedGamepadType);
} }
void void

View File

@@ -39,6 +39,9 @@ extern "C" {
#define IDX_REQUEST_IDR_FRAME 9 #define IDX_REQUEST_IDR_FRAME 9
#define IDX_ENCRYPTED 10 #define IDX_ENCRYPTED 10
#define IDX_HDR_MODE 11 #define IDX_HDR_MODE 11
#define IDX_RUMBLE_TRIGGER_DATA 12
#define IDX_SET_MOTION_EVENT 13
#define IDX_SET_RGB_LED 14
static const short packetTypes[] = { static const short packetTypes[] = {
0x0305, // Start A 0x0305, // Start A
@@ -53,6 +56,9 @@ static const short packetTypes[] = {
0x0302, // IDR frame 0x0302, // IDR frame
0x0001, // fully encrypted 0x0001, // fully encrypted
0x010e, // HDR mode 0x010e, // HDR mode
0x5500, // Rumble triggers (Sunshine protocol extension)
0x5501, // Set motion event (Sunshine protocol extension)
0x5502, // Set RGB LED (Sunshine protocol extension)
}; };
namespace asio = boost::asio; namespace asio = boost::asio;
@@ -146,6 +152,31 @@ namespace stream {
std::uint16_t highfreq; std::uint16_t highfreq;
}; };
struct control_rumble_triggers_t {
control_header_v2 header;
std::uint16_t id;
std::uint16_t left;
std::uint16_t right;
};
struct control_set_motion_event_t {
control_header_v2 header;
std::uint16_t id;
std::uint16_t reportrate;
std::uint8_t type;
};
struct control_set_rgb_led_t {
control_header_v2 header;
std::uint16_t id;
std::uint8_t r;
std::uint8_t g;
std::uint8_t b;
};
struct control_hdr_mode_t { struct control_hdr_mode_t {
control_header_v2 header; control_header_v2 header;
@@ -350,7 +381,7 @@ namespace stream {
net::peer_t peer; net::peer_t peer;
std::uint8_t seq; std::uint8_t seq;
platf::rumble_queue_t rumble_queue; platf::feedback_queue_t feedback_queue;
safe::mail_raw_t::event_t<video::hdr_info_t> hdr_queue; safe::mail_raw_t::event_t<video::hdr_info_t> hdr_queue;
} control; } control;
@@ -621,37 +652,106 @@ namespace stream {
return replaced; return replaced;
} }
/**
* @brief Pass gamepad feedback data back to the client.
* @param session The session object.
* @param msg The message to pass.
* @return 0 on success.
*/
int int
send_rumble(session_t *session, std::uint16_t id, std::uint16_t lowfreq, std::uint16_t highfreq) { send_feedback_msg(session_t *session, platf::gamepad_feedback_msg_t &msg) {
if (!session->control.peer) { if (!session->control.peer) {
BOOST_LOG(warning) << "Couldn't send rumble data, still waiting for PING from Moonlight"sv; BOOST_LOG(warning) << "Couldn't send gamepad feedback data, still waiting for PING from Moonlight"sv;
// Still waiting for PING from Moonlight // Still waiting for PING from Moonlight
return -1; return -1;
} }
std::string payload;
if (msg.type == platf::gamepad_feedback_e::rumble) {
control_rumble_t plaintext; control_rumble_t plaintext;
plaintext.header.type = packetTypes[IDX_RUMBLE_DATA]; plaintext.header.type = packetTypes[IDX_RUMBLE_DATA];
plaintext.header.payloadLength = sizeof(control_rumble_t) - sizeof(control_header_v2); plaintext.header.payloadLength = sizeof(plaintext) - sizeof(control_header_v2);
auto &data = msg.data.rumble;
plaintext.useless = 0xC0FFEE; plaintext.useless = 0xC0FFEE;
plaintext.id = util::endian::little(id); plaintext.id = util::endian::little(msg.id);
plaintext.lowfreq = util::endian::little(lowfreq); plaintext.lowfreq = util::endian::little(data.lowfreq);
plaintext.highfreq = util::endian::little(highfreq); plaintext.highfreq = util::endian::little(data.highfreq);
BOOST_LOG(verbose) << id << " :: "sv << util::hex(lowfreq).to_string_view() << " :: "sv << util::hex(highfreq).to_string_view(); BOOST_LOG(verbose) << "Rumble: "sv << msg.id << " :: "sv << util::hex(data.lowfreq).to_string_view() << " :: "sv << util::hex(data.highfreq).to_string_view();
std::array<std::uint8_t, std::array<std::uint8_t,
sizeof(control_encrypted_t) + crypto::cipher::round_to_pkcs7_padded(sizeof(plaintext)) + crypto::cipher::tag_size> sizeof(control_encrypted_t) + crypto::cipher::round_to_pkcs7_padded(sizeof(plaintext)) + crypto::cipher::tag_size>
encrypted_payload; encrypted_payload;
auto payload = encode_control(session, util::view(plaintext), encrypted_payload); payload = encode_control(session, util::view(plaintext), encrypted_payload);
if (session->broadcast_ref->control_server.send(payload, session->control.peer)) { }
TUPLE_2D(port, addr, platf::from_sockaddr_ex((sockaddr *) &session->control.peer->address.address)); else if (msg.type == platf::gamepad_feedback_e::rumble_triggers) {
BOOST_LOG(warning) << "Couldn't send termination code to ["sv << addr << ':' << port << ']'; control_rumble_triggers_t plaintext;
plaintext.header.type = packetTypes[IDX_RUMBLE_TRIGGER_DATA];
plaintext.header.payloadLength = sizeof(plaintext) - sizeof(control_header_v2);
auto &data = msg.data.rumble_triggers;
plaintext.id = util::endian::little(msg.id);
plaintext.left = util::endian::little(data.left_trigger);
plaintext.right = util::endian::little(data.right_trigger);
BOOST_LOG(verbose) << "Rumble triggers: "sv << msg.id << " :: "sv << util::hex(data.left_trigger).to_string_view() << " :: "sv << util::hex(data.right_trigger).to_string_view();
std::array<std::uint8_t,
sizeof(control_encrypted_t) + crypto::cipher::round_to_pkcs7_padded(sizeof(plaintext)) + crypto::cipher::tag_size>
encrypted_payload;
payload = encode_control(session, util::view(plaintext), encrypted_payload);
}
else if (msg.type == platf::gamepad_feedback_e::set_motion_event_state) {
control_set_motion_event_t plaintext;
plaintext.header.type = packetTypes[IDX_SET_MOTION_EVENT];
plaintext.header.payloadLength = sizeof(plaintext) - sizeof(control_header_v2);
auto &data = msg.data.motion_event_state;
plaintext.id = util::endian::little(msg.id);
plaintext.reportrate = util::endian::little(data.report_rate);
plaintext.type = data.motion_type;
BOOST_LOG(verbose) << "Motion event state: "sv << msg.id << " :: "sv << util::hex(data.report_rate).to_string_view() << " :: "sv << util::hex(data.motion_type).to_string_view();
std::array<std::uint8_t,
sizeof(control_encrypted_t) + crypto::cipher::round_to_pkcs7_padded(sizeof(plaintext)) + crypto::cipher::tag_size>
encrypted_payload;
payload = encode_control(session, util::view(plaintext), encrypted_payload);
}
else if (msg.type == platf::gamepad_feedback_e::set_rgb_led) {
control_set_rgb_led_t plaintext;
plaintext.header.type = packetTypes[IDX_SET_RGB_LED];
plaintext.header.payloadLength = sizeof(plaintext) - sizeof(control_header_v2);
auto &data = msg.data.rgb_led;
plaintext.id = util::endian::little(msg.id);
plaintext.r = data.r;
plaintext.g = data.g;
plaintext.b = data.b;
BOOST_LOG(verbose) << "RGB: "sv << msg.id << " :: "sv << util::hex(data.r).to_string_view() << util::hex(data.g).to_string_view() << util::hex(data.b).to_string_view();
std::array<std::uint8_t,
sizeof(control_encrypted_t) + crypto::cipher::round_to_pkcs7_padded(sizeof(plaintext)) + crypto::cipher::tag_size>
encrypted_payload;
payload = encode_control(session, util::view(plaintext), encrypted_payload);
}
else {
BOOST_LOG(error) << "Unknown gamepad feedback message type"sv;
return -1; return -1;
} }
BOOST_LOG(debug) << "Send gamepadnr ["sv << id << "] with lowfreq ["sv << lowfreq << "] and highfreq ["sv << highfreq << ']'; if (session->broadcast_ref->control_server.send(payload, session->control.peer)) {
TUPLE_2D(port, addr, platf::from_sockaddr_ex((sockaddr *) &session->control.peer->address.address));
BOOST_LOG(warning) << "Couldn't send gamepad feedback to ["sv << addr << ':' << port << ']';
return -1;
}
return 0; return 0;
} }
@@ -858,23 +958,21 @@ namespace stream {
if (!session->control.peer) { if (!session->control.peer) {
has_session_awaiting_peer = true; has_session_awaiting_peer = true;
} }
else {
auto &feedback_queue = session->control.feedback_queue;
while (feedback_queue->peek()) {
auto feedback_msg = feedback_queue->pop();
auto &rumble_queue = session->control.rumble_queue; send_feedback_msg(session, *feedback_msg);
while (rumble_queue->peek()) {
auto rumble = rumble_queue->pop();
send_rumble(session, rumble->id, rumble->lowfreq, rumble->highfreq);
} }
// Unlike rumble which we send as best-effort, HDR state messages are critical
// for proper functioning of some clients. We must wait to pop entries from
// the queue until we're sure we have a peer to send them to.
auto &hdr_queue = session->control.hdr_queue; auto &hdr_queue = session->control.hdr_queue;
while (session->control.peer && hdr_queue->peek()) { while (session->control.peer && hdr_queue->peek()) {
auto hdr_info = hdr_queue->pop(); auto hdr_info = hdr_queue->pop();
send_hdr_mode(session, std::move(hdr_info)); send_hdr_mode(session, std::move(hdr_info));
} }
}
++pos; ++pos;
}) })
@@ -1648,7 +1746,7 @@ namespace stream {
session->config = config; session->config = config;
session->control.rumble_queue = mail->queue<platf::rumble_t>(mail::rumble); session->control.feedback_queue = mail->queue<platf::gamepad_feedback_msg_t>(mail::gamepad_feedback);
session->control.hdr_queue = mail->event<video::hdr_info_t>(mail::hdr); session->control.hdr_queue = mail->event<video::hdr_info_t>(mail::hdr);
session->control.iv = iv; session->control.iv = iv;
session->control.cipher = crypto::cipher::gcm_t { session->control.cipher = crypto::cipher::gcm_t {