mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 22:47:44 +00:00
LibCore+LibGUI: Don't fire timers in non-visible windows by default
LibCore timers now have a TimerShouldFireWhenNotVisible flag which is set to "No" by default. If "No", the timer will not be fired by the event loop if it's within a CObject tree whose nearest GWindow ancestor is currently not visible for timer purposes. (Specificially, this means that the window is either minimized or fully occluded, and so does not want to fire timers just to update the UI.) This is another nice step towards a calm and serene operating system.
This commit is contained in:
parent
f8f2b8b520
commit
411d293961
6 changed files with 42 additions and 9 deletions
|
@ -366,6 +366,11 @@ void CEventLoop::wait_for_event(WaitMode mode)
|
||||||
auto& timer = *it.value;
|
auto& timer = *it.value;
|
||||||
if (!timer.has_expired(now))
|
if (!timer.has_expired(now))
|
||||||
continue;
|
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
|
#ifdef CEVENTLOOP_DEBUG
|
||||||
dbg() << "CEventLoop: Timer " << timer.timer_id << " has expired, sending CTimerEvent to " << timer.owner;
|
dbg() << "CEventLoop: Timer " << timer.timer_id << " has expired, sending CTimerEvent to " << timer.owner;
|
||||||
#endif
|
#endif
|
||||||
|
@ -411,13 +416,18 @@ void CEventLoop::get_next_timer_expiration(timeval& soonest)
|
||||||
bool has_checked_any = false;
|
bool has_checked_any = false;
|
||||||
for (auto& it : *s_timers) {
|
for (auto& it : *s_timers) {
|
||||||
auto& fire_time = it.value->fire_time;
|
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))
|
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;
|
soonest = fire_time;
|
||||||
has_checked_any = true;
|
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);
|
ASSERT(milliseconds >= 0);
|
||||||
auto timer = make<EventLoopTimer>();
|
auto timer = make<EventLoopTimer>();
|
||||||
|
@ -427,6 +437,7 @@ int CEventLoop::register_timer(CObject& object, int milliseconds, bool should_re
|
||||||
gettimeofday(&now, nullptr);
|
gettimeofday(&now, nullptr);
|
||||||
timer->reload(now);
|
timer->reload(now);
|
||||||
timer->should_reload = should_reload;
|
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.
|
int timer_id = ++s_next_timer_id; // FIXME: This will eventually wrap around.
|
||||||
ASSERT(timer_id); // FIXME: Aforementioned wraparound.
|
ASSERT(timer_id); // FIXME: Aforementioned wraparound.
|
||||||
timer->timer_id = timer_id;
|
timer->timer_id = timer_id;
|
||||||
|
|
|
@ -38,7 +38,7 @@ public:
|
||||||
|
|
||||||
bool was_exit_requested() const { return m_exit_requested; }
|
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 bool unregister_timer(int timer_id);
|
||||||
|
|
||||||
static void register_notifier(Badge<CNotifier>, CNotifier&);
|
static void register_notifier(Badge<CNotifier>, CNotifier&);
|
||||||
|
@ -77,6 +77,7 @@ private:
|
||||||
int interval { 0 };
|
int interval { 0 };
|
||||||
timeval fire_time { 0, 0 };
|
timeval fire_time { 0, 0 };
|
||||||
bool should_reload { false };
|
bool should_reload { false };
|
||||||
|
TimerShouldFireWhenNotVisible fire_when_not_visible { TimerShouldFireWhenNotVisible::No };
|
||||||
WeakPtr<CObject> owner;
|
WeakPtr<CObject> owner;
|
||||||
|
|
||||||
void reload(const timeval& now);
|
void reload(const timeval& now);
|
||||||
|
|
|
@ -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) {
|
if (m_timer_id) {
|
||||||
dbgprintf("CObject{%p} already has a timer!\n", this);
|
dbgprintf("CObject{%p} already has a timer!\n", this);
|
||||||
ASSERT_NOT_REACHED();
|
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()
|
void CObject::stop_timer()
|
||||||
|
@ -165,3 +165,10 @@ void CObject::dispatch_event(CEvent& e, CObject* stay_within)
|
||||||
target = target->parent();
|
target = target->parent();
|
||||||
} while (target && target != stay_within && !e.is_accepted());
|
} 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;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Badge.h>
|
||||||
#include <AK/Function.h>
|
#include <AK/Function.h>
|
||||||
#include <AK/IntrusiveList.h>
|
#include <AK/IntrusiveList.h>
|
||||||
#include <AK/Noncopyable.h>
|
#include <AK/Noncopyable.h>
|
||||||
|
@ -13,7 +14,13 @@ namespace AK {
|
||||||
class JsonObject;
|
class JsonObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class TimerShouldFireWhenNotVisible {
|
||||||
|
No = 0,
|
||||||
|
Yes
|
||||||
|
};
|
||||||
|
|
||||||
class CEvent;
|
class CEvent;
|
||||||
|
class CEventLoop;
|
||||||
class CChildEvent;
|
class CChildEvent;
|
||||||
class CCustomEvent;
|
class CCustomEvent;
|
||||||
class CTimerEvent;
|
class CTimerEvent;
|
||||||
|
@ -26,10 +33,10 @@ public: \
|
||||||
{ \
|
{ \
|
||||||
return adopt(*new klass(forward<Args>(args)...)); \
|
return adopt(*new klass(forward<Args>(args)...)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define C_OBJECT_ABSTRACT(klass) \
|
#define C_OBJECT_ABSTRACT(klass) \
|
||||||
public: \
|
public: \
|
||||||
virtual const char* class_name() const override { return #klass; }
|
virtual const char* class_name() const override { return #klass; }
|
||||||
|
|
||||||
class CObject
|
class CObject
|
||||||
: public RefCounted<CObject>
|
: public RefCounted<CObject>
|
||||||
|
@ -69,7 +76,7 @@ public:
|
||||||
CObject* parent() { return m_parent; }
|
CObject* parent() { return m_parent; }
|
||||||
const CObject* parent() const { 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();
|
void stop_timer();
|
||||||
bool has_timer() const { return m_timer_id; }
|
bool has_timer() const { return m_timer_id; }
|
||||||
|
|
||||||
|
@ -96,6 +103,8 @@ public:
|
||||||
m_parent->remove_child(*this);
|
m_parent->remove_child(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool is_visible_for_timer_purposes() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit CObject(CObject* parent = nullptr, bool is_widget = false);
|
explicit CObject(CObject* parent = nullptr, bool is_widget = false);
|
||||||
|
|
||||||
|
|
|
@ -589,6 +589,8 @@ void GWindow::update_all_windows(Badge<GWindowServerConnection>)
|
||||||
|
|
||||||
void GWindow::notify_state_changed(Badge<GWindowServerConnection>, bool minimized, bool occluded)
|
void GWindow::notify_state_changed(Badge<GWindowServerConnection>, 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 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!)
|
// When double buffering is disabled, there is only the back bitmap (which we can now mark volatile!)
|
||||||
RefPtr<GraphicsBitmap>& bitmap = m_double_buffering_enabled ? m_front_bitmap : m_back_bitmap;
|
RefPtr<GraphicsBitmap>& bitmap = m_double_buffering_enabled ? m_front_bitmap : m_back_bitmap;
|
||||||
|
|
|
@ -139,6 +139,8 @@ public:
|
||||||
static void update_all_windows(Badge<GWindowServerConnection>);
|
static void update_all_windows(Badge<GWindowServerConnection>);
|
||||||
void notify_state_changed(Badge<GWindowServerConnection>, bool minimized, bool occluded);
|
void notify_state_changed(Badge<GWindowServerConnection>, bool minimized, bool occluded);
|
||||||
|
|
||||||
|
virtual bool is_visible_for_timer_purposes() const override { return m_visible_for_timer_purposes; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
GWindow(CObject* parent = nullptr);
|
GWindow(CObject* parent = nullptr);
|
||||||
virtual void wm_event(GWMEvent&);
|
virtual void wm_event(GWMEvent&);
|
||||||
|
@ -176,4 +178,5 @@ private:
|
||||||
bool m_fullscreen { false };
|
bool m_fullscreen { false };
|
||||||
bool m_show_titlebar { true };
|
bool m_show_titlebar { true };
|
||||||
bool m_layout_pending { false };
|
bool m_layout_pending { false };
|
||||||
|
bool m_visible_for_timer_purposes { true };
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue