#pragma once #include #include #include #include #include #include "playback.h" #include "dbus.hpp" #define BACKEND_TYPE(type) \ public: \ inline int64_t default_priority() { \ return (int64_t)UIBackend::PriorityType::type; \ } \ private: class UIBackend { protected: std::vector args; Playback *playback; std::atomic_bool exit_flag = false; double new_speed = 1.0; double new_tempo = 1.0; double new_pitch = 1.0; bool speed_set = false; bool tempo_set = false; bool pitch_set = false; bool multi_instance = false; bool daemon_found = false; enum class PriorityType : int64_t { Default = 0, Native = 100, MetaToolkit = 50, NonNative = 0, Fallback = -100 }; public: DBusAPI *dbus_api; inline virtual bool allow_multi_instance() { return true; } virtual std::string get_id(); virtual std::string get_name(); UIBackend() = default; /// @brief A hook to add any licenses of software packages used by the UI backend. inline virtual void add_licenses() { // Don't add any here, but leave this specified. That way, licenses specific to UI frontends are only added per UI frontend. } inline int64_t default_priority() { return (int64_t)PriorityType::Default; } void init_libportal(); void init_playback(); void setup_playback_args(); void init_dbus(); bool parse_args(std::vector realArgs, int argc, char **argv); /// @brief The main loop of the UI. Be sure to call @ref UIBackend::run and be prepared for it to throw an integer exit code that needs to escape your implementation. virtual int run(std::vector realArgs, int argc, char **argv); /// @brief A hook that is called when the D-Bus API receives a request to exit the program. inline virtual void QuitHandler() { // Set a flag for loops managed by the UI backend. exit_flag.store(true); } static std::map backends; template static inline std::string get_backend_id() { UIBackend *backend = new T(); auto output = backend->get_id(); delete backend; return output; } static inline UIBackend* get_first_backend() { if (backends.empty()) { return nullptr; } return (*backends.begin()).second; } static void register_backend(UIBackend *backend) { std::string backend_id = backend->get_id(); if (backends.contains(backend_id)) { // Guard against potential memory leak due to reassigning a new pointer without deallocating the previous one delete backend; return; } backends[backend_id] = backend; } template static void register_backend() { UIBackend *backend = new T(); backend->register_self(); } static inline void unregister_backend(std::string id) { if (backends.contains(id)) { auto backend = backends[id]; delete backend; backends.erase(id); } } inline void unregister_self() { UIBackend::unregister_backend(get_id()); } inline void register_self() { UIBackend::register_backend(this); } template static void unregister_backend() { unregister_backend(get_backend_id()); } static void deinit_backends(); static inline std::optional get_backend(std::string id) { if (backends.contains(id)) { return backends[id]; } else { return {}; } } template static inline std::optional get_backend() { auto id = get_backend_id(); auto output = get_backend(id); if (output.has_value()) { return (T*)output.value(); } else { return {}; } } static UIBackend *running_ui_backend; virtual ~UIBackend(); }; void init_backends();