1 /* This file is part of the KDE project
2 Copyright (C) 2005-2006 Tom Albers <tomalbers@kde.nl>
3 Copyright (C) 2005-2006 Bram Schoenmakers <bramschoenmakers@kde.nl>
5 The parts for idle detection is based on
6 kdepim's karm idletimedetector.cpp/.h
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include <kapplication.h>
27 // The order here is important, otherwise Qt headers are preprocessed into garbage.... :-(
29 #include "config.h" // HAVE_LIBXSS
30 #ifdef HAVE_LIBXSS // Idle detection.
32 #include <X11/Xutil.h>
33 #include <X11/extensions/scrnsaver.h>
37 #include "rsiglobals.h"
40 #include "rsitimer_dpms.h"
43 RSITimer::RSITimer( QObject
*parent
, const char *name
)
44 : QObject( parent
, name
), m_breakRequested( false )
45 , m_tinyBreakRequested( false )
46 , m_bigBreakRequested( false )
47 , m_suspended( false )
48 , m_needRestart( false )
49 , m_pause_left( 0 ), m_relax_left( 0 )
50 , dpmsOff( -10 ), dpmsStandby(-10), dpmsSuspend(-10)
51 , m_lastActivity( QDateTime::currentDateTime() )
52 , m_intervals( RSIGlobals::instance()->intervals() )
54 kdDebug() << "Starting timer constructor" << endl
;
56 // if big_maximized < tiny_maximized, the bigbreaks will not get reset,
57 // guard against that situation.
58 if (m_intervals
["big_maximized"] < m_intervals
["tiny_maximized"])
60 kdDebug() << "max big > max tiny, not allowed & corrected" << endl
;
61 m_intervals
["big_maximized"] = m_intervals
["tiny_maximized"];
65 slotReadConfig( /* restart */ true );
67 m_tiny_left
= m_intervals
["tiny_minimized"];
68 m_big_left
= m_intervals
["big_minimized"];
78 int RSITimer::idleTime()
82 #ifdef HAVE_LIBXSS // Idle detection.
83 XScreenSaverInfo
* _mit_info
;
84 _mit_info
= XScreenSaverAllocInfo();
85 XScreenSaverQueryInfo(qt_xdisplay(), qt_xrootwin(), _mit_info
);
86 totalIdle
= (_mit_info
->idle
/1000);
89 // When dpms turns off the monitor the idle gets a reset to 0
90 // Eat the activity in that area. Bug 6439 bugs.freedesktop.org
91 int t
= m_lastActivity
.secsTo(QDateTime::currentDateTime());
93 // refresh timings when there is idleness for 6 minutes, should be
94 // enough to have values before it is needed.
97 QueryDPMSTimeouts(qt_xdisplay(), dpmsStandby
, dpmsSuspend
, dpmsOff
);
98 kdDebug() << "DPMS settings: " << dpmsStandby
<< " - "
99 << dpmsSuspend
<< " - " << dpmsOff
<< endl
;
102 if (totalIdle
== 0 && ( t
< dpmsOff
-1 || t
> dpmsOff
+1 )
103 && ( t
< dpmsStandby
-1 || t
> dpmsStandby
+1 )
104 && ( t
< dpmsSuspend
-1 || t
> dpmsSuspend
+1 ))
105 m_lastActivity
=QDateTime::currentDateTime();
107 totalIdle
= m_lastActivity
.secsTo(QDateTime::currentDateTime());
110 totalIdle
= m_pause_left
> 0 ? 1 : 0;
111 #endif // HAVE_LIBXSS
116 void RSITimer::breakNow( int t
)
118 emit
updateWidget( t
);
122 void RSITimer::resetAfterBreak()
127 emit
relax( -1, false );
128 updateIdleAvg( 0.0 );
129 m_nextBreak
= m_tiny_left
< m_big_left
? TINY_BREAK
: BIG_BREAK
;
130 // and what about the break after the next break? pass it along relax()
131 // so we can warn the user in advance
132 m_nextnextBreak
= m_nextBreak
== TINY_BREAK
&&
133 m_big_left
<= 2 * m_tiny_left
? BIG_BREAK
: TINY_BREAK
;
136 void RSITimer::resetAfterTinyBreak()
138 m_tiny_left
= m_intervals
["tiny_minimized"];
140 emit
updateToolTip( m_tiny_left
, m_big_left
);
141 RSIGlobals::instance()->DCOPBreak( false, false );
143 if ( m_big_left
< m_tiny_left
)
145 // don't risk a big break just right after a tiny break, so delay it a bit
146 m_big_left
+= m_tiny_left
- m_big_left
;
150 void RSITimer::resetAfterBigBreak()
152 m_tiny_left
= m_intervals
["tiny_minimized"];
153 m_big_left
= m_intervals
["big_minimized"];
155 emit
updateToolTip( m_tiny_left
, m_big_left
);
156 RSIGlobals::instance()->DCOPBreak( false, true );
159 // -------------------------- SLOTS ------------------------//
161 void RSITimer::slotStartNoImage( )
167 void RSITimer::slotStart( bool newImage
)
169 emit
minimize( newImage
);
170 emit
updateIdleAvg( 0.0 );
174 void RSITimer::slotStopNoImage()
179 void RSITimer::slotStop( bool newImage
)
183 emit
minimize( newImage
);
184 emit
updateIdleAvg( 0.0 );
185 emit
updateToolTip( 0, 0 );
188 void RSITimer::slotSuspended( bool b
)
190 m_needRestart
? slotRestart() : (b
? slotStop() : slotStart() );
193 void RSITimer::slotRestart()
195 m_tiny_left
= m_intervals
["tiny_minimized"];
196 m_big_left
= m_intervals
["big_minimized"];
199 m_needRestart
= false;
202 void RSITimer::skipBreak()
204 if ( m_big_left
<= m_tiny_left
)
206 resetAfterBigBreak();
207 RSIGlobals::instance()->stats()->increaseStat( BIG_BREAKS_SKIPPED
);
211 resetAfterTinyBreak();
212 RSIGlobals::instance()->stats()->increaseStat( TINY_BREAKS_SKIPPED
);
217 void RSITimer::slotReadConfig( bool restart
)
221 m_intervals
= RSIGlobals::instance()->intervals();
225 m_needRestart
= true;
228 void RSITimer::slotRequestBreak()
230 m_breakRequested
= true;
233 void RSITimer::slotRequestTinyBreak()
236 if ( !m_bigBreakRequested
)
238 m_tinyBreakRequested
= true;
239 RSIGlobals::instance()->stats()->increaseStat( TINY_BREAKS
);
243 void RSITimer::slotRequestBigBreak()
246 if ( !m_tinyBreakRequested
)
248 RSIGlobals::instance()->stats()->increaseStat( BIG_BREAKS
);
249 m_bigBreakRequested
= true;
253 // ----------------------------- EVENTS -----------------------//
255 void RSITimer::timerEvent( QTimerEvent
* )
257 // Dont change the tray icon when suspended, or evaluate
262 RSIGlobals::instance()->stats()->increaseStat( TOTAL_TIME
);
268 RSIGlobals::instance()->stats()->increaseStat( ACTIVITY
);
269 RSIGlobals::instance()->stats()->setStat( CURRENT_IDLE_TIME
, 0 );
273 RSIGlobals::instance()->stats()->setStat( MAX_IDLENESS
, t
, true );
274 RSIGlobals::instance()->stats()->setStat( CURRENT_IDLE_TIME
, t
);
278 kdDebug() << m_intervals["tiny_maximized"] << " " << m_intervals["big_maximized"] << " " << t << endl;
281 int breakInterval
= m_tiny_left
< m_big_left
?
282 m_intervals
["tiny_maximized"] : m_intervals
["big_maximized"];
285 if ( m_breakRequested
)
287 if ( m_tinyBreakRequested
)
289 breakNow( m_intervals
["tiny_maximized"] );
290 m_pause_left
= m_intervals
["tiny_maximized"];
291 m_nextBreak
= TINY_BREAK
;
292 RSIGlobals::instance()->DCOPBreak( true, false );
294 else if ( m_bigBreakRequested
)
296 breakNow( m_intervals
["big_maximized"] );
297 m_pause_left
= m_intervals
["big_maximized"];
298 m_nextBreak
= BIG_BREAK
;
299 RSIGlobals::instance()->DCOPBreak( true, true );
303 breakNow( breakInterval
);
304 m_pause_left
= breakInterval
;
307 m_breakRequested
= false;
308 m_bigBreakRequested
= false;
309 m_tinyBreakRequested
= false;
313 if ( t
> 0 && m_pause_left
> 0 ) // means: widget is maximized
315 if ( m_pause_left
- 1 > 0 ) // break is not over yet
318 updateWidget( m_pause_left
);
320 else // user survived the break, set him/her free
322 emit
minimize( true );
324 // make sure we clean up stuff in the code ahead
325 if ( m_nextBreak
== TINY_BREAK
)
326 resetAfterTinyBreak();
327 else if ( m_nextBreak
== BIG_BREAK
)
328 resetAfterBigBreak();
330 emit
updateToolTip( m_tiny_left
, m_big_left
);
337 kdDebug() << " patience: " << m_patience << " pause_left: "
338 << m_pause_left << " relax_left: " << m_relax_left
339 << " tiny_left: " << m_tiny_left << " big_left: "
340 << m_big_left << " idle: " << t << endl;
343 if ( t
== 0 ) // activity!
345 if ( m_patience
> 0 ) // we're trying to break
348 if ( m_patience
== 0 ) // that's it!
350 emit
relax( -1, false );
353 breakNow( breakInterval
);
354 m_nextBreak
== TINY_BREAK
?
355 RSIGlobals::instance()->DCOPBreak( true, false ):
356 RSIGlobals::instance()->DCOPBreak( true, true );
357 m_pause_left
= breakInterval
;
359 else // reset relax dialog
361 emit
relax( breakInterval
, m_nextnextBreak
== BIG_BREAK
);
362 m_relax_left
= breakInterval
;
365 else if ( m_relax_left
> 0 )
367 // no patience left and still moving during a relax moment?
368 // this will teach him
369 breakNow( m_relax_left
);
370 m_pause_left
= m_relax_left
;
372 emit
relax( -1, false );
374 else if ( m_pause_left
== 0 )
376 // there's no relax moment or break going on.
378 // If we emitted tiny/bigBreakSkipped then we have
379 // to emit a signal again when user becomes active.
380 // so if the timers are original, emit it.
381 if ( m_tiny_left
== m_intervals
["tiny_minimized"] ||
382 m_big_left
== m_intervals
["big_minimized"] )
384 emit
skipBreakEnded();
390 // This is an extra safeguard. When m_useIdleDetection is false
391 // timers are not reset after the user have had a break. This
392 // will make sure the timers are being reset when this happens.
393 if (m_tiny_left
< -1 || m_big_left
< -1)
395 if ( m_nextBreak
== TINY_BREAK
)
396 resetAfterTinyBreak();
397 else if ( m_nextBreak
== BIG_BREAK
)
398 resetAfterBigBreak();
402 double value
= 100 - ( ( m_tiny_left
/ (double)m_intervals
["tiny_minimized"] ) * 100 );
403 emit
updateIdleAvg( value
);
405 else if ( m_useIdleDetection
&& t
== m_intervals
["big_maximized"] &&
406 m_intervals
["tiny_maximized"] <= m_intervals
["big_maximized"] )
408 // the user was sufficiently idle for a big break
409 if ( m_relax_left
== 0 && m_pause_left
== 0 )
411 RSIGlobals::instance()->stats()->increaseStat( IDLENESS_CAUSED_SKIP_BIG
);
412 RSIGlobals::instance()->stats()->increaseStat( BIG_BREAKS
);
415 resetAfterBigBreak();
416 emit
bigBreakSkipped();
418 else if ( m_useIdleDetection
&& t
== m_intervals
["tiny_maximized"] &&
419 m_tiny_left
< m_big_left
)
421 // the user was sufficiently idle for a tiny break
422 if ( m_relax_left
== 0 && m_pause_left
== 0 )
424 RSIGlobals::instance()->stats()->increaseStat( IDLENESS_CAUSED_SKIP_TINY
);
425 RSIGlobals::instance()->stats()->increaseStat( TINY_BREAKS
);
428 resetAfterTinyBreak();
429 emit
tinyBreakSkipped();
431 else if ( m_relax_left
> 0 )
435 // just in case the user dares to become active
438 emit
relax( m_relax_left
, m_nextnextBreak
== BIG_BREAK
);
441 // update the stats properly when breaking
442 if ( m_useIdleDetection
&& t
> m_intervals
["big_maximized"] &&
443 m_relax_left
== 0 && m_pause_left
== 0 )
445 RSIGlobals::instance()->stats()->setStat( LAST_BIG_BREAK
, QVariant( QDateTime::currentDateTime() ) );
448 // update the stats properly when breaking
449 if ( m_useIdleDetection
&& t
> m_intervals
["tiny_maximized"] &&
450 m_relax_left
== 0 && m_pause_left
== 0 )
452 RSIGlobals::instance()->stats()->setStat( LAST_TINY_BREAK
, QVariant( QDateTime::currentDateTime() ) );
457 if ( m_patience
== 0 && m_pause_left
== 0 && m_relax_left
== 0 &&
458 ( m_tiny_left
== 0 || m_big_left
== 0 ) )
460 if ( m_nextBreak
== TINY_BREAK
)
462 RSIGlobals::instance()->stats()->increaseStat( TINY_BREAKS
);
466 RSIGlobals::instance()->stats()->increaseStat( BIG_BREAKS
);
470 if (m_patience
> breakInterval
)
471 m_patience
=breakInterval
;
473 emit
relax( breakInterval
, m_nextnextBreak
== BIG_BREAK
);
474 m_relax_left
= breakInterval
;
477 emit
updateToolTip( m_tiny_left
, m_big_left
);
480 //--------------------------- CONFIG ----------------------------//
482 void RSITimer::readConfig()
484 KConfig
* config
= kapp
->config();
486 config
->setGroup("General Settings");
488 m_useIdleDetection
= config
->readBoolEntry("UseIdleDetection", true);
490 config
->setGroup("General");
491 QDateTime
*tempDt
= new QDateTime();
492 m_lastrunDt
= config
->readDateTimeEntry( "LastRunTimeStamp", tempDt
);
493 m_lastrunTiny
= config
->readNumEntry( "LastRunTinyLeft", 0 );
494 m_lastrunBig
= config
->readNumEntry( "LastRunBigLeft", 0 );
500 void RSITimer::writeConfig()
502 KConfig
*config
= kapp
->config();
504 config
->setGroup("General");
505 config
->writeEntry( "LastRunTimeStamp", QDateTime::currentDateTime() );
506 config
->writeEntry( "LastRunTinyLeft", m_tiny_left
);
507 config
->writeEntry( "LastRunBigLeft", m_big_left
);
510 void RSITimer::restoreSession()
512 if ( !m_lastrunDt
.isNull() )
514 int between
= m_lastrunDt
.secsTo( QDateTime::currentDateTime() );
516 if ( between
< m_intervals
["big_minimized"] &&
517 (m_lastrunBig
- between
) > 20 )
519 m_big_left
= m_lastrunBig
- between
;
522 if ( between
< m_intervals
["tiny_minimized"] &&
523 (m_lastrunTiny
- between
) > 20 )
525 m_tiny_left
= m_lastrunTiny
- between
;
532 RSITimerNoIdle::RSITimerNoIdle( QObject
*parent
, const char *name
)
533 : RSITimer( parent
, name
)
535 kdDebug() << "Starting noIdle timer" << endl
;
538 RSITimerNoIdle::~RSITimerNoIdle()
542 void RSITimerNoIdle::timerEvent( QTimerEvent
* )
544 // Dont change the tray icon when suspended, or evaluate
549 RSIGlobals::instance()->stats()->increaseStat( TOTAL_TIME
);
550 RSIGlobals::instance()->stats()->increaseStat( ACTIVITY
);
552 int breakInterval
= m_tiny_left
< m_big_left
?
553 m_intervals
["tiny_maximized"] : m_intervals
["big_maximized"];
555 if ( m_breakRequested
)
557 if ( m_tinyBreakRequested
)
559 breakNow( m_intervals
["tiny_maximized"] );
560 m_pause_left
= m_intervals
["tiny_maximized"];
561 m_nextBreak
= TINY_BREAK
;
562 RSIGlobals::instance()->DCOPBreak( true, false );
564 else if ( m_bigBreakRequested
)
566 breakNow( m_intervals
["big_maximized"] );
567 m_pause_left
= m_intervals
["big_maximized"];
568 m_nextBreak
= BIG_BREAK
;
569 RSIGlobals::instance()->DCOPBreak( true, true );
572 breakNow( breakInterval
);
573 m_pause_left
= breakInterval
;
574 m_breakRequested
= false;
575 m_bigBreakRequested
= false;
576 m_tinyBreakRequested
= false;
581 kdDebug() << " patience: " << m_patience << " pause_left: "
582 << m_pause_left << " relax_left: " << m_relax_left
583 << " tiny_left: " << m_tiny_left << " big_left: "
584 << m_big_left << endl;
587 if ( m_pause_left
> 0 )
590 if ( m_pause_left
== 0 )
593 emit
minimize( true );
594 emit
relax( -1, false );
595 if ( m_nextBreak
== TINY_BREAK
)
597 resetAfterTinyBreak();
599 else if ( m_nextBreak
== BIG_BREAK
)
601 resetAfterBigBreak();
604 m_nextBreak
= NO_BREAK
;
608 emit
updateWidget( m_pause_left
);
612 if ( m_pause_left
== 0 && m_tiny_left
== 0 )
614 emit
relax( breakInterval
, m_nextnextBreak
== BIG_BREAK
);
615 m_pause_left
= breakInterval
;
616 m_nextBreak
= TINY_BREAK
;
617 breakNow( breakInterval
);
618 RSIGlobals::instance()->stats()->setStat( LAST_TINY_BREAK
,
619 QVariant( QDateTime::currentDateTime() ) );
620 RSIGlobals::instance()->DCOPBreak( true, false );
621 RSIGlobals::instance()->stats()->increaseStat( TINY_BREAKS
);
628 if ( m_pause_left
== 0 && m_big_left
== 0 )
630 emit
relax( breakInterval
, m_nextnextBreak
== BIG_BREAK
);
631 m_pause_left
= breakInterval
;
632 m_nextBreak
= BIG_BREAK
;
633 breakNow( breakInterval
);
634 RSIGlobals::instance()->stats()->setStat( LAST_BIG_BREAK
,
635 QVariant( QDateTime::currentDateTime() ) );
636 RSIGlobals::instance()->DCOPBreak( true, true );
637 RSIGlobals::instance()->stats()->increaseStat( BIG_BREAKS
);
644 double value
= 100 - ( ( m_tiny_left
/ (double)m_intervals
["tiny_minimized"] ) * 100 );
645 emit
updateIdleAvg( value
);
647 emit
updateToolTip( m_tiny_left
, m_big_left
);
650 #include "rsitimer.moc"