michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "base/idle_timer.h" michael@0: michael@0: // We may not want to port idle_timer to Linux, but we have implemented it michael@0: // anyway. Define this to 1 to enable the Linux idle timer and then add the michael@0: // libs that need to be linked (Xss). michael@0: #define ENABLE_XSS_SUPPORT 0 michael@0: michael@0: #if defined(OS_MACOSX) michael@0: #include michael@0: #endif michael@0: michael@0: #if defined(OS_LINUX) && ENABLE_XSS_SUPPORT michael@0: // We may not want to port idle_timer to Linux, but we have implemented it michael@0: // anyway. Remove the 0 above if we want it. michael@0: #include michael@0: #include michael@0: #include "base/lazy_instance.h" michael@0: #include "base/thread_local.h" michael@0: #endif michael@0: michael@0: #include "base/message_loop.h" michael@0: #include "base/time.h" michael@0: michael@0: namespace base { michael@0: michael@0: #if defined(OS_WIN) michael@0: bool OSIdleTimeSource(int32_t *milliseconds_interval_since_last_event) { michael@0: LASTINPUTINFO lastInputInfo; michael@0: lastInputInfo.cbSize = sizeof(lastInputInfo); michael@0: if (GetLastInputInfo(&lastInputInfo) == 0) { michael@0: return false; michael@0: } michael@0: int32_t last_input_time = lastInputInfo.dwTime; michael@0: michael@0: // Note: On Windows GetLastInputInfo returns a 32bit value which rolls over michael@0: // ~49days. michael@0: int32_t current_time = GetTickCount(); michael@0: int32_t delta = current_time - last_input_time; michael@0: // delta will go negative if we've been idle for 2GB of ticks. michael@0: if (delta < 0) michael@0: delta = -delta; michael@0: *milliseconds_interval_since_last_event = delta; michael@0: return true; michael@0: } michael@0: #elif defined(OS_MACOSX) michael@0: bool OSIdleTimeSource(int32_t *milliseconds_interval_since_last_event) { michael@0: *milliseconds_interval_since_last_event = michael@0: CGEventSourceSecondsSinceLastEventType( michael@0: kCGEventSourceStateCombinedSessionState, michael@0: kCGAnyInputEventType) * 1000.0; michael@0: return true; michael@0: } michael@0: #elif defined(OS_LINUX) && ENABLE_XSS_SUPPORT michael@0: class IdleState { michael@0: public: michael@0: IdleState() { michael@0: int event_base, error_base; michael@0: have_idle_info_ = XScreenSaverQueryExtension(GDK_DISPLAY(), &event_base, michael@0: &error_base); michael@0: if (have_idle_info_) michael@0: idle_info_.Set(XScreenSaverAllocInfo()); michael@0: } michael@0: michael@0: ~IdleState() { michael@0: if (idle_info_.Get()) { michael@0: XFree(idle_info_.Get()); michael@0: idle_info_.~ThreadLocalPointer(); michael@0: } michael@0: } michael@0: michael@0: int32_t IdleTime() { michael@0: if (have_idle_info_ && idle_info_.Get()) { michael@0: XScreenSaverQueryInfo(GDK_DISPLAY(), GDK_ROOT_WINDOW(), michael@0: idle_info_.Get()); michael@0: return idle_info_.Get()->idle; michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: private: michael@0: bool have_idle_info_; michael@0: ThreadLocalPointer idle_info_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(IdleState); michael@0: }; michael@0: michael@0: bool OSIdleTimeSource(int32_t* milliseconds_interval_since_last_event) { michael@0: static LazyInstance state_instance(base::LINKER_INITIALIZED); michael@0: IdleState* state = state_instance.Pointer(); michael@0: int32_t idle_time = state->IdleTime(); michael@0: if (0 < idle_time) { michael@0: *milliseconds_interval_since_last_event = idle_time; michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: #endif michael@0: michael@0: IdleTimer::IdleTimer(TimeDelta idle_time, bool repeat) michael@0: : idle_interval_(idle_time), michael@0: repeat_(repeat), michael@0: idle_time_source_(OSIdleTimeSource) { michael@0: DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()) << michael@0: "Requires a thread that processes Windows UI events"; michael@0: } michael@0: michael@0: IdleTimer::~IdleTimer() { michael@0: Stop(); michael@0: } michael@0: michael@0: void IdleTimer::Start() { michael@0: StartTimer(); michael@0: } michael@0: michael@0: void IdleTimer::Stop() { michael@0: timer_.Stop(); michael@0: } michael@0: michael@0: void IdleTimer::Run() { michael@0: // Verify we can fire the idle timer. michael@0: if (TimeUntilIdle().InMilliseconds() <= 0) { michael@0: OnIdle(); michael@0: last_time_fired_ = Time::Now(); michael@0: } michael@0: Stop(); michael@0: StartTimer(); // Restart the timer for next run. michael@0: } michael@0: michael@0: void IdleTimer::StartTimer() { michael@0: DCHECK(!timer_.IsRunning()); michael@0: TimeDelta delay = TimeUntilIdle(); michael@0: if (delay.InMilliseconds() < 0) michael@0: delay = TimeDelta(); michael@0: timer_.Start(delay, this, &IdleTimer::Run); michael@0: } michael@0: michael@0: TimeDelta IdleTimer::CurrentIdleTime() { michael@0: int32_t interval = 0; michael@0: if (idle_time_source_(&interval)) { michael@0: return TimeDelta::FromMilliseconds(interval); michael@0: } michael@0: NOTREACHED(); michael@0: return TimeDelta::FromMilliseconds(0); michael@0: } michael@0: michael@0: TimeDelta IdleTimer::TimeUntilIdle() { michael@0: TimeDelta time_since_last_fire = Time::Now() - last_time_fired_; michael@0: TimeDelta current_idle_time = CurrentIdleTime(); michael@0: if (current_idle_time > time_since_last_fire) { michael@0: if (repeat_) michael@0: return idle_interval_ - time_since_last_fire; michael@0: return idle_interval_; michael@0: } michael@0: return idle_interval_ - current_idle_time; michael@0: } michael@0: michael@0: } // namespace base