Wednesday, 6 September 2017

A Simulated LED type, 7 segment display digital clock using c++ in windows

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.


Source code:
#include <windows.h>
#define ID_TIMER    1
LRESULT CALLBACK WndProc(HWNDUINTWPARAMLPARAM);
int WINAPI WinMain(HINSTANCE hInstanceHINSTANCE hPrevInstancePSTR szCmdLineint 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(NULLIDI_APPLICATION);
       wndclass.hCursor = LoadCursor(NULLIDC_ARROW);
       wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
       wndclass.lpszMenuName = NULL;
       wndclass.lpszClassName = szAppName;
       if (!RegisterClass(&wndclass))
       {
              MessageBox(NULLTEXT("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 hdcint 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 hdcint iNumberBOOL fSuppress)
{
       if (!fSuppress || (iNumber / 10 != 0))
              DisplayDigit(hdciNumber / 10);
       OffsetWindowOrgEx(hdc, -42, 0, NULL);
       DisplayDigit(hdciNumber % 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 hdcBOOL f24HourBOOL 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 hwndUINT messageWPARAM wParamLPARAM 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(hwndID_TIMER, 1000, NULL);
              // fall through
       case WM_SETTINGCHANGE:
              GetLocaleInfo(LOCALE_USER_DEFAULTLOCALE_ITIME, szBuffer, 2);
              f24Hour = (szBuffer[0] == `1`);
              GetLocaleInfo(LOCALE_USER_DEFAULTLOCALE_ITLZERO, szBuffer, 2);
              fSuppress = (szBuffer[0] == `0`);
              InvalidateRect(hwndNULLTRUE);
              return 0;
       case WM_SIZE:
              cxClient = LOWORD(lParam);
              cyClient = HIWORD(lParam);
              return 0;
       case WM_TIMER:
              InvalidateRect(hwndNULLTRUE);
              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(hwndID_TIMER);
              DeleteObject(hBrushRed);
              PostQuitMessage(0);
              return 0;
       }
       return DefWindowProc(hwndmessagewParamlParam);
}


/*
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: