#pragma once #include #include #include #include #include "glibmm/main.h" #include "gtkmm/mediafile.h" #include "gtkmm/mediastream.h" #include "sigc++/signal.h" class TimerService { struct TimerData { uint64_t duration; uint64_t timerId; }; public: static std::shared_ptr getInstance() { if (!instance) { instance = std::shared_ptr(new TimerService()); // add a timer to tick every second Glib::signal_timeout().connect([]() { instance->tick(); return true; // continue calling every second }, 1000); } return instance; } void addTimer(uint64_t duration) { this->timerAddedSignal.emit(std::to_string(duration), timerIdCounter); this->activeTimers[timerIdCounter] = TimerData{.duration = duration, .timerId = timerIdCounter}; timerIdCounter++; } void expireTimer(uint64_t timerId) { this->timerExpiredSignal.emit(timerId); if (ringingTimerCounter == 0) { mediaFile->set_volume(1.0); mediaFile->set_loop(true); mediaFile->play(); spdlog::info("Playing timer sound"); } ringingTimerCounter++; } void removeTimer(uint64_t timerId) { ringingTimerCounter--; this->activeTimers.erase(timerId); this->timerCancelledSignal.emit(timerId); if (ringingTimerCounter <= 0) { ringingTimerCounter = 0; mediaFile->pause(); spdlog::info("Paused timer sound"); } } sigc::signal &getSignalTimerSet() { return this->timerAddedSignal; } sigc::signal &getSignalTimerExpired() { return this->timerExpiredSignal; } sigc::signal &getSignalTimerCancelled() { return this->timerCancelledSignal; } sigc::signal tickSignal; private: TimerService() { this->mediaFile = Gtk::MediaFile::create(); if (this->mediaFile) { this->mediaFile->set_filename("/usr/share/sounds/freedesktop/stereo/alarm-clock-elapsed.oga"); // Connect to signals to debug playback issues this->mediaFile->property_error().signal_changed().connect([]() { if (auto err = mediaFile->get_error()) { spdlog::error("Timer sound error: {}", err.what()); } }); this->mediaFile->property_prepared().signal_changed().connect([]() { if (mediaFile->is_prepared()) { spdlog::info("Timer sound ready"); } }); } else { spdlog::error("Failed to create media file!"); } } inline static Glib::RefPtr mediaFile; static inline uint64_t timerIdCounter = 0; static inline u_int64_t ringingTimerCounter = 0; sigc::signal timerAddedSignal; sigc::signal timerCancelledSignal; sigc::signal timerExpiredSignal; inline static std::shared_ptr instance = nullptr; std::map activeTimers; void tick() { for (auto &[id, timer] : activeTimers) { if (timer.duration > 0) { timer.duration--; } else if (timer.duration == 0) { spdlog::info("Timer {} expired", timer.timerId); expireTimer(timer.timerId); timer.duration = -1; // Mark as expired to prevent multiple expirations } } tickSignal.emit(); } };