Skip to content

Commit b867cb1

Browse files
committed
feat(timer): further enhanced timing accuracy
1 parent 28edfe4 commit b867cb1

File tree

4 files changed

+127
-131
lines changed

4 files changed

+127
-131
lines changed

include/timer/timer.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,26 @@ typedef enum {
4242
* Global State Variables
4343
* ============================================================================ */
4444

45-
/* Timer control */
45+
#include <time.h>
46+
#include <stdint.h>
47+
48+
/* Global Timer State */
4649
extern BOOL CLOCK_IS_PAUSED;
4750
extern BOOL CLOCK_SHOW_CURRENT_TIME;
4851
extern BOOL CLOCK_USE_24HOUR;
4952
extern BOOL CLOCK_SHOW_SECONDS;
5053
extern BOOL CLOCK_COUNT_UP;
5154
extern char CLOCK_STARTUP_MODE[20];
5255

53-
/* Timer data */
5456
extern int CLOCK_TOTAL_TIME;
5557
extern int countdown_elapsed_time;
5658
extern int countup_elapsed_time;
59+
60+
/* Absolute Time State for Drift-Free Timing (Milliseconds) */
61+
extern int64_t g_target_end_time; /* For Countdown: When the timer should end */
62+
extern int64_t g_start_time; /* For CountUp: When the timer started */
63+
extern int64_t g_pause_start_time; /* Timestamp when pause began */
64+
5765
extern time_t CLOCK_LAST_TIME_UPDATE;
5866
extern int last_displayed_second;
5967

src/drawing/drawing_timer_precision.c

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,50 @@
66
#include <windows.h>
77
#include "drawing/drawing_timer_precision.h"
88

9-
/** High-resolution timer state (sub-second precision) */
10-
static DWORD g_timer_start_tick = 0;
11-
static BOOL g_timer_ms_initialized = FALSE;
12-
static int g_paused_milliseconds = 0;
9+
/**
10+
* @file drawing_timer_precision.c
11+
* @brief High-precision timer state for sub-second display
12+
*/
1313

14-
void ResetTimerMilliseconds(void) {
15-
g_timer_start_tick = GetTickCount();
16-
g_timer_ms_initialized = TRUE;
17-
g_paused_milliseconds = 0;
18-
}
14+
#include <windows.h>
15+
#include <stdint.h>
16+
#include "drawing/drawing_timer_precision.h"
1917

20-
void PauseTimerMilliseconds(void) {
21-
if (g_timer_ms_initialized) {
22-
DWORD current_tick = GetTickCount();
23-
DWORD elapsed_ms = current_tick - g_timer_start_tick;
24-
g_paused_milliseconds = (int)(elapsed_ms % 1000);
25-
}
26-
}
18+
/* Legacy stubs for compatibility */
19+
void ResetTimerMilliseconds(void) {}
20+
void PauseTimerMilliseconds(void) {}
2721

28-
/** @return Elapsed centiseconds, frozen during pause to prevent visual jumps */
22+
/** @return Elapsed centiseconds (0-99) synchronized with main timer */
2923
int GetElapsedCentiseconds(void) {
3024
extern BOOL CLOCK_IS_PAUSED;
25+
extern BOOL CLOCK_COUNT_UP;
26+
extern BOOL CLOCK_SHOW_CURRENT_TIME;
27+
extern int64_t g_target_end_time;
28+
extern int64_t g_start_time;
29+
extern int64_t g_pause_start_time;
30+
extern int64_t GetAbsoluteTimeMs(void);
31+
32+
int64_t calc_time_point;
3133

3234
if (CLOCK_IS_PAUSED) {
33-
return g_paused_milliseconds / 10;
35+
calc_time_point = g_pause_start_time;
36+
} else {
37+
calc_time_point = GetAbsoluteTimeMs();
3438
}
3539

36-
if (!g_timer_ms_initialized) {
37-
ResetTimerMilliseconds();
38-
return 0;
40+
if (CLOCK_SHOW_CURRENT_TIME) {
41+
return (int)((calc_time_point % 1000) / 10);
3942
}
4043

41-
DWORD current_tick = GetTickCount();
42-
DWORD elapsed_ms = current_tick - g_timer_start_tick;
43-
return (int)((elapsed_ms % 1000) / 10);
44+
if (CLOCK_COUNT_UP) {
45+
int64_t elapsed_ms = calc_time_point - g_start_time;
46+
if (elapsed_ms < 0) elapsed_ms = 0;
47+
return (int)((elapsed_ms % 1000) / 10);
48+
} else {
49+
int64_t remaining_ms = g_target_end_time - calc_time_point;
50+
if (remaining_ms < 0) remaining_ms = 0;
51+
return (int)((remaining_ms % 1000) / 10);
52+
}
4453
}
4554

4655
int GetSystemCentiseconds(void) {

src/timer/timer.c

Lines changed: 35 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include <string.h>
1717
#include <ctype.h>
1818
#include <windows.h>
19+
#include <time.h>
20+
#include <stdint.h>
1921

2022
#define SECONDS_PER_MINUTE 60
2123
#define SECONDS_PER_HOUR 3600
@@ -51,6 +53,22 @@ char CLOCK_TIMEOUT_FILE_PATH[MAX_PATH] = "";
5153
int time_options[MAX_TIME_OPTIONS] = {0};
5254
int time_options_count = 0;
5355

56+
/* Absolute Time State Definitions (Milliseconds) */
57+
int64_t g_target_end_time = 0;
58+
int64_t g_start_time = 0;
59+
int64_t g_pause_start_time = 0;
60+
61+
/* Helper for millisecond precision */
62+
int64_t GetAbsoluteTimeMs(void) {
63+
FILETIME ft;
64+
GetSystemTimeAsFileTime(&ft);
65+
ULARGE_INTEGER uli;
66+
uli.LowPart = ft.dwLowDateTime;
67+
uli.HighPart = ft.dwHighDateTime;
68+
/* Convert 100-nanosecond intervals (since Jan 1, 1601) to milliseconds */
69+
return (int64_t)(uli.QuadPart / 10000ULL);
70+
}
71+
5472
/** Reset QPC baseline to prevent time jumps after pause/resume */
5573
BOOL InitializeHighPrecisionTimer(void) {
5674
if (!QueryPerformanceFrequency(&timer_frequency)) {
@@ -63,45 +81,6 @@ BOOL InitializeHighPrecisionTimer(void) {
6381
return TRUE;
6482
}
6583

66-
/** Delta-based measurement, auto-updates baseline to avoid accumulation errors */
67-
static double GetElapsedMilliseconds(void) {
68-
if (!high_precision_timer_initialized) {
69-
if (!InitializeHighPrecisionTimer()) {
70-
return 0.0;
71-
}
72-
}
73-
74-
LARGE_INTEGER current_count;
75-
if (!QueryPerformanceCounter(&current_count)) {
76-
return 0.0;
77-
}
78-
79-
double elapsed = (double)(current_count.QuadPart - timer_last_count.QuadPart)
80-
* MILLISECONDS_PER_SECOND / (double)timer_frequency.QuadPart;
81-
timer_last_count = current_count;
82-
83-
return elapsed;
84-
}
85-
86-
/** Accumulate elapsed time, clamp countdown to prevent negative display */
87-
static void UpdateElapsedTime(void) {
88-
if (CLOCK_IS_PAUSED) {
89-
return;
90-
}
91-
92-
double elapsed_ms = GetElapsedMilliseconds();
93-
int elapsed_sec = (int)(elapsed_ms / MILLISECONDS_PER_SECOND);
94-
95-
if (CLOCK_COUNT_UP) {
96-
countup_elapsed_time += elapsed_sec;
97-
} else {
98-
countdown_elapsed_time += elapsed_sec;
99-
if (countdown_elapsed_time > CLOCK_TOTAL_TIME) {
100-
countdown_elapsed_time = CLOCK_TOTAL_TIME;
101-
}
102-
}
103-
}
104-
10584
/** Leading spaces stabilize width when hours/minutes disappear during countdown */
10685
static void FormatTimeComponents(int hours, int minutes, int seconds,
10786
char* buffer, size_t buffer_size) {
@@ -153,7 +132,7 @@ static void FormatSystemClock(char* time_text) {
153132
}
154133

155134
static void FormatCountUpTime(char* time_text) {
156-
UpdateElapsedTime();
135+
// Elapsed time is now updated by HandleMainTimer in timer_events.c
157136

158137
int hours = countup_elapsed_time / SECONDS_PER_HOUR;
159138
int minutes = (countup_elapsed_time % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE;
@@ -163,7 +142,7 @@ static void FormatCountUpTime(char* time_text) {
163142
}
164143

165144
static void FormatCountdownTime(char* time_text) {
166-
UpdateElapsedTime();
145+
// Elapsed time is now updated by HandleMainTimer in timer_events.c
167146

168147
int remaining = CLOCK_TOTAL_TIME - countdown_elapsed_time;
169148
if (remaining <= 0) {
@@ -272,18 +251,23 @@ void WriteConfigDefaultStartTime(int seconds) {
272251

273252
/** Fallback to DEFAULT_FALLBACK_TIME if countdown has invalid total time */
274253
void ResetTimer(void) {
254+
int64_t now = GetAbsoluteTimeMs();
255+
275256
if (CLOCK_COUNT_UP) {
276257
countup_elapsed_time = 0;
258+
g_start_time = now;
277259
} else {
278260
countdown_elapsed_time = 0;
279261
if (CLOCK_TOTAL_TIME <= 0) {
280262
CLOCK_TOTAL_TIME = DEFAULT_FALLBACK_TIME;
281263
}
264+
g_target_end_time = now + ((int64_t)CLOCK_TOTAL_TIME * 1000);
282265
}
283266

284267
CLOCK_IS_PAUSED = FALSE;
285268
countdown_message_shown = FALSE;
286269
countup_message_shown = FALSE;
270+
g_pause_start_time = 0;
287271

288272
InitializeHighPrecisionTimer();
289273
ResetMillisecondAccumulator();
@@ -294,10 +278,19 @@ void TogglePauseTimer(void) {
294278
BOOL was_paused = CLOCK_IS_PAUSED;
295279
CLOCK_IS_PAUSED = !CLOCK_IS_PAUSED;
296280

281+
int64_t now = GetAbsoluteTimeMs();
282+
297283
if (CLOCK_IS_PAUSED && !was_paused) {
284+
g_pause_start_time = now;
298285
PauseTimerMilliseconds();
299286
} else if (!CLOCK_IS_PAUSED && was_paused) {
287+
if (g_pause_start_time > 0) {
288+
int64_t pause_duration = now - g_pause_start_time;
289+
g_target_end_time += pause_duration;
290+
g_start_time += pause_duration;
291+
g_pause_start_time = 0;
292+
}
300293
InitializeHighPrecisionTimer();
301294
ResetMillisecondAccumulator();
302295
}
303-
}
296+
}

0 commit comments

Comments
 (0)