making a clock in win32
whats the best way to make a clock (in win32)? bearing in mind i'm not up to multi-threaing level win32 programming
this is what i first came up with
int cur_time = time(NULL);
int dif_time = 0;
while (GetMessage (&messages, NULL, 0, 0))
{
//once a second send WM_PAINT message
dif_time = cur_time - time(NULL);
if(dif_time)
{
PostMessage(hwnd, WM_PAINT, NULL, NULL);
cur_time = time(NULL);
}
TranslateMessage(&messages);
DispatchMessage(&messages);
}
but it doesn't seem to be very consistent at ticking once a second
[734 byte] By [
g3RC4n] at [2007-11-20 10:01:23]

# 1 Re: making a clock in win32
okay... clock without multi-threading... here we go:
First -- Get accurate(enough) time.
If you're using C, include <time.h>
else if you're using C++, include <ctime>
time_t value = NULL; // use time_t type and initilize to 0
time(&value); // Get current time
tm actualTime = {0}; // use tm struct type and initilize struct to 0
localtime_s(&actualTime, value); // convert to an actual clock time
That gets the time... (tm structure has a tm_sec, tm_min, and tm_hour members to show the time)
Next you need to route an event so you can update the time
UINT_PTR SetTimer(
HWND hWnd,
UINT_PTR nIDEvent,
UINT uElapse,
TIMERPROC lpTimerFunc
);
the above function does that... an example:
#define IDT_MAIN 1001
// Routes a event to WM_TIMER every 700 miliseconds, or 7/10 a second.
// It is better to update more than once a second due to windows
// timer inaccurices.
SetTimer(windowHandle, IDT_MAIN, 700, NULL);
// Just remember to call this before WinMain returns
KillTimer(windowHandle, IDT_MAIN);
// For the window message.. here
switch (message)
{
case WM_TIMER:
{
if (wParam == IDT_MAIN)
{
// Get time and display it somewhere
}
} break;
}
There we go. I think I got everything... need any more help feel free to ask. ;)
# 2 Re: making a clock in win32
nice, thanks
g3RC4n at 2007-11-9 13:32:52 >

# 3 Re: making a clock in win32
Just to add. You don't post WM_PAINT messages ever. The way to trigger a paint is to InvalidateRect and then UpdateWindow
# 4 Re: making a clock in win32
// Routes a event to WM_TIMER every 700 miliseconds, or 7/10 a second.
// It is better to update more than once a second due to windows
// timer inaccurices.
This is a VERY strange idea! Why would you do that?
It will NOT improve the accuracy of Window's timer, but it wil make your clock "jerky": one digit will stay up .7 seconds, another - 1.4 seconds.
BTW, it's very popular to blame Windows' timer (and other things), but I want to assure you that it's perfectly fine for this task - to count seconds.
# 5 Re: making a clock in win32
finished the clock
//analogue alarm clock
//started 11 august 2007
//version 0.5
//coded by g3RC4n
//******************************************************************************
//notes:
// clock is always square
// diameter is 3/4 os the shortest axis
//******************************************************************************
#include <windows.h>
#include <ctime>
#include <math.h>
#define IDT_MAIN 1001
char szClassName[ ] = "WindowsApp";
const int x_boarder = 8;
const int y_boarder = 34;
const int client_area = 200;
const int dls = 1;
POINT circle[60];
struct curTime
{
int sec;
int min;
int hour;
};
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
void DrawCircle(HWND hwnd);
curTime CurrentTime();
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil)
{
HWND hwnd;
MSG messages;
WNDCLASSEX wincl;
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure;
wincl.style = CS_DBLCLKS;
wincl.cbSize = sizeof (WNDCLASSEX);
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
if (!RegisterClassEx (&wincl))
return 0;
hwnd = CreateWindowEx (
0,
szClassName,
"g3 analogue clock v0.5",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
client_area + x_boarder,
client_area + y_boarder,
HWND_DESKTOP,
NULL,
hThisInstance,
NULL
);
ShowWindow (hwnd, nFunsterStil);
while (GetMessage (&messages, NULL, 0, 0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
KillTimer(hwnd, IDT_MAIN);
return messages.wParam;
}
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
SetTimer(hwnd, IDT_MAIN, 700, NULL);
return 0;
case WM_TIMER:
if (wParam == IDT_MAIN)
{
DrawCircle(hwnd);
SetTimer(hwnd, IDT_MAIN, 700, NULL);
}
return 0;
case WM_SIZE:
DrawCircle(hwnd);
return 0;
case WM_PAINT:
DrawCircle(hwnd);
return 0;
case WM_DESTROY:
PostQuitMessage (0);
return 0;
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
void DrawCircle(HWND hwnd)
{
HDC hdc;
PAINTSTRUCT ps;
curTime time;
RECT rect;
int shrtax;
int buffer;
GetClientRect(hwnd, &rect);
if(rect.right < rect.bottom)
{
shrtax = rect.right;
}
else
{
shrtax = rect.bottom;
}
double pi = 3.142;
int x;
int y;
double a = pi/2;
int r = shrtax;
int i = 0;
int i2 = 59;
while(i < 60)
{
x = cos(a) * r;
y = sin(a) * r;
a+= pi/30;
circle[i2].x = x;
circle[i2].y = y;
i2--;
i++;
}
InvalidateRect(hwnd, &rect, true);
hdc = BeginPaint(hwnd, &ps);
//set mapping mode 0,0 at the origin
// |+
// |
//-____|_____+
// |
// |
// |-
//*****************************
SetMapMode(hdc, MM_ISOTROPIC);
SetWindowExtEx(hdc, 1000, 1000, NULL);
SetViewportExtEx(hdc, shrtax/2 , -shrtax/2, NULL);
SetViewportOrgEx(hdc, shrtax/2, shrtax/2, NULL);
//*****************************
SetPixel(hdc, 0,0,RGB(0,0,0));
i = 0;
while(i < 10000)
{
x = cos(a) * r;
y = sin(a) * r;
a+= pi/30;
SetPixel(hdc, x,y,RGB(0,0,0));
i++;
}
time = CurrentTime();
HPEN hpen;
//hour hand
//thickest pen
hpen = CreatePen(PS_SOLID, 8, RGB(0,0,0));
SelectObject(hdc, hpen);
buffer = time.hour * 5;
MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, circle[buffer].x, circle[buffer].y);
//minuet hand
//midum pen
hpen = CreatePen(PS_SOLID, 4, RGB(0,0,0));
SelectObject(hdc, hpen);
MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, circle[time.min].x, circle[time.min].y);
//second hand
hpen = CreatePen(PS_SOLID, 1, RGB(0,0,0));
SelectObject(hdc, hpen);
MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, circle[time.sec].x, circle[time.sec].y);
EndPaint(hwnd, &ps);
DeleteDC(hdc);
DeleteObject(hpen);
return;
}
curTime CurrentTime()
{
curTime cur = {0,0,0};
time_t value = 0;
time(&value);
value = value % 43200;
cur.hour = (value / 3600) + dls;
value %= 3600;
cur.min = value / 60;
value %= 60;
cur.sec = value;
return cur;
}
compiles in dev c++
the clocks fugly but it's more about the concept, it's alos a tad messy but anyway
the clock isn't smooth lol
g3RC4n at 2007-11-9 13:35:53 >

