More work on the noIdleTimer:
[rsibreak.git] / src / rsitimer.cpp
blobf10d81eca8dfa3033687ece9de9f752e89f46cca
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>
24 #include <kdebug.h>
25 #include <kconfig.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.
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
33 #include <X11/extensions/scrnsaver.h>
34 #include <fixx11h.h>
35 #endif // HAVE_LIBXSS
37 #include "rsiglobals.h"
38 #include "rsistats.h"
39 #include "rsitimer.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"];
64 startTimer( 1000 );
65 slotReadConfig( /* restart */ true );
67 m_tiny_left = m_intervals["tiny_minimized"];
68 m_big_left = m_intervals["big_minimized"];
70 restoreSession();
73 RSITimer::~RSITimer()
75 writeConfig();
78 int RSITimer::idleTime()
80 int totalIdle = 0;
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);
87 XFree(_mit_info);
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.
95 if (t == 3600)
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();
106 else
107 totalIdle = m_lastActivity.secsTo(QDateTime::currentDateTime());
109 #else
110 totalIdle = m_pause_left > 0 ? 1 : 0;
111 #endif // HAVE_LIBXSS
113 return totalIdle;
116 void RSITimer::breakNow( int t )
118 emit updateWidget( t );
119 emit breakNow();
122 void RSITimer::resetAfterBreak()
124 m_pause_left = 0;
125 m_relax_left = 0;
126 m_patience = 0;
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"];
139 resetAfterBreak();
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"];
154 resetAfterBreak();
155 emit updateToolTip( m_tiny_left, m_big_left );
156 RSIGlobals::instance()->DCOPBreak( false, true );
159 // -------------------------- SLOTS ------------------------//
161 void RSITimer::slotStartNoImage( )
163 slotStart( false );
167 void RSITimer::slotStart( bool newImage )
169 emit minimize( newImage );
170 emit updateIdleAvg( 0.0 );
171 m_suspended = false;
174 void RSITimer::slotStopNoImage()
176 slotStop( false );
179 void RSITimer::slotStop( bool newImage )
181 m_suspended = true;
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"];
197 resetAfterBreak();
198 slotStart( false );
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 );
209 else
211 resetAfterTinyBreak();
212 RSIGlobals::instance()->stats()->increaseStat( TINY_BREAKS_SKIPPED );
214 slotStart();
217 void RSITimer::slotReadConfig( bool restart )
219 readConfig();
221 m_intervals = RSIGlobals::instance()->intervals();
222 if ( restart )
223 slotRestart();
224 else
225 m_needRestart = true;
228 void RSITimer::slotRequestBreak()
230 m_breakRequested = true;
233 void RSITimer::slotRequestTinyBreak()
235 slotRequestBreak();
236 if ( !m_bigBreakRequested )
238 m_tinyBreakRequested = true;
239 RSIGlobals::instance()->stats()->increaseStat( TINY_BREAKS );
243 void RSITimer::slotRequestBigBreak()
245 slotRequestBreak();
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
258 // a possible break.
259 if ( m_suspended )
260 return;
262 RSIGlobals::instance()->stats()->increaseStat( TOTAL_TIME );
264 int t = idleTime();
266 if ( t == 0 )
268 RSIGlobals::instance()->stats()->increaseStat( ACTIVITY );
269 RSIGlobals::instance()->stats()->setStat( CURRENT_IDLE_TIME, 0 );
271 else
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 );
301 else
303 breakNow( breakInterval );
304 m_pause_left = breakInterval;
307 m_breakRequested = false;
308 m_bigBreakRequested = false;
309 m_tinyBreakRequested = false;
310 m_relax_left = 0;
313 if ( t > 0 && m_pause_left > 0 ) // means: widget is maximized
315 if ( m_pause_left - 1 > 0 ) // break is not over yet
317 --m_pause_left;
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 );
333 return;
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
347 --m_patience;
348 if ( m_patience == 0 ) // that's it!
350 emit relax( -1, false );
351 m_relax_left = 0;
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;
371 m_relax_left = 0;
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();
387 --m_tiny_left;
388 --m_big_left;
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 )
433 --m_relax_left;
435 // just in case the user dares to become active
436 --m_patience;
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() ) );
456 // show relax popup
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 );
464 else
466 RSIGlobals::instance()->stats()->increaseStat( BIG_BREAKS );
469 m_patience = 15;
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 );
496 delete tempDt;
497 tempDt = 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
545 // a possible break.
546 if ( m_suspended )
547 return;
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;
577 m_relax_left = 0;
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 )
589 --m_pause_left;
590 if ( m_pause_left == 0 )
592 // break is over
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;
606 else
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 );
623 else
625 --m_tiny_left;
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 );
639 else
641 --m_big_left;
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"