5 // Created by Evan Schoenberg on 7/5/05.
8 #import "AdiumIdleManager.h"
9 #import "AIStatusController.h"
11 #define MACHINE_IDLE_THRESHOLD 30 //30 seconds of inactivity is considered idle
12 #define MACHINE_ACTIVE_POLL_INTERVAL 30 //Poll every 30 seconds when the user is active
13 #define MACHINE_IDLE_POLL_INTERVAL 1 //Poll every second when the user is idle
15 //Private idle function
16 extern CFTimeInterval CGSSecondsSinceLastInputEvent(unsigned long evType);
18 @interface AdiumIdleManager (PRIVATE)
19 - (void)_setMachineIsIdle:(BOOL)inIdle;
20 - (void)screenSaverDidStart;
21 - (void)screenSaverDidStop;
25 * @class AdiumIdleManager
26 * @brief Core class to manage sending notifications when the system is idle or no longe ridle
28 * Posts AIMachineIsIdleNotification to adium's notification center when the machine becomes idle.
29 * Posts AIMachineIsActiveNotification when the machine is no longer idle
30 * Posts AIMachineIdleUpdateNotification periodically while idle with an NSDictionary userInfo
31 * containing an NSNumber double value @"Duration" (a CFTimeInterval) and an NSDate @"IdleSince".
33 @implementation AdiumIdleManager
40 if ((self = [super init])) {
41 [self _setMachineIsIdle:NO];
42 [[NSDistributedNotificationCenter defaultCenter] addObserver:self
43 selector:@selector(screenSaverDidStart)
44 name:@"com.apple.screensaver.didstart"
46 [[NSDistributedNotificationCenter defaultCenter] addObserver:self
47 selector:@selector(screenSaverDidStop)
48 name:@"com.apple.screensaver.didstop"
56 * @brief Returns the current machine idle time
58 * Returns the current number of seconds the machine has been idle. The machine is idle when there are no input
59 * events from the user (such as mouse movement or keyboard input) or when the screen saver is active.
60 * In addition to this method, the status controller sends out notifications when the machine becomes idle,
61 * stays idle, and returns to an active state.
63 - (CFTimeInterval)currentMachineIdle
65 CFTimeInterval idleTime;
67 /* CGSSecondsSinceLastInputEvent is a private function available in 10.2 and later. Note that CGEventSourceSecondsSinceLastEventType()
68 * should work as of 10.4 but doesn't return a sensical value.
70 idleTime = CGSSecondsSinceLastInputEvent(-1);
72 /* On MDD Powermacs, the above function will return a large value when the machine is active (perhaps a -1?).
73 * Here we check for that value and correctly return a 0 idle time.
75 if (idleTime >= 18446744000.0) idleTime = 0.0; //18446744073.0 is the lowest I've seen on my MDD -ai
81 * @brief Timer that checkes for machine idle
83 * This timer periodically checks the machine for inactivity. When the machine has been inactive for atleast
84 * MACHINE_IDLE_THRESHOLD seconds, a notification is broadcast.
86 * When the machine is active, this timer is called infrequently. It's not important to notice that the user went
87 * idle immediately, so we relax our CPU usage while waiting for an idle state to begin.
89 * When the machine is idle, the timer is called frequently. It's important to notice immediately when the user
92 - (void)_idleCheckTimer:(NSTimer *)inTimer
94 CFTimeInterval currentIdle = [self currentMachineIdle];
97 if (currentIdle < lastSeenIdle) {
98 /* If the machine is less idle than the last time we recorded, it means that activity has occured and the
99 * user is no longer idle.
101 [self _setMachineIsIdle:NO];
103 //Periodically broadcast a 'MachineIdleUpdate' notification
104 [[adium notificationCenter] postNotificationName:AIMachineIdleUpdateNotification
106 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
107 [NSNumber numberWithDouble:currentIdle], @"Duration",
108 [NSDate dateWithTimeIntervalSinceNow:-currentIdle], @"IdleSince",
112 //If machine inactivity is over the threshold, the user has gone idle.
113 if (currentIdle > MACHINE_IDLE_THRESHOLD) [self _setMachineIsIdle:YES];
116 lastSeenIdle = currentIdle;
120 * @brief Sets the machine as idle or not
122 * This internal method updates the frequency of our idle timer depending on whether the machine is considered
123 * idle or not. It also posts the AIMachineIsIdleNotification and AIMachineIsActiveNotification notifications
124 * based on the passed idle state
126 - (void)_setMachineIsIdle:(BOOL)inIdle
128 machineIsIdle = inIdle;
130 //Post the appropriate idle or active notification
132 [[adium notificationCenter] postNotificationName:AIMachineIsIdleNotification object:nil];
134 [[adium notificationCenter] postNotificationName:AIMachineIsActiveNotification object:nil];
137 //Update our timer interval for either idle or active polling
138 [idleTimer invalidate];
140 idleTimer = [[NSTimer scheduledTimerWithTimeInterval:(machineIsIdle ? MACHINE_IDLE_POLL_INTERVAL : MACHINE_ACTIVE_POLL_INTERVAL)
142 selector:@selector(_idleCheckTimer:)
144 repeats:YES] retain];
148 * @brief Called by the screen saver when it starts
150 * When the screen saver starts, we set ourself to idle.
152 - (void)screenSaverDidStart
154 [self _setMachineIsIdle:YES];
158 * @brief Called by the screen saver when it starts
160 * When the screen saver stops, we set ourself to active.
162 - (void)screenSaverDidStop
164 [self _setMachineIsIdle:NO];