1 // Statistics.cc
2 //
3 // Copyright (C) 2002 - 2008, 2010 Rob Caelers & Raymond Penners
4 // All rights reserved.
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 //
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #ifdef PLATFORM_OS_OSX
25 #include "OSXHelpers.hh"
26 #endif
27 
28 #include <cstring>
29 #include <sstream>
30 #include <assert.h>
31 #include <math.h>
32 
33 #include "debug.hh"
34 
35 #include "Statistics.hh"
36 
37 #include "Core.hh"
38 #include "Util.hh"
39 #include "Timer.hh"
40 #include "TimePred.hh"
41 #include "InputMonitorFactory.hh"
42 #include "IInputMonitor.hh"
43 #include "timeutil.h"
44 
45 #ifdef HAVE_DISTRIBUTION
46 #include "DistributionManager.hh"
47 #endif
48 
49 const char *WORKRAVESTATS="WorkRaveStats";
50 const int STATSVERSION = 4;
51 
52 #define MAX_JUMP (10000)
53 
54 //! Constructor
Statistics()55 Statistics::Statistics() :
56   core(NULL),
57   current_day(NULL),
58   been_active(false),
59   prev_x(-1),
60   prev_y(-1),
61   click_x(-1),
62   click_y(-1)
63 {
64    last_mouse_time = 0;
65 }
66 
67 
68 //! Destructor
~Statistics()69 Statistics::~Statistics()
70 {
71   update();
72 
73   for (vector<DailyStatsImpl *>::iterator i = history.begin(); i != history.end(); i++)
74     {
75       delete *i;
76     }
77 
78   delete current_day;
79 
80   if (input_monitor != NULL)
81     {
82       input_monitor->unsubscribe_statistics(this);
83     }
84 }
85 
86 
87 //! Initializes the Statistics.
88 void
init(Core * control)89 Statistics::init(Core *control)
90 {
91   core = control;
92 
93   input_monitor = InputMonitorFactory::get_monitor(IInputMonitorFactory::CAPABILITY_STATISTICS);
94   if (input_monitor != NULL)
95     {
96       input_monitor->subscribe_statistics(this);
97     }
98 
99 #ifdef HAVE_DISTRIBUTION
100   init_distribution_manager();
101 #endif
102 
103   current_day = NULL;
104   bool ok = load_current_day();
105   if (!ok)
106     {
107       start_new_day();
108     }
109 
110   load_history();
111 }
112 
113 
114 //! Periodic heartbeat.
115 void
update()116 Statistics::update()
117 {
118   TRACE_ENTER("Statistics::update");
119 
120   IActivityMonitor *monitor = core->get_activity_monitor();
121   ActivityState state = monitor->get_current_state();
122 
123   if (state == ACTIVITY_ACTIVE && !been_active)
124     {
125       const time_t now = core->get_time();
126       struct tm *tmnow = localtime(&now);
127 
128       current_day->start = *tmnow;
129       current_day->stop = *tmnow;
130 
131       been_active = true;
132     }
133 
134   update_current_day(state == ACTIVITY_ACTIVE);
135   save_day(current_day);
136   TRACE_EXIT();
137 }
138 
139 
140 bool
delete_all_history()141 Statistics::delete_all_history()
142 {
143     update();
144 
145     string histfile = Util::get_home_directory() + "historystats";
146     if( Util::file_exists( histfile.c_str() ) && std::remove( histfile.c_str() ) )
147     {
148         return false;
149     }
150     else
151     {
152         for( vector<DailyStatsImpl *>::iterator i = history.begin(); ( i != history.end() ); delete *i++ )
153             ;
154 
155         history.clear();
156     }
157 
158     string todayfile = Util::get_home_directory() + "todaystats";
159     if( Util::file_exists( todayfile.c_str() ) && std::remove( todayfile.c_str() ) )
160     {
161         return false;
162     }
163     else
164     {
165         if( current_day )
166         {
167             delete current_day;
168             current_day = NULL;
169         }
170         start_new_day();
171    }
172 
173     return true;
174 }
175 
176 
177 //! Starts a new day and archive current any (if exists)
178 void
start_new_day()179 Statistics::start_new_day()
180 {
181   TRACE_ENTER("Statistics::start_new_day");
182   const time_t now = core->get_time();
183   struct tm *tmnow = localtime(&now);
184 
185   if (current_day == NULL ||
186       tmnow->tm_mday !=  current_day->start.tm_mday ||
187       tmnow->tm_mon  !=  current_day->start.tm_mon  ||
188       tmnow->tm_year !=  current_day->start.tm_year
189       )
190     {
191       TRACE_MSG("New day");
192       if (current_day != NULL)
193         {
194           TRACE_MSG("Save old day");
195           day_to_history(current_day);
196           day_to_remote_history(current_day);
197         }
198 
199       current_day = new DailyStatsImpl();
200       been_active = false;
201 
202       current_day->start = *tmnow;
203       current_day->stop = *tmnow;
204     }
205 
206   update_current_day(false);
207   save_day(current_day);
208 
209   TRACE_EXIT();
210 }
211 
212 
213 void
day_to_history(DailyStatsImpl * stats)214 Statistics::day_to_history(DailyStatsImpl *stats)
215 {
216   add_history(stats);
217 
218   stringstream ss;
219   ss << Util::get_home_directory();
220   ss << "historystats" << ends;
221 
222   bool exists = Util::file_exists(ss.str());
223   ofstream stats_file(ss.str().c_str(), ios::app);
224 
225   if (!exists)
226     {
227       stats_file << WORKRAVESTATS << " " << STATSVERSION  << endl;
228     }
229 
230   save_day(stats, stats_file);
231   stats_file.close();
232 
233 }
234 
235 
236 //! Adds the current day to this history.
237 void
day_to_remote_history(DailyStatsImpl * stats)238 Statistics::day_to_remote_history(DailyStatsImpl *stats)
239 {
240 #ifdef HAVE_DISTRIBUTION
241   DistributionManager *dist_manager = core->get_distribution_manager();
242 
243   if (dist_manager != NULL)
244     {
245       PacketBuffer state_packet;
246       state_packet.create();
247 
248       state_packet.pack_byte(STATS_MARKER_HISTORY);
249       pack_stats(state_packet, stats);
250       state_packet.pack_byte(STATS_MARKER_END);
251 
252       dist_manager->broadcast_client_message(DCM_STATS, state_packet);
253     }
254 #else
255   (void) stats;
256 #endif
257 }
258 
259 
260 //! Saves the current day to the specified stream.
261 void
save_day(DailyStatsImpl * stats,ofstream & stats_file)262 Statistics::save_day(DailyStatsImpl *stats, ofstream &stats_file)
263 {
264   stats_file << "D "
265              << stats->start.tm_mday << " "
266              << stats->start.tm_mon << " "
267              << stats->start.tm_year << " "
268              << stats->start.tm_hour << " "
269              << stats->start.tm_min << " "
270              << stats->stop.tm_mday << " "
271              << stats->stop.tm_mon << " "
272              << stats->stop.tm_year << " "
273              << stats->stop.tm_hour << " "
274              << stats->stop.tm_min <<  endl;
275 
276   for(int i = 0; i < BREAK_ID_SIZEOF; i++)
277     {
278       BreakStats &bs = stats->break_stats[i];
279 
280       stats_file << "B " << i << " " << STATS_BREAKVALUE_SIZEOF << " ";
281       for(int j = 0; j < STATS_BREAKVALUE_SIZEOF; j++)
282         {
283           stats_file << bs[j] << " ";
284         }
285       stats_file << endl;
286     }
287 
288   stats_file << "m " << STATS_VALUE_SIZEOF << " ";
289   for(int j = 0; j < STATS_VALUE_SIZEOF; j++)
290     {
291       stats_file << stats->misc_stats[j] << " ";
292     }
293   stats_file << endl;
294 
295   stats_file.close();
296 }
297 
298 
299 //! Saves the statistics of the specified day.
300 void
save_day(DailyStatsImpl * stats)301 Statistics::save_day(DailyStatsImpl *stats)
302 {
303   stringstream ss;
304   ss << Util::get_home_directory();
305   ss << "todaystats" << ends;
306 
307   ofstream stats_file(ss.str().c_str());
308 
309   stats_file << WORKRAVESTATS << " " << STATSVERSION  << endl;
310 
311   save_day(stats, stats_file);
312 }
313 
314 
315 //! Add the stats the the history list.
316 void
add_history(DailyStatsImpl * stats)317 Statistics::add_history(DailyStatsImpl *stats)
318 {
319   if (history.size() == 0)
320     {
321       history.push_back(stats);
322     }
323   else
324     {
325       bool found = false;
326       HistoryRIter i = history.rbegin();
327       while (i != history.rend())
328         {
329           DailyStatsImpl *ref = *i;
330 
331           if (stats->start.tm_year == ref->start.tm_year  &&
332               stats->start.tm_mon == ref->start.tm_mon &&
333               stats->start.tm_mday == ref->start.tm_mday)
334             {
335               delete *i;
336               *i = stats;
337               found = true;
338               break;
339             }
340 
341           else if ( stats->start.tm_year > ref->start.tm_year
342                     || (stats->start.tm_year == ref->start.tm_year
343                         && (stats->start.tm_mon > ref->start.tm_mon
344                             || (stats->start.tm_mon == ref->start.tm_mon
345                                 && stats->start.tm_mday > ref->start.tm_mday))))
346           {
347             if (i == history.rbegin())
348               {
349                 history.push_back(stats);
350               }
351             else
352               {
353                 history.insert(i.base(), stats);
354               }
355             found = true;
356             break;
357           }
358           i++;
359         }
360 
361       if (!found)
362         {
363           history.insert(history.begin(), stats);
364         }
365     }
366 }
367 
368 //! Load the statistics of the current day.
369 bool
load_current_day()370 Statistics::load_current_day()
371 {
372   TRACE_ENTER("Statistics::load_current_day");
373   stringstream ss;
374   ss << Util::get_home_directory();
375   ss << "todaystats" << ends;
376 
377   ifstream stats_file(ss.str().c_str());
378 
379   load(stats_file, false);
380 
381   been_active = true;
382 
383   TRACE_EXIT();
384   return current_day != NULL;
385 }
386 
387 
388 //! Loads the history.
389 void
load_history()390 Statistics::load_history()
391 {
392   TRACE_ENTER("Statistics::load_history");
393 
394   stringstream ss;
395   ss << Util::get_home_directory();
396   ss << "historystats" << ends;
397 
398   ifstream stats_file(ss.str().c_str());
399 
400   load(stats_file, true);
401   TRACE_EXIT();
402 }
403 
404 
405 //! Loads the statistics.
406 void
load(ifstream & infile,bool history)407 Statistics::load(ifstream &infile, bool history)
408 {
409   TRACE_ENTER("Statistics::load");
410 
411   DailyStatsImpl *stats = NULL;
412 
413   bool ok = infile.good();
414 
415   if (ok)
416     {
417       string tag;
418       infile >> tag;
419 
420       ok = (tag == WORKRAVESTATS);
421     }
422 
423   if (ok)
424     {
425       int version;
426       infile >> version;
427 
428       ok = (version == STATSVERSION) || (version == 3);
429     }
430 
431 
432   while (ok && !infile.eof())
433     {
434       char line[BUFSIZ] = "";
435       char cmd;
436 
437       infile.getline(line, BUFSIZ);
438 
439       if (strlen(line) > 1)
440         {
441           cmd = line[0];
442 
443           stringstream ss(line+1);
444 
445           if (cmd == 'D')
446             {
447               if (history && stats != NULL)
448                 {
449                   add_history(stats);
450                   stats = NULL;
451                 }
452               else if (!history && stats != NULL)
453                 {
454                   /* Corrupt today stats */
455                   return;
456                 }
457 
458               stats = new DailyStatsImpl();
459 
460               ss >> stats->start.tm_mday
461                  >> stats->start.tm_mon
462                  >> stats->start.tm_year
463                  >> stats->start.tm_hour
464                  >> stats->start.tm_min
465                  >> stats->stop.tm_mday
466                  >> stats->stop.tm_mon
467                  >> stats->stop.tm_year
468                  >> stats->stop.tm_hour
469                  >> stats->stop.tm_min;
470 
471               if (!history)
472                 {
473                   current_day = stats;
474                 }
475             }
476           else if (stats != NULL)
477             {
478               if (cmd == 'B')
479                 {
480                   int bt, size;
481                   ss >> bt;
482                   ss >> size;
483 
484                   BreakStats &bs = stats->break_stats[bt];
485 
486                   if (size > STATS_BREAKVALUE_SIZEOF)
487                     {
488                       size = STATS_BREAKVALUE_SIZEOF;
489                     }
490 
491                   for(int j = 0; j < size; j++)
492                     {
493                       int value;
494                       ss >> value;
495 
496                       bs[j] = value;
497                     }
498                 }
499               else if (cmd == 'M' || cmd == 'm')
500                 {
501                   int size;
502                   ss >> size;
503 
504                   if (size > STATS_VALUE_SIZEOF)
505                     {
506                       size = STATS_VALUE_SIZEOF;
507                     }
508 
509                   for(int j = 0; j < size; j++)
510                     {
511                       int value;
512                       ss >> value;
513 
514                       if (cmd == 'm')
515                         {
516                           // Ignore older 'M' stats. they are broken....
517                           stats->misc_stats[j] = value;
518                         }
519                       else
520                         {
521                           stats->misc_stats[j] = 0;
522                         }
523                     }
524                 }
525               else if (cmd == 'G')
526                 {
527                   int total_active;
528                   ss >> total_active;
529 
530                   stats->misc_stats[STATS_VALUE_TOTAL_ACTIVE_TIME] = total_active;
531                 }
532             }
533         }
534     }
535 
536   if (history && stats != NULL)
537     {
538       add_history(stats);
539     }
540 
541   TRACE_EXIT();
542 }
543 
544 
545 //! Increment the specified statistics counter of the current day.
546 void
increment_break_counter(BreakId bt,StatsBreakValueType st)547 Statistics::increment_break_counter(BreakId bt, StatsBreakValueType st)
548 {
549   if (current_day == NULL)
550     {
551       start_new_day();
552     }
553 
554   BreakStats &bs = current_day->break_stats[bt];
555   bs[st]++;
556 }
557 
558 void
set_break_counter(BreakId bt,StatsBreakValueType st,int value)559 Statistics::set_break_counter(BreakId bt, StatsBreakValueType st, int value)
560 {
561   if (current_day == NULL)
562     {
563       start_new_day();
564     }
565 
566   BreakStats &bs = current_day->break_stats[bt];
567   bs[st] = value;
568 }
569 
570 
571 void
add_break_counter(BreakId bt,StatsBreakValueType st,int value)572 Statistics::add_break_counter(BreakId bt, StatsBreakValueType st, int value)
573 {
574   if (current_day == NULL)
575     {
576       start_new_day();
577     }
578 
579   BreakStats &bs = current_day->break_stats[bt];
580   bs[st] += value;
581 }
582 
583 void
set_counter(StatsValueType t,int value)584 Statistics::set_counter(StatsValueType t, int value)
585 {
586   current_day->misc_stats[t] = value;
587 }
588 
589 
590 int64_t
get_counter(StatsValueType t)591 Statistics::get_counter(StatsValueType t)
592 {
593   return current_day->misc_stats[t];
594 }
595 
596 
597 //! Dump
598 void
dump()599 Statistics::dump()
600 {
601   TRACE_ENTER("Statistics::dump");
602 
603   update_current_day(false);
604 
605   stringstream ss;
606   for(int i = 0; i < BREAK_ID_SIZEOF; i++)
607     {
608       BreakStats &bs = current_day->break_stats[i];
609 
610       ss << "Break " << i << " ";
611       for(int j = 0; j < STATS_BREAKVALUE_SIZEOF; j++)
612         {
613           int value = bs[j];
614 
615           ss << value << " ";
616         }
617     }
618 
619   ss << "stats ";
620   for(int j = 0; j < STATS_VALUE_SIZEOF; j++)
621     {
622       int64_t value = current_day->misc_stats[j];
623 
624       ss  << value << " ";
625     }
626 
627   TRACE_EXIT();
628 }
629 
630 
631 Statistics::DailyStatsImpl *
get_current_day() const632 Statistics::get_current_day() const
633 {
634   return current_day;
635 }
636 
637 
638 Statistics::DailyStatsImpl *
get_day(int day) const639 Statistics::get_day(int day) const
640 {
641   DailyStatsImpl *ret = NULL;
642 
643   if (day == 0)
644     {
645       ret = current_day;
646     }
647   else
648     {
649       if (day > 0)
650         {
651           day = history.size() - day;
652         }
653       else
654         {
655           day = -day;
656           day--;
657         }
658 
659       if (day < int(history.size()) && day >= 0)
660         {
661           ret = history[day];
662         }
663     }
664 
665   return ret;
666 }
667 
668 void
get_day_index_by_date(int y,int m,int d,int & idx,int & next,int & prev) const669 Statistics::get_day_index_by_date(int y, int m, int d,
670                                   int &idx, int &next, int &prev) const
671 {
672   TRACE_ENTER_MSG("Statistics::get_day_by_date", y << "/" << m << "/" << d);
673   idx = next = prev = -1;
674   for (int i = 0; i <= int(history.size()); i++)
675     {
676       int j = history.size() - i;
677       DailyStatsImpl *stats = j == 0 ? current_day : history[i];
678       if (idx < 0 && stats->starts_at_date(y, m, d))
679         {
680           idx = j;
681         }
682       else if (stats->starts_before_date(y, m, d))
683         {
684           prev = j;
685         }
686       else if (next < 0)
687         {
688           next = j;
689         }
690     }
691 
692   if (prev < 0 && current_day->starts_before_date(y, m, d))
693     {
694       prev = 0;
695     }
696   if (next < 0 && !current_day->starts_at_date(y, m, d)
697       && !current_day->starts_before_date(y, m, d))
698     {
699       next = 0;
700     }
701   TRACE_EXIT();
702 }
703 
704 
705 int
get_history_size() const706 Statistics::get_history_size() const
707 {
708   return history.size();
709 }
710 
711 
712 
713 void
update_current_day(bool active)714 Statistics::update_current_day(bool active)
715 {
716   if (core != NULL)
717     {
718       // Collect total active time from dialy limit timer.
719       Timer *t = core->get_break(BREAK_ID_DAILY_LIMIT)->get_timer();
720       assert(t != NULL);
721       current_day->misc_stats[STATS_VALUE_TOTAL_ACTIVE_TIME] = (int)t->get_elapsed_time();
722 
723       if (active)
724         {
725           const time_t now = core->get_time();
726           struct tm *tmnow = localtime(&now);
727           current_day->stop = *tmnow;
728         }
729 
730       for (int i = 0; i < BREAK_ID_SIZEOF; i++)
731         {
732           Timer *t = core->get_break(BreakId(i))->get_timer();
733           assert(t != NULL);
734 
735           time_t overdue = t->get_total_overdue_time();
736 
737           set_break_counter(((BreakId)i),
738                             Statistics::STATS_BREAKVALUE_TOTAL_OVERDUE, (int)overdue);
739         }
740 
741 
742     }
743 }
744 
745 
746 #ifdef HAVE_DISTRIBUTION
747 // Create the monitor based on the specified configuration.
748 void
init_distribution_manager()749 Statistics::init_distribution_manager()
750 {
751   DistributionManager *dist_manager = core->get_distribution_manager();
752 
753   if (dist_manager != NULL)
754     {
755       dist_manager->register_client_message(DCM_STATS, DCMT_MASTER, this);
756     }
757 }
758 
759 bool
request_client_message(DistributionClientMessageID id,PacketBuffer & buffer)760 Statistics::request_client_message(DistributionClientMessageID id, PacketBuffer &buffer)
761 {
762   TRACE_ENTER("Statistics::request_client_message");
763   (void) id;
764 
765   update_current_day(false);
766   dump();
767 
768   buffer.pack_byte(STATS_MARKER_TODAY);
769   pack_stats(buffer, current_day);
770   buffer.pack_byte(STATS_MARKER_END);
771 
772   TRACE_EXIT();
773   return true;
774 }
775 
776 
777 bool
pack_stats(PacketBuffer & buf,const DailyStatsImpl * stats)778 Statistics::pack_stats(PacketBuffer &buf, const DailyStatsImpl *stats)
779 {
780   TRACE_ENTER("Statistics::pack_stats");
781 
782   int pos = 0;
783 
784   buf.pack_byte(STATS_MARKER_STARTTIME);
785   buf.reserve_size(pos);
786   buf.pack_byte(stats->start.tm_mday);
787   buf.pack_byte(stats->start.tm_mon);
788   buf.pack_ushort(stats->start.tm_year);
789   buf.pack_byte(stats->start.tm_hour);
790   buf.pack_byte(stats->start.tm_min);
791   buf.update_size(pos);
792 
793   buf.pack_byte(STATS_MARKER_STOPTIME);
794   buf.reserve_size(pos);
795   buf.pack_byte(stats->stop.tm_mday);
796   buf.pack_byte(stats->stop.tm_mon);
797   buf.pack_ushort(stats->stop.tm_year);
798   buf.pack_byte(stats->stop.tm_hour);
799   buf.pack_byte(stats->stop.tm_min);
800   buf.update_size(pos);
801 
802   for(int i = 0; i < BREAK_ID_SIZEOF; i++)
803     {
804       BreakStats &bs = current_day->break_stats[i];
805 
806       buf.pack_byte(STATS_MARKER_BREAK_STATS);
807       buf.reserve_size(pos);
808       buf.pack_byte(i);
809       buf.pack_ushort(STATS_BREAKVALUE_SIZEOF);
810 
811       for(int j = 0; j < STATS_BREAKVALUE_SIZEOF; j++)
812         {
813           buf.pack_ulong(bs[j]);
814         }
815       buf.update_size(pos);
816     }
817 
818   buf.pack_byte(STATS_MARKER_MISC_STATS);
819   buf.reserve_size(pos);
820   buf.pack_ushort(STATS_VALUE_SIZEOF);
821 
822   for(int j = 0; j < STATS_VALUE_SIZEOF; j++)
823     {
824       buf.pack_ulong(current_day->misc_stats[j]);
825     }
826   buf.update_size(pos);
827 
828   TRACE_EXIT();
829   return true;
830 }
831 
832 bool
client_message(DistributionClientMessageID id,bool master,const char * client_id,PacketBuffer & buffer)833 Statistics::client_message(DistributionClientMessageID id, bool master, const char *client_id,
834                            PacketBuffer &buffer)
835 {
836   TRACE_ENTER("Statistics::client_message");
837 
838   (void) id;
839   (void) master;
840   (void) client_id;
841 
842   return false;
843 
844   DailyStatsImpl *stats = NULL;
845   int pos = 0;
846   bool stats_to_history = false;
847 
848   while (buffer.bytes_available() > 0)
849     {
850       StatsMarker marker = (StatsMarker) buffer.unpack_byte();
851       TRACE_MSG("Marker = " << marker);
852       switch (marker)
853         {
854         case STATS_MARKER_TODAY:
855           stats = current_day;
856           break;
857 
858         case STATS_MARKER_HISTORY:
859           stats = new DailyStatsImpl();
860           stats_to_history = true;
861           break;
862 
863         case STATS_MARKER_STARTTIME:
864           {
865             int size = buffer.unpack_ushort();
866             (void) size;
867 
868             stats->start.tm_mday = buffer.unpack_byte();
869             stats->start.tm_mon = buffer.unpack_byte();
870             stats->start.tm_year = buffer.unpack_ushort();
871             stats->start.tm_hour = buffer.unpack_byte();
872             stats->start.tm_min = buffer.unpack_byte();
873           }
874           break;
875 
876         case STATS_MARKER_STOPTIME:
877           {
878             int size = buffer.unpack_ushort();
879             (void) size;
880 
881             stats->stop.tm_mday = buffer.unpack_byte();
882             stats->stop.tm_mon = buffer.unpack_byte();
883             stats->stop.tm_year = buffer.unpack_ushort();
884             stats->stop.tm_hour = buffer.unpack_byte();
885             stats->stop.tm_min = buffer.unpack_byte();
886           }
887           break;
888 
889         case STATS_MARKER_BREAK_STATS:
890           {
891             int size = buffer.read_size(pos);
892             int bt = buffer.unpack_byte();
893             (void) size;
894 
895             BreakStats &bs = stats->break_stats[bt];
896 
897             int count = buffer.unpack_ushort();
898 
899             if (count > STATS_BREAKVALUE_SIZEOF)
900               {
901                 count = STATS_BREAKVALUE_SIZEOF;
902               }
903 
904             for(int j = 0; j < count; j++)
905               {
906                 bs[j] = buffer.unpack_ulong();
907               }
908 
909             buffer.skip_size(pos);
910           }
911           break;
912 
913         case STATS_MARKER_MISC_STATS:
914           {
915             int size = buffer.read_size(pos);
916             int count = buffer.unpack_ushort();
917             (void) size;
918 
919             if (count > STATS_VALUE_SIZEOF)
920               {
921                 count = STATS_VALUE_SIZEOF;
922               }
923 
924             for(int j = 0; j < count; j++)
925               {
926                 stats->misc_stats[j] = buffer.unpack_ulong();
927               }
928 
929             buffer.skip_size(pos);
930           }
931           break;
932 
933         case STATS_MARKER_END:
934           if (stats_to_history)
935             {
936               TRACE_MSG("Save to history");
937               day_to_history(stats);
938               stats_to_history = false;
939             }
940           break;
941 
942         default:
943           {
944             TRACE_MSG("Unknown marker");
945             int size = buffer.read_size(pos);
946             (void) size;
947             buffer.skip_size(pos);
948           }
949         }
950     }
951 
952   if (stats_to_history)
953     {
954       // this should not happend. but just to avoid a potential memory leak...
955       TRACE_MSG("Save to history");
956       day_to_history(stats);
957       stats_to_history = false;
958     }
959 
960   dump();
961 
962   TRACE_EXIT();
963   return true;
964 }
965 
966 #endif
967 
968 bool
starts_at_date(int y,int m,int d)969 Statistics::DailyStatsImpl::starts_at_date(int y, int m, int d)
970 {
971   return (start.tm_year + 1900 == y
972           && start.tm_mon + 1 == m
973           && start.tm_mday == d);
974 }
975 
976 bool
starts_before_date(int y,int m,int d)977 Statistics::DailyStatsImpl::starts_before_date(int y, int m, int d)
978 {
979   return (start.tm_year + 1900 < y
980           || (start.tm_year + 1900 == y
981               && (start.tm_mon + 1 < m
982                   || (start.tm_mon + 1 == m
983                       && start.tm_mday < d))));
984 }
985 
986 
987 //! Activity is reported by the input monitor.
988 void
action_notify()989 Statistics::action_notify()
990 {
991 }
992 
993 
994 //! Mouse activity is reported by the input monitor.
995 void
mouse_notify(int x,int y,int wheel_delta)996 Statistics::mouse_notify(int x, int y, int wheel_delta)
997 {
998   static const int sensitivity = 3;
999 
1000   lock.lock();
1001 
1002   if (current_day != NULL && x >=0 && y >= 0)
1003     {
1004       int delta_x = sensitivity;
1005       int delta_y = sensitivity;
1006 
1007       if (prev_x != -1 && prev_y != -1)
1008         {
1009           delta_x = abs(x - prev_x);
1010           delta_y = abs(y - prev_y);
1011         }
1012 
1013       prev_x = x;
1014       prev_y = y;
1015 
1016       // Sanity checks, ignore unreasonable large jumps...
1017       if ( delta_x < MAX_JUMP && delta_y < MAX_JUMP &&
1018           (delta_x >= sensitivity || delta_y >= sensitivity || wheel_delta != 0 ))
1019         {
1020           int64_t movement = current_day->misc_stats[STATS_VALUE_TOTAL_MOUSE_MOVEMENT];
1021           int distance = int(sqrt((double)(delta_x * delta_x + delta_y * delta_y)));
1022 
1023           movement += distance;
1024           if (movement > 0)
1025             {
1026               current_day->misc_stats[STATS_VALUE_TOTAL_MOUSE_MOVEMENT] = movement;
1027             }
1028 
1029 
1030           gint64 now = g_get_real_time();
1031           gint64 tv = now - last_mouse_time;
1032 
1033           int tv_sec = tv / G_USEC_PER_SEC;
1034           if (last_mouse_time != 0  && tv_sec >= 0 && tv_sec < 1)
1035             {
1036               current_day->total_mouse_time += tv;
1037               current_day->misc_stats[STATS_VALUE_TOTAL_MOVEMENT_TIME] = current_day->total_mouse_time / G_USEC_PER_SEC;
1038             }
1039 
1040           last_mouse_time = now;
1041         }
1042     }
1043 
1044   lock.unlock();
1045 }
1046 
1047 
1048 //! Mouse button activity is reported by the input monitor.
1049 void
button_notify(bool is_press)1050 Statistics::button_notify(bool is_press)
1051 {
1052   lock.lock();
1053   if (current_day != NULL)
1054     {
1055       if (click_x != -1 && click_y != -1 &&
1056           prev_x != -1  && prev_y != -1)
1057         {
1058           int delta_x = click_x - prev_x;
1059           int delta_y = click_y - prev_y;
1060 
1061           int64_t movement = current_day->misc_stats[STATS_VALUE_TOTAL_CLICK_MOVEMENT];
1062           int64_t distance = int(sqrt((double)(delta_x * delta_x + delta_y * delta_y)));
1063 
1064           movement += distance;
1065           if (movement > 0)
1066             {
1067               current_day->misc_stats[STATS_VALUE_TOTAL_CLICK_MOVEMENT] = movement;
1068             }
1069         }
1070 
1071       click_x = prev_x;
1072       click_y = prev_y;
1073 
1074       if (is_press)
1075         {
1076           current_day->misc_stats[STATS_VALUE_TOTAL_CLICKS]++;
1077         }
1078     }
1079   lock.unlock();
1080 }
1081 
1082 
1083 //! Keyboard activity is reported by the input monitor.
1084 void
keyboard_notify(bool repeat)1085 Statistics::keyboard_notify(bool repeat)
1086 {
1087   if (repeat)
1088     return;
1089 
1090   lock.lock();
1091   if (current_day != NULL)
1092     {
1093       current_day->misc_stats[STATS_VALUE_TOTAL_KEYSTROKES]++;
1094     }
1095   lock.unlock();
1096 }
1097