mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 23:27:49 +01:00
some refactoring 2
This commit is contained in:
@@ -4,6 +4,11 @@ project(cardboy_sdk LANGUAGES CXX)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
set(CMAKE_CXX_EXTENSIONS NO)
|
||||
# add_compile_options(-Werror -O0 -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-unused-variable
|
||||
# -Wno-error=unused-function
|
||||
# -Wshadow -Wformat=2 -Wfloat-equal -D_GLIBCXX_DEBUG -Wconversion)
|
||||
#add_compile_options(-fsanitize=address -fno-sanitize-recover -D_GLIBCXX_DEBUG)
|
||||
#add_link_options(-fsanitize=address -fno-sanitize-recover -D_GLIBCXX_DEBUG)
|
||||
|
||||
add_subdirectory(utils)
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@ public:
|
||||
const auto snap = captureTime();
|
||||
renderIfNeeded(snap);
|
||||
lastSnapshot = snap;
|
||||
refreshTimer = context.scheduleRepeatingTimer(200);
|
||||
if (auto* timer = context.timer())
|
||||
refreshTimer = timer->scheduleTimer(200, true);
|
||||
}
|
||||
|
||||
void onStop() override { cancelRefreshTimer(); }
|
||||
@@ -75,10 +76,11 @@ private:
|
||||
TimeSnapshot lastSnapshot{};
|
||||
|
||||
void cancelRefreshTimer() {
|
||||
if (refreshTimer != cardboy::sdk::kInvalidAppTimer) {
|
||||
context.cancelTimer(refreshTimer);
|
||||
refreshTimer = cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
if (refreshTimer == cardboy::sdk::kInvalidAppTimer)
|
||||
return;
|
||||
if (auto* timer = context.timer())
|
||||
timer->cancelTimer(refreshTimer);
|
||||
refreshTimer = cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
|
||||
void handleButtonEvent(const cardboy::sdk::AppButtonEvent& button) {
|
||||
|
||||
@@ -1156,15 +1156,17 @@ public:
|
||||
uint32_t stableFrames = 0;
|
||||
|
||||
void cancelTick() {
|
||||
if (tickTimer != kInvalidAppTimer) {
|
||||
context.cancelTimer(tickTimer);
|
||||
tickTimer = kInvalidAppTimer;
|
||||
}
|
||||
if (tickTimer == kInvalidAppTimer)
|
||||
return;
|
||||
if (auto* timer = context.timer())
|
||||
timer->cancelTimer(tickTimer);
|
||||
tickTimer = kInvalidAppTimer;
|
||||
}
|
||||
|
||||
void scheduleNextTick(uint32_t delayMs) {
|
||||
cancelTick();
|
||||
tickTimer = context.scheduleTimer(delayMs, false);
|
||||
if (auto* timer = context.timer())
|
||||
tickTimer = timer->scheduleTimer(delayMs, false);
|
||||
}
|
||||
|
||||
uint32_t idleDelayMs() const { return browserDirty ? 50 : 140; }
|
||||
|
||||
@@ -69,7 +69,8 @@ public:
|
||||
const auto snap = captureTime();
|
||||
renderIfNeeded(snap);
|
||||
lastSnapshot = snap;
|
||||
refreshTimer = context.scheduleRepeatingTimer(kRefreshIntervalMs);
|
||||
if (auto* timer = context.timer())
|
||||
refreshTimer = timer->scheduleTimer(kRefreshIntervalMs, true);
|
||||
}
|
||||
|
||||
void onStop() override { cancelRefreshTimer(); }
|
||||
@@ -104,10 +105,11 @@ private:
|
||||
std::uint32_t lastNotificationInteractionMs = 0;
|
||||
|
||||
void cancelRefreshTimer() {
|
||||
if (refreshTimer != cardboy::sdk::kInvalidAppTimer) {
|
||||
context.cancelTimer(refreshTimer);
|
||||
refreshTimer = cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
if (refreshTimer == cardboy::sdk::kInvalidAppTimer)
|
||||
return;
|
||||
if (auto* timer = context.timer())
|
||||
timer->cancelTimer(refreshTimer);
|
||||
refreshTimer = cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
|
||||
static bool comboPressed(const cardboy::sdk::InputState& state) { return state.a && state.select; }
|
||||
|
||||
@@ -189,15 +189,17 @@ private:
|
||||
}
|
||||
|
||||
void cancelInactivityTimer() {
|
||||
if (inactivityTimer != cardboy::sdk::kInvalidAppTimer) {
|
||||
context.cancelTimer(inactivityTimer);
|
||||
inactivityTimer = cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
if (inactivityTimer == cardboy::sdk::kInvalidAppTimer)
|
||||
return;
|
||||
if (auto* timer = context.timer())
|
||||
timer->cancelTimer(inactivityTimer);
|
||||
inactivityTimer = cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
|
||||
void resetInactivityTimer() {
|
||||
cancelInactivityTimer();
|
||||
inactivityTimer = context.scheduleTimer(kIdleTimeoutMs);
|
||||
if (auto* timer = context.timer())
|
||||
inactivityTimer = timer->scheduleTimer(kIdleTimeoutMs, false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -248,14 +248,16 @@ private:
|
||||
void scheduleMoveTimer() {
|
||||
cancelMoveTimer();
|
||||
const std::uint32_t interval = currentInterval();
|
||||
moveTimer = context.scheduleRepeatingTimer(interval);
|
||||
if (auto* timer = context.timer())
|
||||
moveTimer = timer->scheduleTimer(interval, true);
|
||||
}
|
||||
|
||||
void cancelMoveTimer() {
|
||||
if (moveTimer != cardboy::sdk::kInvalidAppTimer) {
|
||||
context.cancelTimer(moveTimer);
|
||||
moveTimer = cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
if (moveTimer == cardboy::sdk::kInvalidAppTimer)
|
||||
return;
|
||||
if (auto* timer = context.timer())
|
||||
timer->cancelTimer(moveTimer);
|
||||
moveTimer = cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint32_t currentInterval() const {
|
||||
|
||||
@@ -235,35 +235,40 @@ private:
|
||||
|
||||
void cancelTimers() {
|
||||
if (dropTimer != cardboy::sdk::kInvalidAppTimer) {
|
||||
context.cancelTimer(dropTimer);
|
||||
if (auto* timer = context.timer())
|
||||
timer->cancelTimer(dropTimer);
|
||||
dropTimer = cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
cancelSoftDropTimer();
|
||||
}
|
||||
|
||||
void cancelSoftDropTimer() {
|
||||
if (softTimer != cardboy::sdk::kInvalidAppTimer) {
|
||||
context.cancelTimer(softTimer);
|
||||
softTimer = cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
if (softTimer == cardboy::sdk::kInvalidAppTimer)
|
||||
return;
|
||||
if (auto* timer = context.timer())
|
||||
timer->cancelTimer(softTimer);
|
||||
softTimer = cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
|
||||
void scheduleDropTimer() {
|
||||
cancelDropTimer();
|
||||
const std::uint32_t interval = dropIntervalMs();
|
||||
dropTimer = context.scheduleRepeatingTimer(interval);
|
||||
if (auto* timer = context.timer())
|
||||
dropTimer = timer->scheduleTimer(interval, true);
|
||||
}
|
||||
|
||||
void cancelDropTimer() {
|
||||
if (dropTimer != cardboy::sdk::kInvalidAppTimer) {
|
||||
context.cancelTimer(dropTimer);
|
||||
dropTimer = cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
if (dropTimer == cardboy::sdk::kInvalidAppTimer)
|
||||
return;
|
||||
if (auto* timer = context.timer())
|
||||
timer->cancelTimer(dropTimer);
|
||||
dropTimer = cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
|
||||
void scheduleSoftDropTimer() {
|
||||
cancelSoftDropTimer();
|
||||
softTimer = context.scheduleRepeatingTimer(60);
|
||||
if (auto* timer = context.timer())
|
||||
softTimer = timer->scheduleTimer(60, true);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint32_t dropIntervalMs() const {
|
||||
|
||||
@@ -16,7 +16,6 @@ target_sources(cardboy_backend_interface
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/backend/backend_interface.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/backend.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/display_spec.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/event_bus.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/loop_hooks.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/input_state.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/platform.hpp
|
||||
|
||||
@@ -12,17 +12,13 @@ namespace cardboy::sdk {
|
||||
using AppTimerHandle = std::uint32_t;
|
||||
constexpr AppTimerHandle kInvalidAppTimer = 0;
|
||||
|
||||
using TimerClientId = std::uint32_t;
|
||||
constexpr TimerClientId kInvalidTimerClient = 0;
|
||||
|
||||
struct AppButtonEvent {
|
||||
InputState current{};
|
||||
InputState previous{};
|
||||
};
|
||||
|
||||
struct AppTimerEvent {
|
||||
TimerClientId clientId = kInvalidTimerClient;
|
||||
AppTimerHandle handle = kInvalidAppTimer;
|
||||
AppTimerHandle handle = kInvalidAppTimer;
|
||||
};
|
||||
|
||||
struct AppEvent {
|
||||
@@ -51,6 +47,8 @@ struct AppEvent {
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(std::is_trivially_copyable_v<AppEvent>);
|
||||
|
||||
template<typename... Ts>
|
||||
struct Overload : Ts... {
|
||||
using Ts::operator()...;
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "cardboy/sdk/app_events.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace cardboy::sdk {
|
||||
|
||||
enum class EventBusSignal : std::uint32_t {
|
||||
None = 0,
|
||||
Input = 1u << 0,
|
||||
Timer = 1u << 1,
|
||||
External = 1u << 2,
|
||||
};
|
||||
|
||||
inline EventBusSignal operator|(EventBusSignal lhs, EventBusSignal rhs) {
|
||||
return static_cast<EventBusSignal>(static_cast<std::uint32_t>(lhs) | static_cast<std::uint32_t>(rhs));
|
||||
}
|
||||
|
||||
inline EventBusSignal& operator|=(EventBusSignal& lhs, EventBusSignal rhs) {
|
||||
lhs = lhs | rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
inline EventBusSignal operator&(EventBusSignal lhs, EventBusSignal rhs) {
|
||||
return static_cast<EventBusSignal>(static_cast<std::uint32_t>(lhs) & static_cast<std::uint32_t>(rhs));
|
||||
}
|
||||
|
||||
inline std::uint32_t to_event_bits(EventBusSignal signal) { return static_cast<std::uint32_t>(signal); }
|
||||
|
||||
class IEventBus {
|
||||
public:
|
||||
static constexpr std::uint32_t kWaitForever = 0xFFFFFFFFu;
|
||||
|
||||
virtual ~IEventBus() = default;
|
||||
|
||||
virtual void signal(std::uint32_t bits) = 0;
|
||||
virtual void signalFromISR(std::uint32_t bits) = 0;
|
||||
virtual std::uint32_t wait(std::uint32_t mask, std::uint32_t timeout_ms) = 0;
|
||||
virtual void postEvent(const AppEvent& event) = 0;
|
||||
virtual bool popEvent(AppEvent& outEvent) = 0;
|
||||
};
|
||||
|
||||
} // namespace cardboy::sdk
|
||||
@@ -11,8 +11,8 @@ public:
|
||||
static void invokePreSend(void* framebuffer);
|
||||
|
||||
private:
|
||||
static PreSendHook hook_;
|
||||
static void* userData_;
|
||||
static PreSendHook _hook;
|
||||
static void* _userData;
|
||||
};
|
||||
|
||||
} // namespace cardboy::sdk
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "cardboy/sdk/event_bus.hpp"
|
||||
#include "cardboy/sdk/loop_hooks.hpp"
|
||||
#include "cardboy/sdk/timer_service.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
@@ -74,36 +74,56 @@ public:
|
||||
class INotificationCenter {
|
||||
public:
|
||||
struct Notification {
|
||||
std::uint64_t id = 0;
|
||||
std::uint64_t timestamp = 0;
|
||||
std::uint64_t id = 0;
|
||||
std::uint64_t timestamp = 0;
|
||||
std::uint64_t externalId = 0;
|
||||
std::string title;
|
||||
std::string body;
|
||||
bool unread = true;
|
||||
bool unread = true;
|
||||
};
|
||||
|
||||
virtual ~INotificationCenter() = default;
|
||||
|
||||
virtual void pushNotification(Notification notification) = 0;
|
||||
[[nodiscard]] virtual std::uint32_t revision() const = 0;
|
||||
[[nodiscard]] virtual std::vector<Notification> recent(std::size_t limit) const = 0;
|
||||
virtual void markAllRead() = 0;
|
||||
virtual void clear() = 0;
|
||||
virtual void removeById(std::uint64_t id) = 0;
|
||||
virtual void removeByExternalId(std::uint64_t externalId) = 0;
|
||||
virtual void pushNotification(Notification notification) = 0;
|
||||
[[nodiscard]] virtual std::uint32_t revision() const = 0;
|
||||
[[nodiscard]] virtual std::vector<Notification> recent(std::size_t limit) const = 0;
|
||||
virtual void markAllRead() = 0;
|
||||
virtual void clear() = 0;
|
||||
virtual void removeById(std::uint64_t id) = 0;
|
||||
virtual void removeByExternalId(std::uint64_t externalId) = 0;
|
||||
};
|
||||
|
||||
class IEventBus {
|
||||
public:
|
||||
virtual ~IEventBus() = default;
|
||||
|
||||
virtual void post(const AppEvent& event) = 0;
|
||||
virtual AppEvent pop() = 0;
|
||||
};
|
||||
|
||||
struct AppScopedServices {
|
||||
ITimerService* timer = nullptr;
|
||||
virtual ~AppScopedServices() = default;
|
||||
};
|
||||
|
||||
class IAppServiceProvider {
|
||||
public:
|
||||
virtual ~IAppServiceProvider() = default;
|
||||
|
||||
[[nodiscard]] virtual std::unique_ptr<AppScopedServices> createScopedServices(std::uint64_t generation) = 0;
|
||||
};
|
||||
|
||||
struct Services {
|
||||
IBuzzer* buzzer = nullptr;
|
||||
IBatteryMonitor* battery = nullptr;
|
||||
IStorage* storage = nullptr;
|
||||
IRandom* random = nullptr;
|
||||
IHighResClock* highResClock = nullptr;
|
||||
IFilesystem* filesystem = nullptr;
|
||||
IEventBus* eventBus = nullptr;
|
||||
ITimerService* timer = nullptr;
|
||||
ILoopHooks* loopHooks = nullptr;
|
||||
IBuzzer* buzzer = nullptr;
|
||||
IBatteryMonitor* battery = nullptr;
|
||||
IStorage* storage = nullptr;
|
||||
IRandom* random = nullptr;
|
||||
IHighResClock* highResClock = nullptr;
|
||||
IFilesystem* filesystem = nullptr;
|
||||
IEventBus* eventBus = nullptr;
|
||||
ILoopHooks* loopHooks = nullptr;
|
||||
INotificationCenter* notifications = nullptr;
|
||||
IAppServiceProvider* appServices = nullptr;
|
||||
};
|
||||
|
||||
} // namespace cardboy::sdk
|
||||
|
||||
@@ -10,12 +10,9 @@ class ITimerService {
|
||||
public:
|
||||
virtual ~ITimerService() = default;
|
||||
|
||||
virtual TimerClientId acquireClient() = 0;
|
||||
virtual void releaseClient(TimerClientId clientId) = 0;
|
||||
virtual AppTimerHandle scheduleTimer(TimerClientId clientId, std::uint32_t delay_ms, bool repeat) = 0;
|
||||
virtual void cancelTimer(TimerClientId clientId, AppTimerHandle handle) = 0;
|
||||
virtual void cancelAllTimers(TimerClientId clientId) = 0;
|
||||
virtual AppTimerHandle scheduleTimer(std::uint32_t delay_ms, bool repeat) = 0;
|
||||
virtual void cancelTimer(AppTimerHandle handle) = 0;
|
||||
virtual void cancelAllTimers() = 0;
|
||||
};
|
||||
|
||||
} // namespace cardboy::sdk
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "cardboy/sdk/event_bus.hpp"
|
||||
#include "cardboy/sdk/platform.hpp"
|
||||
#include "cardboy/sdk/services.hpp"
|
||||
|
||||
@@ -12,6 +11,7 @@
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <string>
|
||||
@@ -113,33 +113,39 @@ public:
|
||||
void signal(std::uint32_t bits) override;
|
||||
void signalFromISR(std::uint32_t bits) override;
|
||||
std::uint32_t wait(std::uint32_t mask, std::uint32_t timeout_ms) override;
|
||||
void postEvent(const cardboy::sdk::AppEvent& event) override;
|
||||
bool popEvent(cardboy::sdk::AppEvent& outEvent) override;
|
||||
|
||||
private:
|
||||
DesktopRuntime& runtime;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
std::uint32_t pendingBits = 0;
|
||||
std::mutex eventMutex;
|
||||
std::deque<cardboy::sdk::AppEvent> eventQueue;
|
||||
};
|
||||
|
||||
class DesktopScopedEventBus final : public cardboy::sdk::IAppEventBus {
|
||||
public:
|
||||
explicit DesktopScopedEventBus(cardboy::sdk::IEventBus& bus);
|
||||
|
||||
void post(const cardboy::sdk::AppEvent& event) override;
|
||||
bool pop(cardboy::sdk::AppEvent& outEvent) override;
|
||||
void clear() override;
|
||||
|
||||
private:
|
||||
cardboy::sdk::IEventBus& globalBus;
|
||||
std::mutex mutex;
|
||||
std::deque<cardboy::sdk::AppEvent> queue;
|
||||
};
|
||||
|
||||
class DesktopTimerService final : public cardboy::sdk::ITimerService {
|
||||
public:
|
||||
DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IEventBus& bus);
|
||||
DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IAppEventBus& appBus);
|
||||
~DesktopTimerService() override;
|
||||
|
||||
cardboy::sdk::TimerClientId acquireClient() override;
|
||||
void releaseClient(cardboy::sdk::TimerClientId clientId) override;
|
||||
cardboy::sdk::AppTimerHandle scheduleTimer(cardboy::sdk::TimerClientId clientId, std::uint32_t delay_ms,
|
||||
bool repeat) override;
|
||||
void cancelTimer(cardboy::sdk::TimerClientId clientId, cardboy::sdk::AppTimerHandle handle) override;
|
||||
void cancelAllTimers(cardboy::sdk::TimerClientId clientId) override;
|
||||
cardboy::sdk::AppTimerHandle scheduleTimer(std::uint32_t delay_ms, bool repeat) override;
|
||||
void cancelTimer(cardboy::sdk::AppTimerHandle handle) override;
|
||||
void cancelAllTimers() override;
|
||||
|
||||
private:
|
||||
struct TimerRecord {
|
||||
cardboy::sdk::TimerClientId clientId = cardboy::sdk::kInvalidTimerClient;
|
||||
cardboy::sdk::AppTimerHandle handle = cardboy::sdk::kInvalidAppTimer;
|
||||
std::chrono::steady_clock::time_point due;
|
||||
std::chrono::milliseconds interval{0};
|
||||
@@ -151,15 +157,30 @@ private:
|
||||
void wakeWorker();
|
||||
void cleanupInactive();
|
||||
|
||||
DesktopRuntime& runtime;
|
||||
cardboy::sdk::IEventBus& eventBus;
|
||||
DesktopRuntime& runtime;
|
||||
cardboy::sdk::IAppEventBus& appEventBus;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
std::vector<TimerRecord> timers;
|
||||
bool stopWorker = false;
|
||||
std::thread worker;
|
||||
cardboy::sdk::AppTimerHandle nextHandle = 1;
|
||||
cardboy::sdk::TimerClientId nextClient = 1;
|
||||
};
|
||||
|
||||
class DesktopAppServiceProvider final : public cardboy::sdk::IAppServiceProvider {
|
||||
public:
|
||||
DesktopAppServiceProvider(DesktopRuntime& owner, cardboy::sdk::IEventBus& bus);
|
||||
|
||||
[[nodiscard]] std::unique_ptr<cardboy::sdk::AppScopedServices> createScopedServices(std::uint64_t generation) override;
|
||||
|
||||
private:
|
||||
struct ScopedServices final : cardboy::sdk::AppScopedServices {
|
||||
std::unique_ptr<DesktopScopedEventBus> ownedEventBus;
|
||||
std::unique_ptr<DesktopTimerService> ownedTimer;
|
||||
};
|
||||
|
||||
DesktopRuntime& runtime;
|
||||
cardboy::sdk::IEventBus& eventBus;
|
||||
};
|
||||
|
||||
class DesktopFramebuffer final : public cardboy::sdk::FramebufferFacade<DesktopFramebuffer> {
|
||||
@@ -240,7 +261,7 @@ private:
|
||||
DesktopHighResClock highResService;
|
||||
DesktopFilesystem filesystemService;
|
||||
DesktopEventBus eventBusService;
|
||||
DesktopTimerService timerService;
|
||||
DesktopAppServiceProvider appServiceProvider;
|
||||
DesktopNotificationCenter notificationService;
|
||||
cardboy::sdk::Services services{};
|
||||
};
|
||||
|
||||
@@ -63,26 +63,34 @@ std::uint32_t DesktopEventBus::wait(std::uint32_t mask, std::uint32_t timeout_ms
|
||||
}
|
||||
}
|
||||
|
||||
void DesktopEventBus::postEvent(const cardboy::sdk::AppEvent& event) {
|
||||
DesktopScopedEventBus::DesktopScopedEventBus(cardboy::sdk::IEventBus& bus) : globalBus(bus) {}
|
||||
|
||||
void DesktopScopedEventBus::post(const cardboy::sdk::AppEvent& event) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(eventMutex);
|
||||
if (eventQueue.size() >= kDesktopEventQueueLimit)
|
||||
eventQueue.pop_front();
|
||||
eventQueue.push_back(event);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if (queue.size() >= kDesktopEventQueueLimit)
|
||||
queue.pop_front();
|
||||
queue.push_back(event);
|
||||
}
|
||||
signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Timer));
|
||||
globalBus.signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Timer));
|
||||
}
|
||||
|
||||
bool DesktopEventBus::popEvent(cardboy::sdk::AppEvent& outEvent) {
|
||||
std::lock_guard<std::mutex> lock(eventMutex);
|
||||
if (eventQueue.empty())
|
||||
bool DesktopScopedEventBus::pop(cardboy::sdk::AppEvent& outEvent) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if (queue.empty())
|
||||
return false;
|
||||
outEvent = eventQueue.front();
|
||||
eventQueue.pop_front();
|
||||
outEvent = queue.front();
|
||||
queue.pop_front();
|
||||
return true;
|
||||
}
|
||||
|
||||
DesktopTimerService::DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IEventBus& bus) : runtime(owner), eventBus(bus) {
|
||||
void DesktopScopedEventBus::clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
queue.clear();
|
||||
}
|
||||
|
||||
DesktopTimerService::DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IAppEventBus& appBus) :
|
||||
runtime(owner), appEventBus(appBus) {
|
||||
worker = std::thread(&DesktopTimerService::workerLoop, this);
|
||||
}
|
||||
|
||||
@@ -94,33 +102,17 @@ DesktopTimerService::~DesktopTimerService() {
|
||||
cv.notify_all();
|
||||
if (worker.joinable())
|
||||
worker.join();
|
||||
cancelAllTimers();
|
||||
}
|
||||
|
||||
cardboy::sdk::TimerClientId DesktopTimerService::acquireClient() {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
cardboy::sdk::TimerClientId id = cardboy::sdk::kInvalidTimerClient;
|
||||
do {
|
||||
id = nextClient++;
|
||||
} while (id == cardboy::sdk::kInvalidTimerClient);
|
||||
if (nextClient == cardboy::sdk::kInvalidTimerClient)
|
||||
++nextClient;
|
||||
return id;
|
||||
}
|
||||
|
||||
void DesktopTimerService::releaseClient(cardboy::sdk::TimerClientId clientId) { cancelAllTimers(clientId); }
|
||||
|
||||
cardboy::sdk::AppTimerHandle DesktopTimerService::scheduleTimer(cardboy::sdk::TimerClientId clientId,
|
||||
std::uint32_t delay_ms, bool repeat) {
|
||||
if (clientId == cardboy::sdk::kInvalidTimerClient)
|
||||
return cardboy::sdk::kInvalidAppTimer;
|
||||
|
||||
cardboy::sdk::AppTimerHandle DesktopTimerService::scheduleTimer(std::uint32_t delay_ms, bool repeat) {
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto effectiveDelayMs = std::chrono::milliseconds(delay_ms);
|
||||
const auto dueTime = delay_ms == 0 ? now : now + effectiveDelayMs;
|
||||
const auto interval = std::chrono::milliseconds(std::max<std::uint32_t>(1, repeat ? delay_ms : std::max(delay_ms, 1u)));
|
||||
const auto interval = std::chrono::milliseconds(std::max<std::uint32_t>(1, repeat ? std::max(delay_ms, 1u)
|
||||
: std::max(delay_ms, 1u)));
|
||||
|
||||
TimerRecord record{};
|
||||
record.clientId = clientId;
|
||||
record.repeat = repeat;
|
||||
record.interval = interval;
|
||||
record.due = dueTime;
|
||||
@@ -141,25 +133,22 @@ cardboy::sdk::AppTimerHandle DesktopTimerService::scheduleTimer(cardboy::sdk::Ti
|
||||
}
|
||||
}
|
||||
|
||||
void DesktopTimerService::cancelTimer(cardboy::sdk::TimerClientId clientId, cardboy::sdk::AppTimerHandle handle) {
|
||||
if (clientId == cardboy::sdk::kInvalidTimerClient || handle == cardboy::sdk::kInvalidAppTimer)
|
||||
void DesktopTimerService::cancelTimer(cardboy::sdk::AppTimerHandle handle) {
|
||||
if (handle == cardboy::sdk::kInvalidAppTimer)
|
||||
return;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (auto& record: timers) {
|
||||
if (record.clientId == clientId && record.handle == handle)
|
||||
if (record.handle == handle)
|
||||
record.active = false;
|
||||
}
|
||||
cleanupInactive();
|
||||
wakeWorker();
|
||||
}
|
||||
|
||||
void DesktopTimerService::cancelAllTimers(cardboy::sdk::TimerClientId clientId) {
|
||||
if (clientId == cardboy::sdk::kInvalidTimerClient)
|
||||
return;
|
||||
void DesktopTimerService::cancelAllTimers() {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (auto& record: timers) {
|
||||
if (record.clientId == clientId)
|
||||
record.active = false;
|
||||
record.active = false;
|
||||
}
|
||||
cleanupInactive();
|
||||
wakeWorker();
|
||||
@@ -195,13 +184,12 @@ void DesktopTimerService::workerLoop() {
|
||||
lock.unlock();
|
||||
|
||||
cardboy::sdk::AppTimerEvent timerEvent{};
|
||||
timerEvent.clientId = record.clientId;
|
||||
timerEvent.handle = record.handle;
|
||||
timerEvent.handle = record.handle;
|
||||
|
||||
cardboy::sdk::AppEvent event{};
|
||||
event.timestamp_ms = runtime.clock.millis();
|
||||
event.data = timerEvent;
|
||||
eventBus.postEvent(event);
|
||||
appEventBus.post(event);
|
||||
|
||||
lock.lock();
|
||||
continue;
|
||||
@@ -218,6 +206,19 @@ void DesktopTimerService::cleanupInactive() {
|
||||
timers.end());
|
||||
}
|
||||
|
||||
DesktopAppServiceProvider::DesktopAppServiceProvider(DesktopRuntime& owner, cardboy::sdk::IEventBus& bus) :
|
||||
runtime(owner), eventBus(bus) {}
|
||||
|
||||
std::unique_ptr<cardboy::sdk::AppScopedServices> DesktopAppServiceProvider::createScopedServices(std::uint64_t generation) {
|
||||
(void)generation;
|
||||
auto scoped = std::make_unique<ScopedServices>();
|
||||
scoped->ownedEventBus = std::make_unique<DesktopScopedEventBus>(eventBus);
|
||||
scoped->events = scoped->ownedEventBus.get();
|
||||
scoped->ownedTimer = std::make_unique<DesktopTimerService>(runtime, *scoped->ownedEventBus);
|
||||
scoped->timer = scoped->ownedTimer.get();
|
||||
return scoped;
|
||||
}
|
||||
|
||||
bool DesktopStorage::readUint32(std::string_view ns, std::string_view key, std::uint32_t& out) {
|
||||
auto it = data.find(composeKey(ns, key));
|
||||
if (it == data.end())
|
||||
@@ -441,7 +442,7 @@ DesktopRuntime::DesktopRuntime() :
|
||||
"Cardboy Desktop"),
|
||||
texture(), sprite(texture),
|
||||
pixels(static_cast<std::size_t>(cardboy::sdk::kDisplayWidth * cardboy::sdk::kDisplayHeight) * 4, 0),
|
||||
framebuffer(*this), input(*this), clock(*this), eventBusService(*this), timerService(*this, eventBusService) {
|
||||
framebuffer(*this), input(*this), clock(*this), eventBusService(*this), appServiceProvider(*this, eventBusService) {
|
||||
window.setFramerateLimit(60);
|
||||
if (!texture.resize(sf::Vector2u{cardboy::sdk::kDisplayWidth, cardboy::sdk::kDisplayHeight}))
|
||||
throw std::runtime_error("Failed to allocate texture for desktop framebuffer");
|
||||
@@ -457,7 +458,7 @@ DesktopRuntime::DesktopRuntime() :
|
||||
services.highResClock = &highResService;
|
||||
services.filesystem = &filesystemService;
|
||||
services.eventBus = &eventBusService;
|
||||
services.timer = &timerService;
|
||||
services.appServices = &appServiceProvider;
|
||||
services.loopHooks = nullptr;
|
||||
services.notifications = ¬ificationService;
|
||||
}
|
||||
|
||||
@@ -33,66 +33,44 @@ struct AppContext {
|
||||
|
||||
[[nodiscard]] Services* getServices() const { return services; }
|
||||
|
||||
[[nodiscard]] IBuzzer* buzzer() const { return services ? services->buzzer : nullptr; }
|
||||
[[nodiscard]] IBatteryMonitor* battery() const { return services ? services->battery : nullptr; }
|
||||
[[nodiscard]] IStorage* storage() const { return services ? services->storage : nullptr; }
|
||||
[[nodiscard]] IRandom* random() const { return services ? services->random : nullptr; }
|
||||
[[nodiscard]] IHighResClock* highResClock() const { return services ? services->highResClock : nullptr; }
|
||||
[[nodiscard]] IFilesystem* filesystem() const { return services ? services->filesystem : nullptr; }
|
||||
[[nodiscard]] IEventBus* eventBus() const { return services ? services->eventBus : nullptr; }
|
||||
[[nodiscard]] ITimerService* timerService() const { return services ? services->timer : nullptr; }
|
||||
[[nodiscard]] ILoopHooks* loopHooks() const { return services ? services->loopHooks : nullptr; }
|
||||
[[nodiscard]] IBuzzer* buzzer() const { return services ? services->buzzer : nullptr; }
|
||||
[[nodiscard]] IBatteryMonitor* battery() const { return services ? services->battery : nullptr; }
|
||||
[[nodiscard]] IStorage* storage() const { return services ? services->storage : nullptr; }
|
||||
[[nodiscard]] IRandom* random() const { return services ? services->random : nullptr; }
|
||||
[[nodiscard]] IHighResClock* highResClock() const { return services ? services->highResClock : nullptr; }
|
||||
[[nodiscard]] IFilesystem* filesystem() const { return services ? services->filesystem : nullptr; }
|
||||
[[nodiscard]] AppScopedServices* appServices() const { return _scopedServices; }
|
||||
[[nodiscard]] IEventBus* eventBus() const { return services ? services->eventBus : nullptr; }
|
||||
[[nodiscard]] ITimerService* timer() const { return _scopedServices ? _scopedServices->timer : nullptr; }
|
||||
[[nodiscard]] ILoopHooks* loopHooks() const { return services ? services->loopHooks : nullptr; }
|
||||
[[nodiscard]] INotificationCenter* notificationCenter() const {
|
||||
return services ? services->notifications : nullptr;
|
||||
}
|
||||
|
||||
void requestAppSwitchByIndex(std::size_t index) {
|
||||
pendingAppIndex = index;
|
||||
pendingAppName.clear();
|
||||
pendingSwitchByName = false;
|
||||
pendingSwitch = true;
|
||||
_pendingAppIndex = index;
|
||||
_pendingAppName.clear();
|
||||
_pendingSwitchByName = false;
|
||||
_pendingSwitch = true;
|
||||
}
|
||||
|
||||
void requestAppSwitchByName(std::string_view name) {
|
||||
pendingAppName.assign(name.begin(), name.end());
|
||||
pendingSwitchByName = true;
|
||||
pendingSwitch = true;
|
||||
_pendingAppName.assign(name.begin(), name.end());
|
||||
_pendingSwitchByName = true;
|
||||
_pendingSwitch = true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool hasPendingAppSwitch() const { return pendingSwitch; }
|
||||
|
||||
AppTimerHandle scheduleTimer(std::uint32_t delay_ms, bool repeat = false) {
|
||||
auto* timer = timerService();
|
||||
if (!timer || timerClientId == kInvalidTimerClient)
|
||||
return kInvalidAppTimer;
|
||||
return timer->scheduleTimer(timerClientId, delay_ms, repeat);
|
||||
}
|
||||
|
||||
AppTimerHandle scheduleRepeatingTimer(std::uint32_t interval_ms) {
|
||||
return scheduleTimer(interval_ms, true);
|
||||
}
|
||||
|
||||
void cancelTimer(AppTimerHandle handle) {
|
||||
auto* timer = timerService();
|
||||
if (!timer || timerClientId == kInvalidTimerClient)
|
||||
return;
|
||||
timer->cancelTimer(timerClientId, handle);
|
||||
}
|
||||
|
||||
void cancelAllTimers() {
|
||||
auto* timer = timerService();
|
||||
if (!timer || timerClientId == kInvalidTimerClient)
|
||||
return;
|
||||
timer->cancelAllTimers(timerClientId);
|
||||
}
|
||||
[[nodiscard]] bool hasPendingAppSwitch() const { return _pendingSwitch; }
|
||||
|
||||
private:
|
||||
friend class AppSystem;
|
||||
bool pendingSwitch = false;
|
||||
bool pendingSwitchByName = false;
|
||||
std::size_t pendingAppIndex = 0;
|
||||
std::string pendingAppName;
|
||||
TimerClientId timerClientId = kInvalidTimerClient;
|
||||
bool _pendingSwitch = false;
|
||||
bool _pendingSwitchByName = false;
|
||||
std::size_t _pendingAppIndex = 0;
|
||||
std::string _pendingAppName;
|
||||
AppScopedServices* _scopedServices = nullptr;
|
||||
|
||||
void setScopedServices(AppScopedServices* services) { _scopedServices = services; }
|
||||
};
|
||||
|
||||
class IApp {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <cardboy/sdk/event_bus.hpp>
|
||||
#include "app_framework.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
@@ -16,36 +15,30 @@ public:
|
||||
~AppSystem();
|
||||
|
||||
void registerApp(std::unique_ptr<IAppFactory> factory);
|
||||
bool startApp(const std::string& name);
|
||||
bool startAppByIndex(std::size_t index);
|
||||
void startApp(const std::string& name);
|
||||
void startAppByIndex(std::size_t index);
|
||||
|
||||
void run();
|
||||
|
||||
[[nodiscard]] std::size_t appCount() const { return factories.size(); }
|
||||
[[nodiscard]] std::size_t appCount() const { return _factories.size(); }
|
||||
[[nodiscard]] const IAppFactory* factoryAt(std::size_t index) const;
|
||||
[[nodiscard]] std::size_t indexOfFactory(const IAppFactory* factory) const;
|
||||
[[nodiscard]] std::size_t currentFactoryIndex() const { return activeIndex; }
|
||||
[[nodiscard]] std::size_t currentFactoryIndex() const { return _activeIndex; }
|
||||
|
||||
[[nodiscard]] const IApp* currentApp() const { return current.get(); }
|
||||
[[nodiscard]] const IAppFactory* currentFactory() const { return activeFactory; }
|
||||
[[nodiscard]] const IApp* currentApp() const { return _current.get(); }
|
||||
[[nodiscard]] const IAppFactory* currentFactory() const { return _activeFactory; }
|
||||
|
||||
private:
|
||||
friend struct AppContext;
|
||||
void handlePendingSwitchRequest();
|
||||
std::unique_ptr<AppScopedServices> createAppScopedServices(std::uint64_t generation);
|
||||
|
||||
void dispatchEvent(const AppEvent& event);
|
||||
bool handlePendingSwitchRequest();
|
||||
void attachTimerClient();
|
||||
void detachTimerClient();
|
||||
void processEventBusEvents();
|
||||
|
||||
AppContext context;
|
||||
std::vector<std::unique_ptr<IAppFactory>> factories;
|
||||
std::unique_ptr<IApp> current;
|
||||
IAppFactory* activeFactory = nullptr;
|
||||
std::size_t activeIndex = static_cast<std::size_t>(-1);
|
||||
TimerClientId timerClientId = kInvalidTimerClient;
|
||||
InputState lastInputState{};
|
||||
bool suppressInputs = false;
|
||||
AppContext _context;
|
||||
std::vector<std::unique_ptr<IAppFactory>> _factories;
|
||||
std::unique_ptr<IApp> _current;
|
||||
IAppFactory* _activeFactory = nullptr;
|
||||
std::size_t _activeIndex = static_cast<std::size_t>(-1);
|
||||
std::unique_ptr<AppScopedServices> _scopedServices;
|
||||
std::uint64_t _nextScopedGeneration = 1;
|
||||
};
|
||||
|
||||
} // namespace cardboy::sdk
|
||||
|
||||
@@ -13,8 +13,8 @@ public:
|
||||
static void invokePreSend(void* framebuffer);
|
||||
|
||||
private:
|
||||
static PreSendHook hook_;
|
||||
static void* userData_;
|
||||
static PreSendHook _hook;
|
||||
static void* _userData;
|
||||
};
|
||||
|
||||
} // namespace cardboy::sdk
|
||||
|
||||
@@ -17,11 +17,11 @@ class StatusBar {
|
||||
public:
|
||||
static StatusBar& instance();
|
||||
|
||||
void setServices(Services* services) { services_ = services; }
|
||||
void setServices(Services* services) { _services = services; }
|
||||
|
||||
void setEnabled(bool value);
|
||||
void toggle();
|
||||
[[nodiscard]] bool isEnabled() const { return enabled_; }
|
||||
[[nodiscard]] bool isEnabled() const { return _enabled; }
|
||||
|
||||
void setCurrentAppName(std::string_view name);
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
|
||||
template<typename Framebuffer>
|
||||
void renderIfEnabled(Framebuffer& fb) {
|
||||
if (!enabled_)
|
||||
if (!_enabled)
|
||||
return;
|
||||
renderBar(fb);
|
||||
}
|
||||
@@ -84,9 +84,9 @@ private:
|
||||
[[nodiscard]] std::string prepareLeftText(int displayWidth) const;
|
||||
[[nodiscard]] std::string prepareRightText() const;
|
||||
|
||||
bool enabled_ = false;
|
||||
Services* services_ = nullptr;
|
||||
std::string appName_{};
|
||||
bool _enabled = false;
|
||||
Services* _services = nullptr;
|
||||
std::string _appName{};
|
||||
};
|
||||
|
||||
} // namespace cardboy::sdk
|
||||
|
||||
@@ -7,14 +7,6 @@
|
||||
|
||||
namespace cardboy::sdk {
|
||||
namespace {
|
||||
[[nodiscard]] bool inputsDiffer(const InputState& a, const InputState& b) {
|
||||
return a.up != b.up || a.down != b.down || a.left != b.left || a.right != b.right || a.a != b.a || a.b != b.b ||
|
||||
a.select != b.select || a.start != b.start;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool anyButtonPressed(const InputState& state) {
|
||||
return state.up || state.down || state.left || state.right || state.a || state.b || state.select || state.start;
|
||||
}
|
||||
|
||||
template<typename Framebuffer>
|
||||
void statusBarPreSendHook(void* framebuffer, void* userData) {
|
||||
@@ -25,191 +17,120 @@ void statusBarPreSendHook(void* framebuffer, void* userData) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
AppSystem::AppSystem(AppContext ctx) : context(std::move(ctx)) {
|
||||
context.system = this;
|
||||
AppSystem::AppSystem(AppContext ctx) : _context(std::move(ctx)) {
|
||||
_context.system = this;
|
||||
auto& statusBar = StatusBar::instance();
|
||||
statusBar.setServices(context.services);
|
||||
using FBType = typename AppContext::Framebuffer;
|
||||
FramebufferHooks::setPreSendHook(&statusBarPreSendHook<FBType>, &statusBar);
|
||||
statusBar.setServices(_context.services);
|
||||
FramebufferHooks::setPreSendHook(&statusBarPreSendHook<AppContext::Framebuffer>, &statusBar);
|
||||
}
|
||||
|
||||
AppSystem::~AppSystem() {
|
||||
detachTimerClient();
|
||||
FramebufferHooks::clearPreSendHook();
|
||||
}
|
||||
AppSystem::~AppSystem() { FramebufferHooks::clearPreSendHook(); }
|
||||
|
||||
void AppSystem::registerApp(std::unique_ptr<IAppFactory> factory) {
|
||||
if (!factory)
|
||||
return;
|
||||
factories.emplace_back(std::move(factory));
|
||||
assert(factory);
|
||||
_factories.emplace_back(std::move(factory));
|
||||
}
|
||||
|
||||
bool AppSystem::startApp(const std::string& name) {
|
||||
for (std::size_t i = 0; i < factories.size(); ++i) {
|
||||
if (factories[i]->name() == name)
|
||||
return startAppByIndex(i);
|
||||
void AppSystem::startApp(const std::string& name) {
|
||||
for (std::size_t i = 0; i < _factories.size(); ++i) {
|
||||
if (_factories[i]->name() == name)
|
||||
startAppByIndex(i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AppSystem::startAppByIndex(std::size_t index) {
|
||||
if (index >= factories.size())
|
||||
return false;
|
||||
void AppSystem::startAppByIndex(std::size_t index) {
|
||||
assert(index < _factories.size());
|
||||
|
||||
context.system = this;
|
||||
auto& factory = factories[index];
|
||||
auto app = factory->create(context);
|
||||
if (!app)
|
||||
return false;
|
||||
|
||||
if (current) {
|
||||
current->onStop();
|
||||
current.reset();
|
||||
detachTimerClient();
|
||||
if (_current) {
|
||||
_current->onStop();
|
||||
_current.reset();
|
||||
}
|
||||
|
||||
activeFactory = factory.get();
|
||||
activeIndex = index;
|
||||
context.pendingSwitch = false;
|
||||
context.pendingSwitchByName = false;
|
||||
context.pendingAppName.clear();
|
||||
current = std::move(app);
|
||||
attachTimerClient();
|
||||
lastInputState = context.input.readState();
|
||||
suppressInputs = true;
|
||||
StatusBar::instance().setServices(context.services);
|
||||
StatusBar::instance().setCurrentAppName(activeFactory ? activeFactory->name() : "");
|
||||
current->onStart();
|
||||
return true;
|
||||
_context.system = this;
|
||||
|
||||
auto& factory = _factories[index];
|
||||
|
||||
const std::uint64_t newGeneration = _nextScopedGeneration++;
|
||||
|
||||
auto app = factory->create(_context);
|
||||
assert(app);
|
||||
|
||||
_scopedServices.reset();
|
||||
auto scoped = createAppScopedServices(newGeneration);
|
||||
_scopedServices = std::move(scoped);
|
||||
_context.setScopedServices(_scopedServices.get());
|
||||
|
||||
_activeFactory = factory.get();
|
||||
_activeIndex = index;
|
||||
_context._pendingSwitch = false;
|
||||
_context._pendingSwitchByName = false;
|
||||
_context._pendingAppName.clear();
|
||||
_current = std::move(app);
|
||||
StatusBar::instance().setServices(_context.services);
|
||||
StatusBar::instance().setCurrentAppName(_activeFactory ? _activeFactory->name() : "");
|
||||
_current->onStart();
|
||||
}
|
||||
|
||||
void AppSystem::run() {
|
||||
if (!current) {
|
||||
if (factories.empty() || !startAppByIndex(0))
|
||||
return;
|
||||
if (!_current) {
|
||||
assert(!_factories.empty());
|
||||
startAppByIndex(0);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
auto* eventBus = context.eventBus();
|
||||
if (!eventBus)
|
||||
return;
|
||||
|
||||
if (auto* hooks = context.loopHooks())
|
||||
if (auto* hooks = _context.loopHooks())
|
||||
hooks->onLoopIteration();
|
||||
|
||||
processEventBusEvents();
|
||||
if (handlePendingSwitchRequest())
|
||||
continue;
|
||||
|
||||
const std::uint32_t now = context.clock.millis();
|
||||
const InputState inputNow = context.input.readState();
|
||||
const bool consumedByStatusToggle = StatusBar::instance().handleToggleInput(inputNow, lastInputState);
|
||||
|
||||
if (suppressInputs) {
|
||||
lastInputState = inputNow;
|
||||
if (!anyButtonPressed(inputNow))
|
||||
suppressInputs = false;
|
||||
} else if (!consumedByStatusToggle && inputsDiffer(inputNow, lastInputState)) {
|
||||
AppButtonEvent button{};
|
||||
button.current = inputNow;
|
||||
button.previous = lastInputState;
|
||||
|
||||
AppEvent evt{};
|
||||
evt.timestamp_ms = now;
|
||||
evt.data = button;
|
||||
|
||||
lastInputState = inputNow;
|
||||
dispatchEvent(evt);
|
||||
} else if (consumedByStatusToggle) {
|
||||
lastInputState = inputNow;
|
||||
auto event = _context.eventBus()->pop();
|
||||
if (const auto* btn = event.button()) {
|
||||
const bool consumedByStatusToggle = StatusBar::instance().handleToggleInput(btn->current, btn->previous);
|
||||
if (consumedByStatusToggle) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (handlePendingSwitchRequest())
|
||||
_current->handleEvent(event);
|
||||
if (_context._pendingSwitch) {
|
||||
handlePendingSwitchRequest();
|
||||
continue;
|
||||
|
||||
const std::uint32_t mask = to_event_bits(EventBusSignal::Input) | to_event_bits(EventBusSignal::Timer);
|
||||
eventBus->wait(mask, IEventBus::kWaitForever);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const IAppFactory* AppSystem::factoryAt(std::size_t index) const {
|
||||
if (index >= factories.size())
|
||||
if (index >= _factories.size())
|
||||
return nullptr;
|
||||
return factories[index].get();
|
||||
return _factories[index].get();
|
||||
}
|
||||
|
||||
std::size_t AppSystem::indexOfFactory(const IAppFactory* factory) const {
|
||||
if (!factory)
|
||||
return static_cast<std::size_t>(-1);
|
||||
for (std::size_t i = 0; i < factories.size(); ++i) {
|
||||
if (factories[i].get() == factory)
|
||||
for (std::size_t i = 0; i < _factories.size(); ++i) {
|
||||
if (_factories[i].get() == factory)
|
||||
return i;
|
||||
}
|
||||
return static_cast<std::size_t>(-1);
|
||||
}
|
||||
|
||||
void AppSystem::dispatchEvent(const AppEvent& event) {
|
||||
if (!current)
|
||||
return;
|
||||
|
||||
if (const auto* timer = event.timer()) {
|
||||
if (timer->clientId != timerClientId)
|
||||
return;
|
||||
}
|
||||
|
||||
current->handleEvent(event);
|
||||
}
|
||||
|
||||
bool AppSystem::handlePendingSwitchRequest() {
|
||||
if (!context.pendingSwitch)
|
||||
return false;
|
||||
const bool byName = context.pendingSwitchByName;
|
||||
const std::size_t reqIndex = context.pendingAppIndex;
|
||||
const std::string reqName = context.pendingAppName;
|
||||
context.pendingSwitch = false;
|
||||
context.pendingSwitchByName = false;
|
||||
context.pendingAppName.clear();
|
||||
void AppSystem::handlePendingSwitchRequest() {
|
||||
assert(_context._pendingSwitch);
|
||||
const bool byName = _context._pendingSwitchByName;
|
||||
const std::size_t reqIndex = _context._pendingAppIndex;
|
||||
const std::string reqName = _context._pendingAppName;
|
||||
_context._pendingSwitch = false;
|
||||
_context._pendingSwitchByName = false;
|
||||
_context._pendingAppName.clear();
|
||||
bool switched = false;
|
||||
if (byName)
|
||||
switched = startApp(reqName);
|
||||
startApp(reqName);
|
||||
else
|
||||
switched = startAppByIndex(reqIndex);
|
||||
return switched;
|
||||
startAppByIndex(reqIndex);
|
||||
}
|
||||
|
||||
void AppSystem::attachTimerClient() {
|
||||
auto* timer = context.timerService();
|
||||
if (!timer) {
|
||||
timerClientId = kInvalidTimerClient;
|
||||
context.timerClientId = kInvalidTimerClient;
|
||||
return;
|
||||
}
|
||||
|
||||
timerClientId = timer->acquireClient();
|
||||
context.timerClientId = timerClientId;
|
||||
}
|
||||
|
||||
void AppSystem::detachTimerClient() {
|
||||
auto* timer = context.timerService();
|
||||
if (!timer || timerClientId == kInvalidTimerClient)
|
||||
return;
|
||||
|
||||
timer->releaseClient(timerClientId);
|
||||
timerClientId = kInvalidTimerClient;
|
||||
context.timerClientId = kInvalidTimerClient;
|
||||
}
|
||||
|
||||
void AppSystem::processEventBusEvents() {
|
||||
auto* eventBus = context.eventBus();
|
||||
if (!eventBus)
|
||||
return;
|
||||
|
||||
AppEvent event{};
|
||||
while (eventBus->popEvent(event)) {
|
||||
dispatchEvent(event);
|
||||
if (context.pendingSwitch)
|
||||
break;
|
||||
}
|
||||
std::unique_ptr<AppScopedServices> AppSystem::createAppScopedServices(std::uint64_t generation) {
|
||||
if (!_context.services || !_context.services->appServices)
|
||||
return nullptr;
|
||||
return _context.services->appServices->createScopedServices(generation);
|
||||
}
|
||||
|
||||
} // namespace cardboy::sdk
|
||||
|
||||
@@ -2,22 +2,22 @@
|
||||
|
||||
namespace cardboy::sdk {
|
||||
|
||||
FramebufferHooks::PreSendHook FramebufferHooks::hook_ = nullptr;
|
||||
void* FramebufferHooks::userData_ = nullptr;
|
||||
FramebufferHooks::PreSendHook FramebufferHooks::_hook = nullptr;
|
||||
void* FramebufferHooks::_userData = nullptr;
|
||||
|
||||
void FramebufferHooks::setPreSendHook(PreSendHook hook, void* userData) {
|
||||
hook_ = hook;
|
||||
userData_ = userData;
|
||||
_hook = hook;
|
||||
_userData = userData;
|
||||
}
|
||||
|
||||
void FramebufferHooks::clearPreSendHook() {
|
||||
hook_ = nullptr;
|
||||
userData_ = nullptr;
|
||||
_hook = nullptr;
|
||||
_userData = nullptr;
|
||||
}
|
||||
|
||||
void FramebufferHooks::invokePreSend(void* framebuffer) {
|
||||
if (hook_)
|
||||
hook_(framebuffer, userData_);
|
||||
if (_hook)
|
||||
_hook(framebuffer, _userData);
|
||||
}
|
||||
|
||||
} // namespace cardboy::sdk
|
||||
|
||||
@@ -12,17 +12,17 @@ StatusBar& StatusBar::instance() {
|
||||
return bar;
|
||||
}
|
||||
|
||||
void StatusBar::setEnabled(bool value) { enabled_ = value; }
|
||||
void StatusBar::setEnabled(bool value) { _enabled = value; }
|
||||
|
||||
void StatusBar::toggle() {
|
||||
enabled_ = !enabled_;
|
||||
if (services_ && services_->buzzer)
|
||||
services_->buzzer->beepMove();
|
||||
_enabled = !_enabled;
|
||||
if (_services && _services->buzzer)
|
||||
_services->buzzer->beepMove();
|
||||
}
|
||||
|
||||
void StatusBar::setCurrentAppName(std::string_view name) {
|
||||
appName_.assign(name.begin(), name.end());
|
||||
std::transform(appName_.begin(), appName_.end(), appName_.begin(),
|
||||
_appName.assign(name.begin(), name.end());
|
||||
std::transform(_appName.begin(), _appName.end(), _appName.begin(),
|
||||
[](unsigned char ch) { return static_cast<char>(std::toupper(ch)); });
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ bool StatusBar::handleToggleInput(const InputState& current, const InputState& p
|
||||
}
|
||||
|
||||
std::string StatusBar::prepareLeftText(int displayWidth) const {
|
||||
std::string text = appName_.empty() ? std::string("CARDBOY") : appName_;
|
||||
std::string text = _appName.empty() ? std::string("CARDBOY") : _appName;
|
||||
int maxWidth = std::max(0, displayWidth - 32);
|
||||
while (!text.empty() && font16x8::measureText(text, 1, 1) > maxWidth)
|
||||
text.pop_back();
|
||||
@@ -45,14 +45,14 @@ std::string StatusBar::prepareLeftText(int displayWidth) const {
|
||||
}
|
||||
|
||||
std::string StatusBar::prepareRightText() const {
|
||||
if (!services_)
|
||||
if (!_services)
|
||||
return {};
|
||||
|
||||
std::string right;
|
||||
if (services_->battery && services_->battery->hasData()) {
|
||||
const float current = services_->battery->current();
|
||||
const float chargeMah = services_->battery->charge();
|
||||
const float fallbackV = services_->battery->voltage();
|
||||
if (_services->battery && _services->battery->hasData()) {
|
||||
const float current = _services->battery->current();
|
||||
const float chargeMah = _services->battery->charge();
|
||||
const float fallbackV = _services->battery->voltage();
|
||||
char buf[64];
|
||||
if (std::isfinite(current) && std::isfinite(chargeMah)) {
|
||||
std::snprintf(buf, sizeof(buf), "cur %.2fmA chr %.2fmAh", static_cast<double>(current),
|
||||
@@ -63,7 +63,7 @@ std::string StatusBar::prepareRightText() const {
|
||||
right.assign(buf);
|
||||
}
|
||||
|
||||
if (services_->buzzer && services_->buzzer->isMuted()) {
|
||||
if (_services->buzzer && _services->buzzer->isMuted()) {
|
||||
if (!right.empty())
|
||||
right.append(" ");
|
||||
right.append("MUTE");
|
||||
|
||||
Reference in New Issue
Block a user