diff --git a/Libraries/LibCore/CEventLoop.cpp b/Libraries/LibCore/CEventLoop.cpp index 0f0dbb4b24..15376a3f7a 100644 --- a/Libraries/LibCore/CEventLoop.cpp +++ b/Libraries/LibCore/CEventLoop.cpp @@ -366,6 +366,11 @@ void CEventLoop::wait_for_event(WaitMode mode) auto& timer = *it.value; if (!timer.has_expired(now)) continue; + if (it.value->fire_when_not_visible == TimerShouldFireWhenNotVisible::No + && it.value->owner + && !it.value->owner->is_visible_for_timer_purposes()) { + continue; + } #ifdef CEVENTLOOP_DEBUG dbg() << "CEventLoop: Timer " << timer.timer_id << " has expired, sending CTimerEvent to " << timer.owner; #endif @@ -411,13 +416,18 @@ void CEventLoop::get_next_timer_expiration(timeval& soonest) bool has_checked_any = false; for (auto& it : *s_timers) { auto& fire_time = it.value->fire_time; + if (it.value->fire_when_not_visible == TimerShouldFireWhenNotVisible::No + && it.value->owner + && !it.value->owner->is_visible_for_timer_purposes()) { + continue; + } if (!has_checked_any || fire_time.tv_sec < soonest.tv_sec || (fire_time.tv_sec == soonest.tv_sec && fire_time.tv_usec < soonest.tv_usec)) soonest = fire_time; has_checked_any = true; } } -int CEventLoop::register_timer(CObject& object, int milliseconds, bool should_reload) +int CEventLoop::register_timer(CObject& object, int milliseconds, bool should_reload, TimerShouldFireWhenNotVisible fire_when_not_visible) { ASSERT(milliseconds >= 0); auto timer = make(); @@ -427,6 +437,7 @@ int CEventLoop::register_timer(CObject& object, int milliseconds, bool should_re gettimeofday(&now, nullptr); timer->reload(now); timer->should_reload = should_reload; + timer->fire_when_not_visible = fire_when_not_visible; int timer_id = ++s_next_timer_id; // FIXME: This will eventually wrap around. ASSERT(timer_id); // FIXME: Aforementioned wraparound. timer->timer_id = timer_id; diff --git a/Libraries/LibCore/CEventLoop.h b/Libraries/LibCore/CEventLoop.h index 5166b79093..af942bef19 100644 --- a/Libraries/LibCore/CEventLoop.h +++ b/Libraries/LibCore/CEventLoop.h @@ -38,7 +38,7 @@ public: bool was_exit_requested() const { return m_exit_requested; } - static int register_timer(CObject&, int milliseconds, bool should_reload); + static int register_timer(CObject&, int milliseconds, bool should_reload, TimerShouldFireWhenNotVisible); static bool unregister_timer(int timer_id); static void register_notifier(Badge, CNotifier&); @@ -77,6 +77,7 @@ private: int interval { 0 }; timeval fire_time { 0, 0 }; bool should_reload { false }; + TimerShouldFireWhenNotVisible fire_when_not_visible { TimerShouldFireWhenNotVisible::No }; WeakPtr owner; void reload(const timeval& now); diff --git a/Libraries/LibCore/CObject.cpp b/Libraries/LibCore/CObject.cpp index 7de5a5a044..045fd85694 100644 --- a/Libraries/LibCore/CObject.cpp +++ b/Libraries/LibCore/CObject.cpp @@ -100,14 +100,14 @@ void CObject::custom_event(CCustomEvent&) { } -void CObject::start_timer(int ms) +void CObject::start_timer(int ms, TimerShouldFireWhenNotVisible fire_when_not_visible) { if (m_timer_id) { dbgprintf("CObject{%p} already has a timer!\n", this); ASSERT_NOT_REACHED(); } - m_timer_id = CEventLoop::register_timer(*this, ms, true); + m_timer_id = CEventLoop::register_timer(*this, ms, true, fire_when_not_visible); } void CObject::stop_timer() @@ -165,3 +165,10 @@ void CObject::dispatch_event(CEvent& e, CObject* stay_within) target = target->parent(); } while (target && target != stay_within && !e.is_accepted()); } + +bool CObject::is_visible_for_timer_purposes() const +{ + if (parent()) + return parent()->is_visible_for_timer_purposes(); + return true; +} diff --git a/Libraries/LibCore/CObject.h b/Libraries/LibCore/CObject.h index b9c8828249..10607123cc 100644 --- a/Libraries/LibCore/CObject.h +++ b/Libraries/LibCore/CObject.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -13,7 +14,13 @@ namespace AK { class JsonObject; } +enum class TimerShouldFireWhenNotVisible { + No = 0, + Yes +}; + class CEvent; +class CEventLoop; class CChildEvent; class CCustomEvent; class CTimerEvent; @@ -26,10 +33,10 @@ public: \ { \ return adopt(*new klass(forward(args)...)); \ } - -#define C_OBJECT_ABSTRACT(klass) \ -public: \ - virtual const char* class_name() const override { return #klass; } + +#define C_OBJECT_ABSTRACT(klass) \ +public: \ + virtual const char* class_name() const override { return #klass; } class CObject : public RefCounted @@ -69,7 +76,7 @@ public: CObject* parent() { return m_parent; } const CObject* parent() const { return m_parent; } - void start_timer(int ms); + void start_timer(int ms, TimerShouldFireWhenNotVisible = TimerShouldFireWhenNotVisible::No); void stop_timer(); bool has_timer() const { return m_timer_id; } @@ -96,6 +103,8 @@ public: m_parent->remove_child(*this); } + virtual bool is_visible_for_timer_purposes() const; + protected: explicit CObject(CObject* parent = nullptr, bool is_widget = false); diff --git a/Libraries/LibGUI/GWindow.cpp b/Libraries/LibGUI/GWindow.cpp index 1665d2b0dd..bccc0c7f15 100644 --- a/Libraries/LibGUI/GWindow.cpp +++ b/Libraries/LibGUI/GWindow.cpp @@ -589,6 +589,8 @@ void GWindow::update_all_windows(Badge) void GWindow::notify_state_changed(Badge, bool minimized, bool occluded) { + m_visible_for_timer_purposes = !minimized && !occluded; + // When double buffering is enabled, minimization/occlusion means we can mark the front bitmap volatile (in addition to the back bitmap.) // When double buffering is disabled, there is only the back bitmap (which we can now mark volatile!) RefPtr& bitmap = m_double_buffering_enabled ? m_front_bitmap : m_back_bitmap; diff --git a/Libraries/LibGUI/GWindow.h b/Libraries/LibGUI/GWindow.h index 9367d42c99..e1cba8fb6d 100644 --- a/Libraries/LibGUI/GWindow.h +++ b/Libraries/LibGUI/GWindow.h @@ -139,6 +139,8 @@ public: static void update_all_windows(Badge); void notify_state_changed(Badge, bool minimized, bool occluded); + virtual bool is_visible_for_timer_purposes() const override { return m_visible_for_timer_purposes; } + protected: GWindow(CObject* parent = nullptr); virtual void wm_event(GWMEvent&); @@ -176,4 +178,5 @@ private: bool m_fullscreen { false }; bool m_show_titlebar { true }; bool m_layout_pending { false }; + bool m_visible_for_timer_purposes { true }; };