# 6 Re: making a clock in win32
This is a VERY strange idea! Why would you do that?
IF you are checking against the system clock. (IE: time() difftime() );
And not agumenting the time yourself. (IE: seconds++ );
Then the actual time will be more accurate. Mayhap, the dispaly will be a slight bit jittery in some situations. YET - the actual data will be more accurate.
ACTUALLY, upon some expirements, you are correct (Data is still more accurate though). Unless its changed to 200. IT may work on 500 too.
..... back tog g3RC4n:
Great job! Eccspically(sp?) with the GDI calls.
Though here are a couple areas I can see improvement would be welcome:
case WM_TIMER:
if (wParam == IDT_MAIN)
{
DrawCircle(hwnd);
SetTimer(hwnd, IDT_MAIN, 700, NULL);
}
return 0;
Okay, I am sorry I did not make this clear: SetTimer must only be called once; else that is a (albiet small) memory loss. The WM_TIMER messages will keep coming until KillTimer is called.
value = value % 43200;
cur.hour = (value / 3600) + dls;
value %= 3600;
cur.min = value / 60;
value %= 60;
cur.sec = value;
Although the above is quite inventive. (Good job - in my opinion - inventiveness is a very impressive tool to a programmer). It would be better to use the localtime function and then route the tm structure to your curtime structure. Like below:
tm actualTime = {0}; // use tm struct type and initilize struct to 0
localtime_s(&actualTime, value); // convert to an actual clock time
//..then
cur.hour = actualTime.tm_hour;
cur.min = actualTime.tm_min;
cur.sec = actualTime.tm_sec;
Also, sorry for misleading you. I too though alike.
It would be wise to change the 700 in the SetTimer call to 200 or 500. Depending on your own desire. It would make little difference (or so I belive) either way. 1000 'should' work. Yet it doesnt - not perfectly.
Very good go though! :) I am impressed. I would have never sought to make an actual 'clock.' (My excuse: Too much work) But you did nicely.
Note: I havent actually tested your code. No time... But if you insist I will.
# 7 Re: making a clock in win32
Okay, I am sorry I did not make this clear: SetTimer must only be called once; else that is a (albiet small) memory loss.Could you please explain what memoy will be lost if SetTimer() is called multiple times?
# 8 Re: making a clock in win32
thanks Blobmiester for your support, this was my first win32 program i've made and only been coding since January and win32 for under a month, but i'm 17 so i got a lot of free time i guess lol.
i'll adds the improvements later
g3RC4n at 2007-11-9 13:38:57 >

