Broken Idle Time in 10.4/10.5

After fighting with trying to get the amount of time since a user has done something with the system, I’ve determined that CGEventSourceSecondsSinceLastEventType is broken. The documentation indicates that calling it like:

CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGAnyInputEventType)

will tell me how many seconds since the user last moved the mouse, touched the keyboard, etc. The docs say:

The various system and app defined events do not contribute to this event type’s time.

Unfortunately this just isn’t true. In one app I’m working on, I have a timer that fires every 5 seconds and prints the idle time. It starts going up, but then a notification comes in and it resets back to zero without me touching the keyboard. So, this call is almost useless as I need to know when things are idle in order to perform some tasks. While some of you are saying that I can use a Carbon Event Idle Timer, it turns out that they don’t work in background only apps. My only solution is to make the above call using something like:

+ (double) idleTimeInSeconds
{
	double idleTime = 0;
	double tempIdleTime = 0;
	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventLeftMouseDown);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}
	
	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventLeftMouseUp);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}

	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventRightMouseDown);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}

	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventRightMouseUp);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}
	
	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventMouseMoved);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}
	
	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventLeftMouseDragged);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}
	
	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventRightMouseDragged);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}
	
	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventKeyDown);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}
	
	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventKeyUp);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}
	
	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventFlagsChanged);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}
	
	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventScrollWheel);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}
	
	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventTabletPointer);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}
	
	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventTabletProximity);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}
	
	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventOtherMouseDown);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}
	
	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventOtherMouseUp);
	if (idleTime == 0 || tempIdleTime < idleTime)
	{
		idleTime = tempIdleTime;
	}
	
	tempIdleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventOtherMouseDragged);
	
	return idleTime;
}

Wow, that is freaking ugly, but at least I know exactly when a user event occurred. Feel free to use my code in any way you see fit. I've searched the web and found answers to a lot of my problems, so here's a small contribution back to the community.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.