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