# 9 Re: making a clock in win32
This is a VERY strange idea! Why would you do that?
It will NOT improve the accuracy of Window's timer, but it wil make your clock "jerky": one digit will stay up .7 seconds, another - 1.4 seconds.
BTW, it's very popular to blame Windows' timer (and other things), but I want to assure you that it's perfectly fine for this task - to count seconds.
Just to visualize what he said:
Time line: .......|.......|.......|.......|.......|.......|.......|...-> t
57 58 59 00 01 02 03
Samples Line: .....|.....|.....|.....|.....|.....|.....|.....|.....|.....|...-> t
Result: 56 57 58 58 59 00 00 01 02 03
* Every '.' is 100ms...
Because 1 second (1000ms) doesnt devide without reminder with 700ms
then some seconds seconds will be sampled ones, and some once,
meaning those that will be sampled once, will show up only 700ms,
and those who sampled twice will show up 1400ms!
I would recommend using 100ms, 200ms. (5 or 10 times a second).
# 10 Re: making a clock in win32
No problem, none at all g3RC4n.
Could you please explain what memoy will be lost if SetTimer() is called multiple times?
This is getting tedious.
One question: Have 'you' tried at all to disprove my statement besides think?
I'll still answer your question though, and I do apoligize my temper is higher than normal as of late.
There is multiple purposes of SetTimer. You can create a timer (duh). You can change an alreadly made timer's tick setting. Hell, you don't even have to use WM_TIMER if you dont want to. You could pass a static member function (or a global) to the last parameter and route calls to that.
Enough of the lecture. What matters is, SetTimer creates a new timer on every call. No matter what you pass. In g3RC4n's code that he/she posted the function SetTimer was being called every 7/10 a second. Not only will this an easily avoidable function overhead to the WM_TIMER message, but it result in many - many timers being created that simply don't need to be. Optimization should be the goal - the life - and the dream of every programmer.
I would recommend using 100ms, 200ms. (5 or 10 times a second).
Did you read my previous post?
I 'said' that already. Pointless post. Again, forgive my temper. But please, Read.
After calming down a bit. I must add that you did provide some quite nifty information. I, personally, did not know exatcly why only numbers who 1000 can divide by work perfectly.(50,100,200,500) Though I really wouldn't use anything under 500, there isn't need to update more than that, really.
# 11 Re: making a clock in win32
Yea I know you already said that he was correct, I just wanted to
"visualize" as I said...
Anyway, he isnt creating a new timer every 0.7sec hes just unecessary updating it every 0.7sec since he is using the same ID over and over again, and if you use a already used ID than it just updates and resets the timer.
# 12 Re: making a clock in win32
...This is getting tedious.
One question: Have 'you' tried at all to disprove my statement besides think?
I'll still answer your question though, and I do apoligize my temper is higher than normal as of late.
There is multiple purposes of SetTimer. You can create a timer (duh). You can change an alreadly made timer's tick setting. Hell, you don't even have to use WM_TIMER if you dont want to. You could pass a static member function (or a global) to the last parameter and route calls to that.
Enough of the lecture. What matters is, SetTimer creates a new timer on every call. No matter what you pass. In g3RC4n's code that he/she posted the function SetTimer was being called every 7/10 a second. Not only will this an easily avoidable function overhead to the WM_TIMER message, but it result in many - many timers being created that simply don't need to be. Optimization should be the goal - the life - and the dream of every programmer.Well... Hmmm... Thank you for the "lecture"... I guess.
Whats with the temper? I have simply challenged your unsubstantiated claim that some mysterious small amount of memory will be lost.
Judging by the mentoring tone of your posts I suppose you have some authority on that matter. Having used SetTimer() on multiple occasions during my years of Windows programming, I was afraid that I unknowingly was leaking memory all that time So do you have anything other than your temper to convince others?
Forgetting that the original question was just a simple exercise, lets look at a proper design for a ticking clock.
Suppose we want our clock to tick as close to the change from one second to another.
The precision of the suggested time() function is clearly not sufficient. A better choice will be _ftime_s() that works with a structure that has a field millitm (fraction of a second in milliseconds). We could then set our timer so it will fire [almost] exactly at the time of seconds change.
I would suggest resetting it after each WM_TIMER was handled to a newly calculated interval to avoid accumulation of the error. Oh, and dont worry about that small memory loss. Trust me :)
