This code demonstrates a Simple digital clock. This
clock is a simulated LED type with 7 segment display. It`s looks very
fascinating with a black background and the code is also very simple & even
a beginner in windows c++ programming can understand the logic of the code. The
code explanation is given at the end of the code in the comment block.
Reference: Programming Windows by Charles Petzold.
Reference: Programming Windows by Charles Petzold.
Source code:
#include <windows.h>
#define ID_TIMER
1
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[]
= TEXT("DigClock");
HWND
hwnd;
MSG
msg;
WNDCLASS
wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("Program
requires Windows "), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(
szAppName,
TEXT("Digital
Clock"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
void DisplayDigit(HDC hdc, int iNumber)
{
static BOOL
fSevenSegment[10][7] =
{
1, 1, 1, 0, 1, 1, 1, // 0
0, 0, 1, 0, 0, 1, 0, // 1
1, 0, 1, 1, 1, 0, 1, // 2
1, 0, 1, 1, 0, 1, 1, // 3
0, 1, 1, 1, 0, 1, 0, // 4
1, 1, 0, 1, 0, 1, 1, //
5
1, 1, 0, 1, 1, 1, 1, // 6
1, 0, 1, 0, 0, 1, 0, // 7
1, 1, 1, 1, 1, 1, 1, // 8
1, 1, 1, 1, 0, 1, 1 //
9
};
static POINT ptSegment[7][6]
=
{
7, 6, 11, 2, 31, 2, 35, 6, 31, 10, 11, 10,
6, 7, 10, 11, 10, 31, 6, 35, 2, 31, 2, 11,
36, 7, 40, 11, 40, 31, 36, 35, 32, 31, 32, 11,
7, 36, 11, 32, 31, 32, 35, 36, 31, 40, 11, 40,
6, 37, 10, 41, 10, 61, 6, 65, 2, 61, 2, 41,
36, 37, 40, 41, 40, 61, 36, 65, 32, 61, 32, 41,
7, 66, 11, 62, 31, 62, 35, 66, 31, 70, 11, 70
};
int
iSeg;
for (iSeg
= 0; iSeg < 7; iSeg++)
if (fSevenSegment[iNumber][iSeg])
Polygon(hdc, ptSegment[iSeg], 6);
}
void DisplayTwoDigits(HDC hdc, int iNumber, BOOL fSuppress)
{
if (!fSuppress || (iNumber / 10
!= 0))
DisplayDigit(hdc, iNumber /
10);
OffsetWindowOrgEx(hdc, -42, 0, NULL);
DisplayDigit(hdc, iNumber %
10);
OffsetWindowOrgEx(hdc, -42, 0, NULL);
}
void DisplayColon(HDC hdc)
{
POINT ptColon[2][4]
= { 2, 21, 6, 17, 10, 21, 6, 25, 2, 51, 6, 47, 10, 51, 6, 55 };
Polygon(hdc, ptColon[0], 4); Polygon(hdc,
ptColon[1], 4);
OffsetWindowOrgEx(hdc, -12, 0, NULL);
}
void DisplayTime(HDC hdc, BOOL f24Hour, BOOL fSuppress)
{
SYSTEMTIME st;
GetLocalTime(&st);
if (f24Hour)
DisplayTwoDigits(hdc, st.wHour, fSuppress);
else
DisplayTwoDigits(hdc, (st.wHour %= 12) ? st.wHour :
12, fSuppress);
DisplayColon(hdc);
DisplayTwoDigits(hdc, st.wMinute, FALSE);
DisplayColon(hdc);
DisplayTwoDigits(hdc, st.wSecond, FALSE);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static BOOL
f24Hour, fSuppress;
static HBRUSH hBrushRed;
static int
cxClient, cyClient;
HDC
hdc;
PAINTSTRUCT
ps;
TCHAR
szBuffer[2];
switch (message)
{
case WM_CREATE:
hBrushRed = CreateSolidBrush(RGB(255, 0,
0));
SetTimer(hwnd, ID_TIMER,
1000, NULL);
// fall
through
case WM_SETTINGCHANGE:
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME,
szBuffer, 2);
f24Hour = (szBuffer[0] == `1`);
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITLZERO,
szBuffer, 2);
fSuppress = (szBuffer[0] == `0`);
InvalidateRect(hwnd, NULL, TRUE);
return 0;
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return 0;
case WM_TIMER:
InvalidateRect(hwnd, NULL, TRUE);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
SetMapMode(hdc, MM_ISOTROPIC);
SetWindowExtEx(hdc, 276, 72, NULL);
SetViewportExtEx(hdc, cxClient, cyClient, NULL);
SetWindowOrgEx(hdc, 138, 36, NULL);
SetViewportOrgEx(hdc, cxClient / 2, cyClient / 2, NULL);
SelectObject(hdc, GetStockObject(NULL_PEN));
SelectObject(hdc, hBrushRed);
DisplayTime(hdc, f24Hour, fSuppress);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
KillTimer(hwnd, ID_TIMER);
DeleteObject(hBrushRed);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
/*
Code Explanation:
In this code DIGCLOCK`s window procedure creates a red brush during the
WM_CREATE message and destroys it during the WM_DESTROY message. The WM_CREATE
message also provides DIGCLOCK with an opportunity to set a 1-second timer,
which is stopped during the WM_DESTROY message.
Upon receipt of a WM_TIMER message, DIGCLOCK`s window procedure simply
invalidates the entire window with a call to InvalidateRect. Aesthetically,
this is not the best approach because it means that the entire window will be
erased and redrawn every second, sometimes causing flickering in the display. A
better solution is to invalidate only those parts of the window that need
updating based on the current time.
Invalidating the window during the WM_TIMER message forces the entire
program`s real activity into WM_PAINT. DIGCLOCK begins the WM_PAINT message by
setting the mapping mode to MM_ISOTROPIC. Thus, DIGCLOCK will use arbitrarily
scaled axes that are equal in the horizontal and vertical directions. These
axes (set by a call to SetWindowExtEx) are 276 units horizontally by 72 units
vertically. Of course, these axes seem quite arbitrary, but they are based on
the size and spacing of the clock numbers.
DIGCLOCK sets the window origin to the point (138, 36), which is the
centre of the window extents, and the viewport origin to (cxClient / 2,
cyClient / 2). This means that the clock display will be centred in DIGCLOCK`s
client area but that DIGCLOCK can use axes with an origin of (0, 0) at the
upper-left corner of the display.
The WM_PAINT processing then sets the current brush to the red brush
created earlier and the current pen to the NULL_PEN and calls the function in
DIGCLOCK named DisplayTime.
Getting the Current Time:
The DisplayTime function begins by calling the Windows function GetLocalTime,
which takes as a single argument the SYSTEMTIME structure, defined in WINBASE.H
like so:
typedef struct _SYSTEMTIME { WORD wYear
; WORD wMonth ; WORD wDayOfWeek
; WORD wDay ; WORD wHour
; WORD wMinute ; WORD wSecond
; WORD wMilliseconds ; } SYSTEMTIME, * PSYSTEMTIME ;
As is obvious, the SYSTEMTIME structure encodes the date as well as the
time. The month is 1-based (that is, January is 1), and the day of the week is
0-based (Sunday is 0). The wDay field is the current day of the month, which is
also 1-based.
The SYSTEMTIME structure is used primarily with the GetLocalTime and
GetSystemTime functions. The GetSystemTime function reports the current
Coordinated Universal Time (UTC), which is roughly the same as Greenwich mean
time the date and time at Greenwich, England. The GetLocalTime function reports
the local time, based on the time zone of the location of the computer. The
accuracy of these values is entirely dependent on the diligence of the user in
keeping the time accurate and in indicating the correct time zone. You can
check the time zone set on your machine by double-clicking the time display in
the task bar.
Windows also has SetLocalTime and SetSystemTime functions, as well as
some other useful time-related functions that are discussed in /Platform
SDK/Windows Base Services/General Library/Time.
Displaying Digits and Colons:
The DisplayDigit function in DIGCLOCK defines two arrays. The
fSevenSegment array has 7 BOOL values for each of the 10 decimal digits from 0
through 9. These values indicate which of the segments are illuminated (a 1
value) and which are not (a 0 value). In this array, the 7 segments are ordered
from top to bottom and from left to right. Each of the 7 segments is a 6-sided
polygon. The ptSegment array is an array of POINT structures indicating the
graphical coordinates of each point in each of the 7 segments. Each digit is
then drawn by this code:
for (iSeg = 0 ; iSeg < 7 ; iSeg++)
if (fSevenSegment [iNumber][iSeg])
Polygon (hdc,
ptSegment [iSeg], 6) ;
Similarly, the DisplayColon function draws the colons that separate the
hour and minutes, and the minutes and seconds. The digits are 42 units wide and
the colons are 12 units wide, so with 6 digits and 2 colons, the total width is
276 units, which is the size used in the SetWindowExtEx call.
Upon entry to the DisplayTime function, the origin is at the upper left
corner of the position of the leftmost digit. DisplayTime calls
DisplayTwoDigits, which calls DisplayDigit twice, and after each time calls
OffsetWindowOrgEx to move the window origin 42 units to the right. Similarly,
the DisplayColon function moves the window origin 12 units to the right after
drawing the colon. In this way, the functions can use the same coordinates for
the digits and colons, regardless of where the object is to appear within the
window. The only other tricky aspects of this code involve displaying the time
in a 12-hour or 24-hour format and suppressing the leftmost hours digit if it`s
0.
*/
Output: