Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement controller arrival metadata support #1402

Merged
merged 1 commit into from
Jul 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 5 additions & 4 deletions docs/source/about/advanced_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,18 @@ gamepad
===== ===========
Value Description
===== ===========
x360 xbox 360 controller
ds4 dualshock controller (PS4)
auto Selected based on information from client
x360 Xbox 360 controller
ds4 DualShock 4 controller (PS4)
===== ===========

**Default**
``x360``
``auto``

**Example**
.. code-block:: text

gamepad = x360
gamepad = auto

back_button_timeout
^^^^^^^^^^^^^^^^^^^
Expand Down
63 changes: 62 additions & 1 deletion src/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,21 @@ namespace input {
<< "--end controller packet--"sv;
}

/**
* @brief Prints a controller arrival packet.
* @param packet The controller arrival packet.
*/
void
print(PSS_CONTROLLER_ARRIVAL_PACKET packet) {
BOOST_LOG(debug)
<< "--begin controller arrival packet--"sv << std::endl
<< "controllerNumber ["sv << (uint32_t) packet->controllerNumber << ']' << std::endl
<< "type ["sv << util::hex(packet->type).to_string_view() << ']' << std::endl
<< "capabilities ["sv << util::hex(packet->capabilities).to_string_view() << ']' << std::endl
<< "supportedButtonFlags ["sv << util::hex(packet->supportedButtonFlags).to_string_view() << ']' << std::endl
<< "--end controller arrival packet--"sv;
}

void
print(void *payload) {
auto header = (PNV_INPUT_HEADER) payload;
Expand Down Expand Up @@ -295,6 +310,9 @@ namespace input {
case MULTI_CONTROLLER_MAGIC_GEN5:
print((PNV_MULTI_CONTROLLER_PACKET) payload);
break;
case SS_CONTROLLER_ARRIVAL_MAGIC:
print((PSS_CONTROLLER_ARRIVAL_PACKET) payload);
break;
}
}

Expand Down Expand Up @@ -643,7 +661,7 @@ namespace input {
return -1;
}

