diff --git a/src/core/clock.cpp b/src/core/clock.cpp index 0af9762..ee396ac 100644 --- a/src/core/clock.cpp +++ b/src/core/clock.cpp @@ -8,7 +8,7 @@ #include "util.hpp" SystemClock::SystemClock(QObject* parent): QObject(parent) { - QObject::connect(&this->timer, &QTimer::timeout, this, &SystemClock::update); + QObject::connect(&this->timer, &QTimer::timeout, this, &SystemClock::onTimeout); this->update(); } @@ -30,41 +30,63 @@ void SystemClock::setPrecision(SystemClock::Enum precision) { this->update(); } +void SystemClock::onTimeout() { + this->setTime(this->nextTime); + this->schedule(this->nextTime); +} + void SystemClock::update() { - auto time = QTime::currentTime(); - if (this->mEnabled) { - auto secondPrecision = this->mPrecision >= SystemClock::Seconds; - auto secondChanged = this->setSeconds(secondPrecision ? time.second() : 0); - - auto minutePrecision = this->mPrecision >= SystemClock::Minutes; - auto minuteChanged = this->setMinutes(minutePrecision ? time.minute() : 0); - - auto hourPrecision = this->mPrecision >= SystemClock::Hours; - auto hourChanged = this->setHours(hourPrecision ? time.hour() : 0); - - DropEmitter::call(secondChanged, minuteChanged, hourChanged); - - auto nextTime = QTime( - hourPrecision ? time.hour() : 0, - minutePrecision ? time.minute() : 0, - secondPrecision ? time.second() : 0 - ); - - if (secondPrecision) nextTime = nextTime.addSecs(1); - else if (minutePrecision) nextTime = nextTime.addSecs(60); - else if (hourPrecision) nextTime = nextTime.addSecs(3600); - - auto delay = time.msecsTo(nextTime); - // day rollover - if (delay < 0) delay += 86400000; - - this->timer.start(delay); + this->setTime(QTime::currentTime()); + this->schedule(QTime::currentTime()); } else { this->timer.stop(); } } +void SystemClock::setTime(QTime time) { + auto secondPrecision = this->mPrecision >= SystemClock::Seconds; + auto secondChanged = this->setSeconds(secondPrecision ? time.second() : 0); + + auto minutePrecision = this->mPrecision >= SystemClock::Minutes; + auto minuteChanged = this->setMinutes(minutePrecision ? time.minute() : 0); + + auto hourPrecision = this->mPrecision >= SystemClock::Hours; + auto hourChanged = this->setHours(hourPrecision ? time.hour() : 0); + + DropEmitter::call(secondChanged, minuteChanged, hourChanged); +} + +void SystemClock::schedule(QTime floor) { + auto secondPrecision = this->mPrecision >= SystemClock::Seconds; + auto minutePrecision = this->mPrecision >= SystemClock::Minutes; + auto hourPrecision = this->mPrecision >= SystemClock::Hours; + +setnext: + auto nextTime = QTime( + hourPrecision ? floor.hour() : 0, + minutePrecision ? floor.minute() : 0, + secondPrecision ? floor.second() : 0 + ); + + if (secondPrecision) nextTime = nextTime.addSecs(1); + else if (minutePrecision) nextTime = nextTime.addSecs(60); + else if (hourPrecision) nextTime = nextTime.addSecs(3600); + + auto delay = QTime::currentTime().msecsTo(nextTime); + + // If off by more than 2 hours we likely wrapped around midnight. + if (delay < -7200000) delay += 86400000; + else if (delay < 0) { + // Otherwise its just the timer being unstable. + floor = QTime::currentTime(); + goto setnext; + } + + this->timer.start(delay); + this->nextTime = nextTime; +} + DEFINE_MEMBER_GETSET(SystemClock, hours, setHours); DEFINE_MEMBER_GETSET(SystemClock, minutes, setMinutes); DEFINE_MEMBER_GETSET(SystemClock, seconds, setSeconds); diff --git a/src/core/clock.hpp b/src/core/clock.hpp index b3ac122..7f0dbf7 100644 --- a/src/core/clock.hpp +++ b/src/core/clock.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -49,7 +50,7 @@ signals: void secondsChanged(); private slots: - void update(); + void onTimeout(); private: bool mEnabled = true; @@ -58,6 +59,11 @@ private: quint32 mMinutes = 0; quint32 mSeconds = 0; QTimer timer; + QTime nextTime; + + void update(); + void setTime(QTime time); + void schedule(QTime floor); DECLARE_PRIVATE_MEMBER(SystemClock, hours, setHours, mHours, hoursChanged); DECLARE_PRIVATE_MEMBER(SystemClock, minutes, setMinutes, mMinutes, minutesChanged); diff --git a/src/wayland/toplevel_management/qml.cpp b/src/wayland/toplevel_management/qml.cpp index 2042262..1f0d1fe 100644 --- a/src/wayland/toplevel_management/qml.cpp +++ b/src/wayland/toplevel_management/qml.cpp @@ -3,6 +3,7 @@ #include #include +#include "../../core/util.hpp" #include "../../core/model.hpp" #include "../../core/proxywindow.hpp" #include "../../core/qmlscreen.hpp" @@ -132,22 +133,49 @@ ObjectModel* ToplevelManager::toplevels() { return &this->mToplevels; void ToplevelManager::onToplevelReady(impl::ToplevelHandle* handle) { auto* toplevel = new Toplevel(handle, this); + + // clang-format off QObject::connect(toplevel, &Toplevel::closed, this, &ToplevelManager::onToplevelClosed); + QObject::connect(toplevel, &Toplevel::activatedChanged, this, &ToplevelManager::onToplevelActiveChanged); + // clang-format on + + if (toplevel->activated()) this->setActiveToplevel(toplevel); this->mToplevels.insertObject(toplevel); } +void ToplevelManager::onToplevelActiveChanged() { + auto* toplevel = qobject_cast(this->sender()); + if (toplevel->activated()) this->setActiveToplevel(toplevel); +} + void ToplevelManager::onToplevelClosed() { auto* toplevel = qobject_cast(this->sender()); + if (toplevel == this->mActiveToplevel) this->setActiveToplevel(nullptr); this->mToplevels.removeObject(toplevel); } +DEFINE_MEMBER_GETSET(ToplevelManager, activeToplevel, setActiveToplevel); + ToplevelManager* ToplevelManager::instance() { static auto* instance = new ToplevelManager(); // NOLINT return instance; } +ToplevelManagerQml::ToplevelManagerQml(QObject* parent): QObject(parent) { + QObject::connect( + ToplevelManager::instance(), + &ToplevelManager::activeToplevelChanged, + this, + &ToplevelManagerQml::activeToplevelChanged + ); +} + ObjectModel* ToplevelManagerQml::toplevels() { return ToplevelManager::instance()->toplevels(); } +Toplevel* ToplevelManagerQml::activeToplevel() { + return ToplevelManager::instance()->activeToplevel(); +} + } // namespace qs::wayland::toplevel_management diff --git a/src/wayland/toplevel_management/qml.hpp b/src/wayland/toplevel_management/qml.hpp index 64951b6..d50713c 100644 --- a/src/wayland/toplevel_management/qml.hpp +++ b/src/wayland/toplevel_management/qml.hpp @@ -7,6 +7,7 @@ #include "../../core/model.hpp" #include "../../core/proxywindow.hpp" #include "../../core/qmlscreen.hpp" +#include "../../core/util.hpp" namespace qs::wayland::toplevel_management { @@ -111,14 +112,27 @@ public: static ToplevelManager* instance(); +signals: + void activeToplevelChanged(); + private slots: void onToplevelReady(impl::ToplevelHandle* handle); + void onToplevelActiveChanged(); void onToplevelClosed(); private: explicit ToplevelManager(); ObjectModel mToplevels {this}; + Toplevel* mActiveToplevel = nullptr; + + DECLARE_PRIVATE_MEMBER( + ToplevelManager, + activeToplevel, + setActiveToplevel, + mActiveToplevel, + activeToplevelChanged + ); }; ///! Exposes a list of Toplevels. @@ -127,14 +141,24 @@ private: /// wayland protocol. class ToplevelManagerQml: public QObject { Q_OBJECT; + /// All toplevel windows exposed by the compositor. Q_PROPERTY(ObjectModel* toplevels READ toplevels CONSTANT); + /// Active toplevel or null. + /// + /// > [!INFO] If multiple are active, this will be the most recently activated one. + /// > Usually compositors will not report more than one toplevel as active at a time. + Q_PROPERTY(Toplevel* activeToplevel READ activeToplevel NOTIFY activeToplevelChanged); QML_NAMED_ELEMENT(ToplevelManager); QML_SINGLETON; public: - explicit ToplevelManagerQml(QObject* parent = nullptr): QObject(parent) {} + explicit ToplevelManagerQml(QObject* parent = nullptr); [[nodiscard]] static ObjectModel* toplevels(); + [[nodiscard]] static Toplevel* activeToplevel(); + +signals: + void activeToplevelChanged(); }; } // namespace qs::wayland::toplevel_management