Idle Activity Detection

28 08 2009

Some applications need to detect when the user’s activity has gone idle for a period of time. Perhaps the application needs to perform certain activities when the user is idle or just wants to notify others that the user is idle.

Here is a class using wxWidgets for receiving notifications when there is a change in the user’s idle state. The amount of time that must elapse before setting the idle state is specified in the constructor as a wxTimeSpan value. Notification of the change in state is handled with the IdleDetectorEvent event. 

To initialize the idle detection you would create a new idle detection object and call the Start method.  In this example I am telling the idle detector to notify the event handler when the user has been idle for 5 minutes.

IdleDetector* idleDetector = new IdleDetector();
idleDetector->Start (this, wxTimeSpan (0, 5, 0, 0));

The wxEvtHandler that is specified as the owner of the idle detector object will receive the IdleDetectorEvent whenever the state changes. The IsIdle method of the IdleDetectorEvent can be used to determine the current state. 

BEGIN_EVENT_TABLE(MyHandler, wxEvtHandler)
    EVT_IDLE_STATE_CHANGE( MyHandler::OnIdleStateChange )
END_EVENT_TABLE()

void MyHandler::OnIdleStateChange( IdleDetectorEvent& event )
{
    if (event.IsIdle ())
        wxLogDebug ("User is now idle"); 
    else
        wxLogDebug ("User is no longer idle");
} 

Note: At this time I have only implemented the actual idle time detection method, IdleDetector::GetIdleTime, for Windows.

idledetector.h

#include "wx/window.h"
#include "wx/datetime.h"
#include "wx/timer.h"
class IdleDetectorEvent : public wxEvent
{
public:
    IdleDetectorEvent(wxEventType type = wxEVT_NULL, bool isIdle = true, const wxTimeSpan& idleTime=wxTimeSpan())
        : wxEvent(0, type), m_isIdle(isIdle), m_idleTime(idleTime)
    {}
    IdleDetectorEvent(const IdleDetectorEvent& event)
        : wxEvent(event), m_isIdle(event.m_isIdle), m_idleTime(event.m_idleTime)
    {}
    bool IsIdle() const { return m_isIdle; }
    const wxTimeSpan& GetIdleTime() const { return m_idleTime; }
    virtual wxEvent *Clone() const { return new IdleDetectorEvent(*this); }
private:
    bool        m_isIdle;
    wxTimeSpan  m_idleTime;
private:
    DECLARE_DYNAMIC_CLASS_NO_ASSIGN(IdleDetectorEvent)
};
BEGIN_DECLARE_EVENT_TYPES()
    DECLARE_EVENT_TYPE(EVT_IDLE_STATE_CHANGE, -1)
END_DECLARE_EVENT_TYPES()
typedef void (wxEvtHandler::*IdleDetectorEventFunction) (IdleDetectorEvent&);
#define EVT_IDLE_STATE_CHANGE(func)  DECLARE_EVENT_TABLE_ENTRY( EVT_IDLE_STATE_CHANGE, wxID_ANY, wxID_ANY, IdleDetectorEventHandler(func), NULL ),
#define IdleDetectorEventHandler(func) \
    (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(IdleDetectorEventFunction, &func)
class IdleDetector : public wxTimer
{
public:
    IdleDetector () : m_isIdle(false) {}
    const wxTimeSpan& GetTimeout () const { return m_timeout; }
    void Start (wxEvtHandler* owner, const wxTimeSpan& timeout);
    void Pause ();
    void Resume ();
    bool    IsIdle () const;
    static const wxTimeSpan GetIdleTime ();
private:
    void    Notify ();
    void    SetState (bool isIdle, const wxTimeSpan& idleTime);
private:
    wxTimeSpan      m_timeout;
    bool            m_isIdle;
};

idledetector.cpp

#include "wx/window.h"
#include "idledetector.h"
DEFINE_EVENT_TYPE(EVT_IDLE_STATE_CHANGE)
IMPLEMENT_DYNAMIC_CLASS(IdleDetectorEvent, wxEvent)
#define IDLE_TO_ACTIVE_TIMEOUT  1000
const wxTimeSpan IdleDetector::GetIdleTime ()
{
#ifdef __WXMSW__
    LASTINPUTINFO   info;
    info.cbSize = sizeof(info);
    if (::GetLastInputInfo (&info))
    {
        long timeIdle = ::GetTickCount() - info.dwTime;
        // Handle overflow
        if (timeIdle < 0)
            timeIdle = timeIdle + UINT_MAX + 1;
 
        return (wxTimeSpan(0, 0, 0, timeIdle));
    }
#endif
    return (wxTimeSpan ());
}
void IdleDetector::Start (wxEvtHandler* owner, const wxTimeSpan& timeout)
{
    SetOwner (owner);
    m_timeout = timeout;
    m_isIdle = false;
    SetState (false, wxTimeSpan());
}
void IdleDetector::Pause ()
{
    wxTimer::Stop ();
}
void IdleDetector::Resume ()
{
    SetState (m_isIdle, GetIdleTime());
}
bool IdleDetector::IsIdle () const
{
    return (IsRunning () ? m_isIdle : false);
}
void IdleDetector::Notify ()
{
    wxTimeSpan idleTime = GetIdleTime ();
    //
    // If not currently idle, then the state changes if the amount of time that
    // the session has been idle is greater than or equal to the idle timeout.
    //
    // If currently idle, then the state changes if the amount of time that
    // the session has been idle is less than the idle timeout. This assumes
    // that the idle timeout value is greater than the IDLE_TO_ACTIVE_TIMEOUT
    // timeout value, which it really should be.
    //
    if (!m_isIdle)
        SetState (idleTime >= m_timeout, idleTime);
    else if (idleTime < m_timeout)
        SetState (false, idleTime);
}
void IdleDetector::SetState (bool isIdle, const wxTimeSpan& idleTime)
{
    if (isIdle != m_isIdle)
    {
        m_isIdle = isIdle;
        IdleDetectorEvent   event(EVT_IDLE_STATE_CHANGE, isIdle, idleTime);
        event.SetEventObject (this);
        wxPostEvent (m_owner, event);
    }
    if (m_isIdle)
        wxTimer::Start (IDLE_TO_ACTIVE_TIMEOUT, wxTIMER_CONTINUOUS);
    else
    {
        wxTimeSpan  timeRemaining = m_timeout - idleTime;
        wxTimer::Start (timeRemaining.IsPositive() ? timeRemaining.GetMilliseconds().ToLong() : 10, wxTIMER_ONE_SHOT);
    }
}
About these ads

Actions

Information

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




Follow

Get every new post delivered to your Inbox.

%d bloggers like this: