mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-06-09 20:10:37 -04:00
UI: Controller connect/disconnect toasts
This commit is contained in:
+112
-6
@@ -2,7 +2,9 @@
|
||||
|
||||
#include <RmlUi/Core.h>
|
||||
#include <SDL3/SDL_filesystem.h>
|
||||
#include <absl/container/flat_hash_set.h>
|
||||
#include <aurora/rmlui.hpp>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
@@ -26,6 +28,12 @@ std::vector<std::unique_ptr<Document> > sDocumentStack;
|
||||
std::vector<std::unique_ptr<Document> > sPassiveDocuments;
|
||||
std::deque<Toast> sToasts;
|
||||
|
||||
// Sometimes gamepads can connect and disconnect quickly, especially during
|
||||
// connection negotiation. In this case, we'll receive an _ADDED event for a
|
||||
// disconnected gamepad. Storing IDs here lets use only show disconnected
|
||||
// notifications for gamepads that we sent a connected notification for.
|
||||
absl::flat_hash_set<SDL_JoystickID> sConnectedGamepads;
|
||||
|
||||
} // namespace
|
||||
|
||||
bool initialize() noexcept {
|
||||
@@ -51,11 +59,109 @@ bool initialize() noexcept {
|
||||
void shutdown() noexcept {
|
||||
sDocumentStack.clear();
|
||||
sPassiveDocuments.clear();
|
||||
reset_input_state();
|
||||
release_input_block();
|
||||
sConnectedGamepads.clear();
|
||||
input::reset_input_state();
|
||||
input::release_input_block();
|
||||
sInitialized = false;
|
||||
}
|
||||
|
||||
const char* battery_icon(SDL_PowerState state, int level) noexcept {
|
||||
if (state == SDL_POWERSTATE_UNKNOWN || state == SDL_POWERSTATE_NO_BATTERY) {
|
||||
return "e1a6"; // Battery Unknown
|
||||
}
|
||||
if (state == SDL_POWERSTATE_ERROR) {
|
||||
return "f7ea"; // Battery Error
|
||||
}
|
||||
if (state == SDL_POWERSTATE_CHARGED || level == 100) {
|
||||
return "e1a4"; // Battery Full
|
||||
}
|
||||
if (state == SDL_POWERSTATE_CHARGING) {
|
||||
if (level >= 90)
|
||||
return "f0a7"; // Battery Charging 90
|
||||
if (level >= 80)
|
||||
return "f0a6"; // Battery Charging 80
|
||||
if (level >= 60)
|
||||
return "f0a5"; // Battery Charging 60
|
||||
if (level >= 50)
|
||||
return "f0a4"; // Battery Charging 50
|
||||
if (level >= 30)
|
||||
return "f0a3"; // Battery Charging 30
|
||||
if (level >= 20)
|
||||
return "f0a2"; // Battery Charging 20
|
||||
return "e1a3"; // Battery Charging Full (we use it as empty)
|
||||
}
|
||||
if (level >= 85)
|
||||
return "ebd2"; // Battery 6 Bar
|
||||
if (level >= 70)
|
||||
return "ebd4"; // Battery 5 Bar
|
||||
if (level >= 55)
|
||||
return "ebe2"; // Battery 4 Bar
|
||||
if (level >= 40)
|
||||
return "ebdd"; // Battery 3 Bar
|
||||
if (level >= 25)
|
||||
return "ebe0"; // Battery 2 Bar
|
||||
if (level >= 10)
|
||||
return "ebd9"; // Battery 1 Bar
|
||||
return "e19c"; // Battery Alert
|
||||
}
|
||||
|
||||
const char* connection_state_icon(SDL_JoystickConnectionState state) noexcept {
|
||||
switch (state) {
|
||||
case SDL_JOYSTICK_CONNECTION_WIRELESS:
|
||||
return "e1a7";
|
||||
case SDL_JOYSTICK_CONNECTION_WIRED:
|
||||
return "e1e0";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_event(const SDL_Event& event) noexcept {
|
||||
if (event.type == SDL_EVENT_GAMEPAD_ADDED) {
|
||||
auto* gamepad = SDL_GetGamepadFromID(event.gdevice.which);
|
||||
if (SDL_GamepadConnected(gamepad)) {
|
||||
const char* name = SDL_GetGamepadName(gamepad);
|
||||
Rml::String content = fmt::format("<span>{}</span>", name ? name : "[Unknown]");
|
||||
Rml::String title = "Controller connected";
|
||||
if (const char* icon = connection_state_icon(SDL_GetGamepadConnectionState(gamepad))) {
|
||||
title = fmt::format(
|
||||
"<row><span>{}</span> <icon class=\"connection\">&#x{};</icon></row>", title,
|
||||
icon);
|
||||
}
|
||||
int batteryLevel = -1;
|
||||
const auto powerState = SDL_GetGamepadPowerInfo(gamepad, &batteryLevel);
|
||||
if (powerState != SDL_POWERSTATE_UNKNOWN) {
|
||||
content = fmt::format(
|
||||
"<row>{}</row><row class=\"muted\"><icon class=\"battery\">&#x{};</icon>",
|
||||
content, battery_icon(powerState, batteryLevel));
|
||||
if (batteryLevel > -1) {
|
||||
content = fmt::format("{} <span>{}%</span>", content, batteryLevel);
|
||||
}
|
||||
content += "</row>";
|
||||
}
|
||||
push_toast({
|
||||
.type = "controller",
|
||||
.title = title,
|
||||
.content = content,
|
||||
.duration = std::chrono::seconds(4),
|
||||
});
|
||||
sConnectedGamepads.insert(event.gdevice.which);
|
||||
}
|
||||
} else if (event.type == SDL_EVENT_GAMEPAD_REMOVED &&
|
||||
sConnectedGamepads.contains(event.gdevice.which))
|
||||
{
|
||||
const char* name = SDL_GetGamepadNameForID(event.gdevice.which);
|
||||
push_toast({
|
||||
.type = "controller",
|
||||
.title = "Controller disconnected",
|
||||
.content = name ? name : "[Unknown]",
|
||||
.duration = std::chrono::seconds(4),
|
||||
});
|
||||
sConnectedGamepads.erase(event.gdevice.which);
|
||||
}
|
||||
input::handle_event(event);
|
||||
}
|
||||
|
||||
Document& push_document(std::unique_ptr<Document> doc, bool show, bool passive) noexcept {
|
||||
Document& ret = *doc;
|
||||
if (passive) {
|
||||
@@ -66,7 +172,7 @@ Document& push_document(std::unique_ptr<Document> doc, bool show, bool passive)
|
||||
if (show) {
|
||||
ret.show();
|
||||
}
|
||||
sync_input_block();
|
||||
input::sync_input_block();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -74,7 +180,7 @@ void show_top_document() noexcept {
|
||||
if (auto* doc = top_document()) {
|
||||
doc->show();
|
||||
}
|
||||
sync_input_block();
|
||||
input::sync_input_block();
|
||||
}
|
||||
|
||||
bool any_document_visible() noexcept {
|
||||
@@ -99,7 +205,7 @@ Document* top_document() noexcept {
|
||||
}
|
||||
|
||||
void update() noexcept {
|
||||
update_input();
|
||||
input::update_input();
|
||||
for (const auto& doc : sDocumentStack) {
|
||||
doc->update();
|
||||
}
|
||||
@@ -131,7 +237,7 @@ void update() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
sync_input_block();
|
||||
input::sync_input_block();
|
||||
}
|
||||
|
||||
std::filesystem::path resource_path(const std::filesystem::path& filename) noexcept {
|
||||
|
||||
Reference in New Issue
Block a user