if (platf::alloc_gamepad(platf_input, id, rumble_queue)) {
if (platf::alloc_gamepad(platf_input, id, {}, rumble_queue)) {
free_id(gamepadMask, id);
// allocating a gamepad failed: solution: ignore gamepads
// The implementations of platf::alloc_gamepad already has logging
Expand All @@ -658,6 +676,46 @@ namespace input {
return 0;
}

/**
* @brief Called to pass a controller arrival message to the platform backend.
* @param input The input context pointer.
* @param packet The controller arrival packet.
*/
void
passthrough(std::shared_ptr<input_t> &input, PSS_CONTROLLER_ARRIVAL_PACKET packet) {
if (!config::input.controller) {
return;
}

if (packet->controllerNumber >= gamepadMask.size()) {
// Invalid controller number
return;
}

if (gamepadMask[packet->controllerNumber]) {
// There's already a gamepad in this slot
return;
}

platf::gamepad_arrival_t arrival {
packet->controllerNumber,
packet->type,
util::endian::little(packet->capabilities),
util::endian::little(packet->supportedButtonFlags),
};

gamepadMask[packet->controllerNumber] = true;
input->active_gamepad_state |= (1 << packet->controllerNumber);

// Allocate a new gamepad
if (platf::alloc_gamepad(platf_input, packet->controllerNumber, arrival, input->rumble_queue)) {
free_id(gamepadMask, packet->controllerNumber);
return;
}

input->gamepads[packet->controllerNumber].id = packet->controllerNumber;
}

void
passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET packet) {
if (!config::input.controller) {
Expand Down Expand Up @@ -1135,6 +1193,9 @@ namespace input {
case MULTI_CONTROLLER_MAGIC_GEN5:
passthrough(input, (PNV_MULTI_CONTROLLER_PACKET) payload);
break;
case SS_CONTROLLER_ARRIVAL_MAGIC:
passthrough(input, (PSS_CONTROLLER_ARRIVAL_PACKET) payload);
break;
}
}

Expand Down
17 changes: 16 additions & 1 deletion src/platform/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,13 @@ namespace platf {
std::int16_t rsY;
};

struct gamepad_arrival_t {
std::uint8_t gamepadNumber;
std::uint8_t type;
std::uint16_t capabilities;
std::uint32_t supportedButtons;
};

class deinit_t {
public:
virtual ~deinit_t() = default;
Expand Down Expand Up @@ -455,8 +462,16 @@ namespace platf {
void
unicode(input_t &input, char *utf8, int size);

/**
* @brief Creates a new virtual gamepad.
* @param input The input context.
* @param nr The assigned controller number.
* @param metadata Controller metadata from client (empty if none provided).
* @param rumble_queue The queue for posting rumble messages to the client.
* @return 0 on success.
*/
int
alloc_gamepad(input_t &input, int nr, rumble_queue_t rumble_queue);
alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue);
void
free_gamepad(input_t &input, int nr);

Expand Down
21 changes: 18 additions & 3 deletions src/platform/linux/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -770,8 +770,15 @@ namespace platf {
return 0;
}

/**
* @brief Creates a new virtual gamepad.
* @param nr The assigned controller number.
* @param metadata Controller metadata from client (empty if none provided).
* @param rumble_queue The queue for posting rumble messages to the client.
* @return 0 on success.
*/
int
alloc_gamepad(int nr, rumble_queue_t &&rumble_queue) {
alloc_gamepad(int nr, const gamepad_arrival_t &metadata, rumble_queue_t &&rumble_queue) {
TUPLE_2D_REF(input, gamepad_state, gamepads[nr]);

int err = libevdev_uinput_create_from_device(gamepad_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &input);
Expand Down Expand Up @@ -1480,9 +1487,17 @@ namespace platf {
keyboard_ev(kb, KEY_LEFTCTRL, 0);
}

/**
* @brief Creates a new virtual gamepad.
* @param input The input context.
* @param nr The assigned controller number.
* @param metadata Controller metadata from client (empty if none provided).
* @param rumble_queue The queue for posting rumble messages to the client.
* @return 0 on success.
*/
int
alloc_gamepad(input_t &input, int nr, rumble_queue_t rumble_queue) {
return ((input_raw_t *) input.get())->alloc_gamepad(nr, std::move(rumble_queue));
alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue) {
return ((input_raw_t *) input.get())->alloc_gamepad(nr, metadata, std::move(rumble_queue));
}

void
Expand Down
10 changes: 9 additions & 1 deletion src/platform/macos/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,16 @@ const KeyCodeMap kKeyCodesMap[] = {
BOOST_LOG(info) << "unicode: Unicode input not yet implemented for MacOS."sv;
}

/**
* @brief Creates a new virtual gamepad.
* @param input The input context.
* @param nr The assigned controller number.
* @param metadata Controller metadata from client (empty if none provided).
* @param rumble_queue The queue for posting rumble messages to the client.
* @return 0 on success.
*/
int
alloc_gamepad(input_t &input, int nr, rumble_queue_t rumble_queue) {
alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue) {
BOOST_LOG(info) << "alloc_gamepad: Gamepad not yet implemented for MacOS."sv;
return -1;
}
Expand Down
58 changes: 46 additions & 12 deletions src/platform/windows/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,6 @@ namespace platf {
using client_t = util::safe_ptr<_VIGEM_CLIENT_T, vigem_free>;
using target_t = util::safe_ptr<_VIGEM_TARGET_T, vigem_target_free>;

static VIGEM_TARGET_TYPE
map(const std::string_view &gp) {
if (gp == "x360"sv) {
return Xbox360Wired;
}

return DualShock4Wired;
}

void CALLBACK
x360_notify(
client_t::pointer client,
Expand Down Expand Up @@ -423,15 +414,54 @@ namespace platf {
}
}

/**
* @brief Creates a new virtual gamepad.
* @param input The input context.
* @param nr The assigned controller number.
* @param metadata Controller metadata from client (empty if none provided).
* @param rumble_queue The queue for posting rumble messages to the client.
* @return 0 on success.
*/
int
alloc_gamepad(input_t &input, int nr, rumble_queue_t rumble_queue) {
alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue) {
auto raw = (input_raw_t *) input.get();

if (!raw->vigem) {
return 0;
}

return raw->vigem->alloc_gamepad_interal(nr, rumble_queue, map(config::input.gamepad));
VIGEM_TARGET_TYPE selectedGamepadType;

if (config::input.gamepad == "x360"sv) {
BOOST_LOG(info) << "Gamepad " << nr << " will be Xbox 360 controller (manual selection)"sv;
selectedGamepadType = Xbox360Wired;
}
else if (config::input.gamepad == "ps4"sv || config::input.gamepad == "ds4"sv) {
BOOST_LOG(info) << "Gamepad " << nr << " will be DualShock 4 controller (manual selection)"sv;
selectedGamepadType = DualShock4Wired;
}
else if (metadata.type == LI_CTYPE_PS) {
BOOST_LOG(info) << "Gamepad " << nr << " will be DualShock 4 controller (auto-selected by client-reported type)"sv;
selectedGamepadType = DualShock4Wired;
}
else if (metadata.type == LI_CTYPE_XBOX) {
BOOST_LOG(info) << "Gamepad " << nr << " will be Xbox 360 controller (auto-selected by client-reported type)"sv;
selectedGamepadType = Xbox360Wired;
}
else if (metadata.capabilities & (LI_CCAP_ACCEL | LI_CCAP_GYRO)) {
BOOST_LOG(info) << "Gamepad " << nr << " will be DualShock 4 controller (auto-selected by motion sensor presence)"sv;
selectedGamepadType = DualShock4Wired;
}
else if (metadata.capabilities & LI_CCAP_TOUCHPAD) {
BOOST_LOG(info) << "Gamepad " << nr << " will be DualShock 4 controller (auto-selected by touchpad presence)"sv;
selectedGamepadType = DualShock4Wired;
}
else {
BOOST_LOG(info) << "Gamepad " << nr << " will be Xbox 360 controller (default)"sv;
selectedGamepadType = Xbox360Wired;
}

return raw->vigem->alloc_gamepad_interal(nr, rumble_queue, selectedGamepadType);
}

void
Expand Down Expand Up @@ -591,11 +621,15 @@ namespace platf {
delete input;
}

/**
* @brief Gets the supported gamepads for this platform backend.
* @return Vector of gamepad type strings.
*/
std::vector<std::string_view> &
supported_gamepads() {
// ds4 == ps4
static std::vector<std::string_view> gps {
"x360"sv, "ds4"sv, "ps4"sv
"auto"sv, "x360"sv, "ds4"sv, "ps4"sv
};

return gps;
Expand Down
5 changes: 3 additions & 2 deletions src_assets/common/assets/web/config.html
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,11 @@ <h1 class="my-4">Configuration</h1>
<div class="mb-3" v-if="platform === 'windows'">
<label for="gamepad" class="form-label">Gamepads</label>
<select id="gamepad" class="form-select" v-model="config.gamepad">
<option value="auto">Automatic</option>
<option value="ds4">DS4 (PS4)</option>
<option value="x360">X360 (Xbox 360)</option>
</select>
<div class="form-text">Choose which type of gamepad to Emulate on the host</div>
<div class="form-text">Choose which type of gamepad to emulate on the host</div>
</div>
<!--Ping Timeout-->
<div class="mb-3">
Expand Down Expand Up @@ -1029,7 +1030,7 @@ <h1 class="my-4">Configuration</h1>
"dwmflush": "enabled",
"encoder": "",
"fps": "[10,30,60,90,120]",
"gamepad": "x360",
"gamepad": "auto",
"hevc_mode": 0,
"key_rightalt_to_key_win": "disabled",
"keyboard": "enabled",
Expand Down