Skip to content

Commit

Permalink
feat: check gamepad status on startup
Browse files Browse the repository at this point in the history
  • Loading branch information
Hazer committed Jun 5, 2024
1 parent c761191 commit eaa08e9
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 28 deletions.
17 changes: 14 additions & 3 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,8 +434,8 @@ namespace config {
std::chrono::duration<double> { 1 / 24.9 }, // key_repeat_period

{
platf::supported_gamepads().front().data(),
platf::supported_gamepads().front().size(),
platf::supported_gamepads(nullptr).front().name.data(),
platf::supported_gamepads(nullptr).front().name.size(),
}, // Default gamepad
true, // back as touchpad click enabled (manual DS4 only)
true, // client gamepads with motion events are emulated as DS4
Expand Down Expand Up @@ -938,6 +938,17 @@ namespace config {
return ret;
}

std::vector<std::string_view> &
get_supported_gamepad_options() {
const auto options = platf::supported_gamepads(nullptr);
static std::vector<std::string_view> opts {};
opts.reserve(options.size());
for (auto &opt : options) {
opts.emplace_back(opt.name);
}
return opts;
}

void
apply_config(std::unordered_map<std::string, std::string> &&vars) {
if (!fs::exists(stream.file_apps.c_str())) {
Expand Down Expand Up @@ -1086,7 +1097,7 @@ namespace config {
input.key_repeat_delay = std::chrono::milliseconds { to };
}

string_restricted_f(vars, "gamepad"s, input.gamepad, platf::supported_gamepads());
string_restricted_f(vars, "gamepad"s, input.gamepad, get_supported_gamepad_options());
bool_f(vars, "ds4_back_as_touchpad_click", input.ds4_back_as_touchpad_click);
bool_f(vars, "motion_as_ds4", input.motion_as_ds4);
bool_f(vars, "touchpad_as_ds4", input.touchpad_as_ds4);
Expand Down
12 changes: 12 additions & 0 deletions src/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,18 @@ namespace input {
return std::make_unique<deinit_t>();
}

bool
probe_gamepads() {
auto input = static_cast<platf::input_t *>(platf_input.get());
const auto gamepads = platf::supported_gamepads(input);
for (auto &gamepad : gamepads) {
if (gamepad.is_enabled && gamepad.name != "auto") {
return false;
}
}
return true;
}

std::shared_ptr<input_t>
alloc(safe::mail_t mail) {
auto input = std::make_shared<input_t>(
Expand Down
3 changes: 3 additions & 0 deletions src/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ namespace input {
[[nodiscard]] std::unique_ptr<platf::deinit_t>
init();

bool
probe_gamepads();

std::shared_ptr<input_t>
alloc(safe::mail_t mail);

Expand Down
5 changes: 5 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@ main(int argc, char *argv[]) {

reed_solomon_init();
auto input_deinit_guard = input::init();

if (input::probe_gamepads()) {
BOOST_LOG(warning) << "No gamepad input is available"sv;
}

if (video::probe_encoders()) {
BOOST_LOG(error) << "Video failed to find working encoder"sv;
}
Expand Down
14 changes: 12 additions & 2 deletions src/platform/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ namespace platf {
constexpr std::uint32_t TOUCHPAD_BUTTON = 0x100000;
constexpr std::uint32_t MISC_BUTTON = 0x200000;

struct supported_gamepad_t {
std::string name;
bool is_enabled;
std::string reason_disabled_key;
};

enum class gamepad_feedback_e {
rumble,
rumble_triggers,
Expand Down Expand Up @@ -772,6 +778,10 @@ namespace platf {
[[nodiscard]] std::unique_ptr<deinit_t>
init();

std::vector<std::string_view> &
supported_gamepads();
/**
* @brief Gets the supported gamepads for this platform backend.
* @return Vector of gamepad options and status.
*/
std::vector<supported_gamepad_t> &
supported_gamepads(input_t *input);
} // namespace platf
6 changes: 3 additions & 3 deletions src/platform/linux/input/inputtino.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ namespace platf {
return caps;
}

std::vector<std::string_view> &
supported_gamepads() {
return platf::gamepad::supported_gamepads();
std::vector<supported_gamepad_t> &
supported_gamepads(input_t *input) {
return platf::gamepad::supported_gamepads(input);
}
} // namespace platf
82 changes: 78 additions & 4 deletions src/platform/linux/input/inputtino_gamepad.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <boost/locale.hpp>
#include <fstream>
#include <inputtino/input.hpp>
#include <libevdev/libevdev.h>

Expand All @@ -14,6 +15,13 @@ using namespace std::literals;

namespace platf::gamepad {

enum GamepadStatus {
UHID_NOT_AVAILABLE = 0,
UINPUT_NOT_AVAILABLE,
XINPUT_NOT_AVAILABLE,
GAMEPAD_STATUS // Helper to indicate the number of status
};

int
alloc(input_raw_t *raw, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) {
ControllerType selectedGamepadType;
Expand Down Expand Up @@ -237,12 +245,78 @@ namespace platf::gamepad {
}
}

std::vector<std::string_view> &
supported_gamepads() {
static std::vector<std::string_view> gps {
"auto"sv, "xone"sv, "5"sv, "switch"sv
std::bitset<GamepadStatus::GAMEPAD_STATUS>
checkGamepadStatus() {
std::bitset<GamepadStatus::GAMEPAD_STATUS> status;
// Check for uhid device file
std::ifstream uhid("/dev/uhid");
if (!uhid.good()) {
status.set(GamepadStatus::UHID_NOT_AVAILABLE);
}

// Check for uinput device file
std::ifstream uinput("/dev/uinput");
if (!uinput.good()) {
status.set(GamepadStatus::UINPUT_NOT_AVAILABLE);
}

// Check for xinput availability
std::array<char, 128> buffer {};
FILE *pipe = popen("xinput --list", "r");
if (!pipe) {
status.set(GamepadStatus::XINPUT_NOT_AVAILABLE);
}
else {
std::string result;
while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) {
result += buffer.data();
}
if (pclose(pipe) == -1 || result.find("unable to connect to X server") != std::string::npos) {
status.set(GamepadStatus::XINPUT_NOT_AVAILABLE);
}
}
return status;
}

std::vector<supported_gamepad_t> &
supported_gamepads(input_t *input) {
if (!input) {
static std::vector gps {
supported_gamepad_t { "auto", true, "" },
supported_gamepad_t { "xone", false, "" },
supported_gamepad_t { "5", false, "" },
supported_gamepad_t { "switch", false, "" },
};

return gps;
}

const auto status = checkGamepadStatus();

const bool uhid_available = !status.test(GamepadStatus::UHID_NOT_AVAILABLE);
const bool uxinput_available = !status.test(GamepadStatus::UINPUT_NOT_AVAILABLE) || !status.test(GamepadStatus::XINPUT_NOT_AVAILABLE);

std::string reason_disabled;
if (status.test(GamepadStatus::UINPUT_NOT_AVAILABLE)) {
reason_disabled = "gamepads.uinput-not-available";
}
else {
reason_disabled = "gamepads.xinput-not-available";
}

static std::vector gps {
supported_gamepad_t { "auto", true, "" },
supported_gamepad_t { "xone", uxinput_available, reason_disabled },
supported_gamepad_t { "5", uhid_available, "gamepads.uhid-not-available" },
supported_gamepad_t { "switch", uxinput_available, reason_disabled },
};

for (auto &[name, is_enabled, reason_disabled_key] : gps) {
if (!is_enabled) {
BOOST_LOG(warning) << "Gamepad " << name << " is disabled due to " << reason_disabled_key;
}
}

return gps;
}
} // namespace platf::gamepad
4 changes: 2 additions & 2 deletions src/platform/linux/input/inputtino_gamepad.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ namespace platf::gamepad {
void
battery(input_raw_t *raw, const gamepad_battery_t &battery);

std::vector<std::string_view> &
supported_gamepads();
std::vector<supported_gamepad_t> &
supported_gamepads(input_t *input);
} // namespace platf::gamepad
8 changes: 5 additions & 3 deletions src/platform/linux/input/legacy_input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2564,9 +2564,11 @@ namespace platf {
delete input;
}

std::vector<std::string_view> &
supported_gamepads() {
static std::vector<std::string_view> gamepads { "x360"sv };
std::vector<supported_gamepad_t> &
supported_gamepads(input_t *input) {
static std::vector gamepads {
supported_gamepad_t { "x360", true, "" },
};

return gamepads;
}
Expand Down
8 changes: 5 additions & 3 deletions src/platform/macos/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -564,9 +564,11 @@ const KeyCodeMap kKeyCodesMap[] = {
delete input;
}

std::vector<std::string_view> &
supported_gamepads() {
static std::vector<std::string_view> gamepads { ""sv };
std::vector<supported_gamepad_t> &
supported_gamepads(input_t *input) {
static std::vector gamepads {
supported_gamepad_t { "", false, "gamepads.macos_not_implemented" }
};

return gamepads;
}
Expand Down
32 changes: 24 additions & 8 deletions src/platform/windows/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1719,17 +1719,33 @@ 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() {
std::vector<supported_gamepad_t> &
supported_gamepads(input_t *input) {
bool enabled;
if (input) {
auto vigem = ((input_raw_t *) input)->vigem;
enabled = vigem != nullptr;
}
else {
enabled = false;
}

auto reason = enabled ? "" : "gamepads.vigem-not-available";

// ds4 == ps4
static std::vector<std::string_view> gps {
"auto"sv, "x360"sv, "ds4"sv, "ps4"sv
static std::vector gps {
supported_gamepad_t { "auto", true, reason },
supported_gamepad_t { "x360", enabled, reason },
supported_gamepad_t { "ds4", enabled, reason },
supported_gamepad_t { "ps4", enabled, reason }
};

for (auto &[name, is_enabled, reason_disabled_key] : gps) {
if (!is_enabled) {
BOOST_LOG(warning) << "Gamepad " << name << " is disabled due to " << reason_disabled_key;
}
}

return gps;
}

Expand Down

0 comments on commit eaa08e9

Please sign in to comment.