home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / winbase / ipc / ddeml / clock / clock.c next >
C/C++ Source or Header  |  1997-10-05  |  44KB  |  1,323 lines

  1.  
  2. /******************************************************************************\
  3. *       This is a part of the Microsoft Source Code Samples. 
  4. *       Copyright (C) 1993-1997 Microsoft Corporation.
  5. *       All rights reserved. 
  6. *       This source code is only intended as a supplement to 
  7. *       Microsoft Development Tools and/or WinHelp documentation.
  8. *       See these sources for detailed information regarding the 
  9. *       Microsoft samples programs.
  10. \******************************************************************************/
  11.  
  12. /*
  13.  *  CLOCK.C - Windows DDEML Clock
  14.  *
  15.  *  DDE Transactions:
  16.  *  ----------------
  17.  *  Service: Clock
  18.  *  Topic  : Time
  19.  *  Item   : Now
  20.  *
  21.  *  Use Request or Advise to get the time or use Poke to change the time.
  22.  *  Time Format Hour:Minute:Seconds where
  23.  *   Hour    = 0-23
  24.  *   Minute  = 0-59
  25.  *   Seconds = 0-59
  26.  */
  27.  
  28. #include <time.h>
  29. #include <stdlib.h>
  30. #include <stdio.h>
  31. #include <string.h>
  32. #include "windows.h"
  33. #include <ddeml.h>
  34. #include "clock.h"
  35.  
  36.  
  37. DWORD   idInst = 0;
  38.  
  39. CLOCKDISPSTRUCT ClockDisp;
  40.  
  41. HANDLE  hInst;
  42. HWND    hWindow;
  43.  
  44. HBRUSH  hbrBackground;
  45. HBRUSH  hbrColorWindow;
  46. HBRUSH  hbrColorBlack;
  47. HBRUSH  hbrForeground;
  48. HFONT   hFont;
  49. HPEN    hpenForeground;
  50. HPEN    hpenBackground;
  51.  
  52. INT     nLeadZero   = 0;
  53. INT     TimerID     = 1;    /* number used for timer-id */
  54. INT     clockRadius;
  55. INT     HorzRes;
  56. INT     VertRes;
  57.  
  58. LONG    aspectD;
  59. LONG    aspectN;
  60.  
  61. CHAR    szBuffer[BUFLEN];    /* buffer for stringtable stuff */
  62. CHAR    szFontFile[20];
  63. CHAR    szIniFile[20];
  64. CHAR    szSection[30];
  65.  
  66. POINT   clockCenter;
  67.  
  68. TIME    oTime;
  69.  
  70. RECT    clockRect;
  71. RECT    rCoordRect;
  72.  
  73. HDDEDATA CALLBACK DdeCallback(WORD usType, WORD usFmt, HCONV hConv, HSZ hsz1,
  74.         HSZ hsz2, HDDEDATA hData, DWORD lData1, DWORD lData2);
  75. HSZ hszTime, hszNow, hszClock;    /* Hszs for DDEML use */
  76.  
  77. /*
  78.  *  Function Prototypes
  79.  */
  80.  
  81. LONG APIENTRY ClockWndProc(register HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
  82.  
  83. void NEAR PASCAL ClockCreate(HWND hWnd);
  84. BOOL NEAR PASCAL ClockInit(HANDLE hInstance);
  85. void NEAR PASCAL ClockPaint(HWND hWnd, register HDC hDC, INT hint);
  86. void NEAR PASCAL ClockSize(register HWND hWnd,INT newWidth,INT newHeight, WORD SizeWord);
  87. void NEAR PASCAL ClockTimer(HWND hWnd, UINT msg);
  88. void NEAR PASCAL CompClockDim(void);
  89. void NEAR PASCAL CreateTools(void);
  90. void NEAR PASCAL DeleteTools(void);
  91. void NEAR PASCAL DrawBorder(HWND hWnd, register HDC hDC);
  92. void NEAR PASCAL DrawFace(HDC hDC);
  93. void NEAR PASCAL DrawHand(register HDC hDC, INT pos, HPEN hPen, INT scale, INT patMode);
  94. void NEAR PASCAL DrawFatHand(register HDC hDC, INT pos, HPEN hPen, BOOL hHand);
  95. void NEAR PASCAL FormatInit(register HANDLE hInstance, BOOL fModeChange);
  96. void NEAR PASCAL SizeFont(HWND hWnd, INT newHeight, INT newWidth, INT wlen);
  97. void NEAR PASCAL SetMenuBar( HWND hWnd );
  98.  
  99.  
  100. /*
  101.  *  CreateTools()
  102.  */
  103.  
  104. void NEAR PASCAL CreateTools(void)
  105.  
  106. {
  107.   hbrForeground  = CreateSolidBrush(GetSysColor(COLOR_WINDOWTEXT));
  108.   hbrColorWindow = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
  109.   hbrColorBlack  = CreateSolidBrush(0L);
  110.   hbrBackground  = hbrColorWindow;
  111.   hpenForeground = CreatePen(0, 1, GetSysColor(COLOR_WINDOWTEXT));
  112.   hpenBackground = CreatePen(0, 1, GetSysColor(COLOR_WINDOW));
  113. }
  114.  
  115.  
  116. /*
  117.  *  DeleteTools()
  118.  */
  119.  
  120. void NEAR PASCAL DeleteTools(void)
  121.  
  122. {
  123.   DeleteObject(hbrForeground);
  124.   DeleteObject(hbrColorWindow);
  125.   DeleteObject(hbrColorBlack);
  126.   DeleteObject(hpenForeground);
  127.   DeleteObject(hpenBackground);
  128. }
  129.  
  130.  
  131. /*
  132.  * SizeFont() - size font according to window size
  133.  */
  134.  
  135. void NEAR PASCAL SizeFont(HWND hWnd, INT newHeight, INT newWidth, INT wlen)
  136. {
  137. register HDC    hDC;
  138. TEXTMETRIC    tm;
  139. LOGFONT    FontStruct;
  140.  
  141.     hDC = GetDC(hWnd);
  142.     GetTextMetrics(hDC, &tm);
  143.  
  144.     if (ClockDisp.wFormat == IDM_DIGITAL) {
  145.         if (hFont != NULL)
  146.             DeleteObject(hFont);
  147.  
  148.         FontStruct.lfUnderline = FALSE;
  149.         FontStruct.lfStrikeOut = FALSE;
  150.         FontStruct.lfItalic    = FALSE;
  151.         FontStruct.lfEscapement = FALSE;
  152.         FontStruct.lfOrientation = FALSE;
  153.         FontStruct.lfOutPrecision = OUT_DEFAULT_PRECIS;
  154.         FontStruct.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  155.  
  156.         /* Note that the numbers used in this formula depend on the
  157.          * size of the numbers in the fonts which are 12 vert, 7 horz.
  158.         */
  159.         if (!ClockDisp.bIconic)  {
  160.             FontStruct.lfHeight = (SHORT)min(newHeight/2, 45);
  161.  
  162.             /* The formula at the end is somewhat empirical. */
  163.             FontStruct.lfWidth = (SHORT)min(25, newWidth/(wlen*3/2));
  164.  
  165.             /* This if is here because of a problem where:  if a clock
  166.              * existed with a maximum size font, any new clock with a
  167.              * smaller client area would get the same font that would not
  168.              * fit in the Window.  So here, if the width does not match
  169.              * maximum font dimensions, don't use the maximum height.
  170.              */
  171.             if (FontStruct.lfWidth != 25 && FontStruct.lfHeight == 45)
  172.                 FontStruct.lfHeight = 40;
  173.         }
  174.         else {
  175.             FontStruct.lfHeight = (SHORT)(newHeight/3);
  176.             FontStruct.lfWidth  = (SHORT)(newWidth/5);
  177.         }
  178.  
  179.         FontStruct.lfCharSet      = ANSI_CHARSET;
  180.         FontStruct.lfQuality      = DRAFT_QUALITY;
  181.         FontStruct.lfWeight      = FW_NORMAL;
  182.         FontStruct.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS;
  183.  
  184.         lstrcpy(FontStruct.lfFaceName, "DIGITAL");
  185.  
  186.         hFont = CreateFontIndirect(&FontStruct);
  187.  
  188.         SelectObject(hDC, hFont);
  189.         GetTextMetrics(hDC, &tm);
  190.         SelectObject(hDC, GetStockObject(SYSTEM_FONT));
  191.     }
  192.  
  193.     ReleaseDC(hWnd, hDC);
  194.  
  195.     /* Compute placement for digital text. */
  196.     ClockDisp.line1.x = (newWidth) / 2;
  197.     ClockDisp.line1.y = (newHeight - (tm.tmHeight)) / 2;
  198.     ClockDisp.yline2  = ClockDisp.line1.y + tm.tmHeight;
  199. }
  200.  
  201.  
  202. /*
  203.  *  CompClockDim() - Recompute the clock's dimensions.
  204.  */
  205.  
  206. void NEAR PASCAL CompClockDim(void)
  207.  
  208. {
  209. INT        i;
  210. register INT    tWidth;
  211. register INT    tHeight;
  212.  
  213.     tWidth = clockRect.right - clockRect.left;
  214.     tHeight = clockRect.bottom - clockRect.top;
  215.  
  216.     if (tWidth > (INT)((tHeight * aspectD) / aspectN)) {
  217.         i = (INT)((tHeight * aspectD) / aspectN);
  218.         clockRect.left += (tWidth - i) >> 1;
  219.         clockRect.right = clockRect.left + i;
  220.     }
  221.     else {
  222.         i = (INT)((tWidth * aspectN) / aspectD);
  223.         clockRect.top += (tHeight - i) >> 1;
  224.         clockRect.bottom = clockRect.top + i;
  225.     }
  226. }
  227.  
  228.  
  229. /*
  230.  *  ClockSize()
  231.  */
  232.  
  233. void NEAR PASCAL ClockSize(register HWND hWnd,
  234.                INT       newWidth,
  235.                INT       newHeight,
  236.                WORD      SizeWord)
  237. {
  238.     SetRect((LPRECT)&(clockRect), 0, 0, newWidth, newHeight);
  239.     CompClockDim();
  240.  
  241.     if (SizeWord == SIZEICONIC) {
  242.         /* Update once every 1/2 minute in the iconic state */
  243.         KillTimer(hWnd, TimerID);
  244.         SetTimer(hWnd, TimerID, (WORD)ICON_TLEN, 0L);
  245.         ClockDisp.bIconic = TRUE;
  246.     }
  247.     else if (ClockDisp.bIconic) {
  248.         /* Update every 1/2 second in the opened state. */
  249.         KillTimer(hWnd, TimerID);
  250.         SetTimer(hWnd, TimerID, OPEN_TLEN, 0L);
  251.         ClockDisp.bIconic = FALSE;
  252.     }
  253.  
  254.     /* Compute where the digital readout should go. */
  255.     SizeFont(hWnd, newHeight, newWidth, ClockDisp.wDigTimeLen);
  256. }
  257.  
  258.  
  259. /*
  260.  *  DrawBorder() - Draws a Red Border around either clock.
  261.  */
  262.  
  263. void NEAR PASCAL DrawBorder(HWND hWnd, register HDC hDC)
  264. {
  265. RECT Rect;
  266. HPEN hPen;
  267.  
  268.     GetClientRect(hWnd, (LPRECT) &Rect);
  269.  
  270.     hPen = CreatePen(PS_SOLID, (ClockDisp.bIconic) ? 1 : 2,
  271.                (ClockDisp.bColor)  ? RGB(255,0,0) : RGB(255,255,255));
  272.  
  273.     SelectObject(hDC, hPen);
  274.     Rectangle(hDC, Rect.left+1, Rect.top+1, Rect.right, Rect.bottom);
  275.     SelectObject(hDC, GetStockObject(BLACK_PEN));
  276.  
  277.     DeleteObject(hPen);
  278.  
  279.     /* Draw an external black border on an icon without killing the client area. */
  280.     if (ClockDisp.bIconic) {
  281.         MoveToEx(hDC, Rect.left, Rect.top, NULL);
  282.         LineTo(hDC, Rect.left,  Rect.bottom);
  283.         LineTo(hDC, Rect.right, Rect.bottom);
  284.         LineTo(hDC, Rect.right, Rect.top);
  285.         LineTo(hDC, Rect.left,  Rect.top);
  286.     }
  287. }
  288.  
  289.  
  290. /*
  291.  *  DrawFace()
  292.  */
  293.  
  294. void NEAR PASCAL DrawFace(HDC hDC)
  295. {
  296. INT        i;
  297. RECT       tRect;
  298. INT        blobHeight, blobWidth;
  299.  
  300.     blobWidth = (INT)((MAXBLOBWIDTH * (LONG)(clockRect.right - clockRect.left)) / HorzRes);
  301.     blobHeight = (INT)((blobWidth * aspectN) / aspectD);
  302.  
  303.     if (blobHeight < 2)
  304.         blobHeight = 1;
  305.  
  306.     if (blobWidth < 2)
  307.         blobWidth = 2;
  308.  
  309.     InflateRect((LPRECT)&clockRect, -(blobHeight >> 1), -(blobWidth >> 1));
  310.  
  311.     clockRadius = (clockRect.right - clockRect.left-6) >> 1;
  312.     clockCenter.y = clockRect.top + ((clockRect.bottom - clockRect.top) >> 1);
  313.     clockCenter.x = clockRect.left + clockRadius+3;
  314.  
  315.     for (i=0; i < 60; i++) {
  316.         tRect.top  = (INT)(((LONG)(CirTab[i].cos) * clockRadius) / CLKSCALE + clockCenter.y);
  317.         tRect.left = (INT)(((LONG)(CirTab[i].sin) * clockRadius) / CLKSCALE + clockCenter.x);
  318.  
  319.         if (i % 5) {
  320.             /* Draw a dot. */
  321.             if (blobWidth > 2 && blobHeight >= 2) {
  322.                 tRect.right = tRect.left + 1;
  323.                 tRect.bottom = tRect.top + 1;
  324.                 FillRect(hDC, (LPRECT)&tRect, hbrForeground);
  325.             }
  326.         }
  327.         else {
  328.             tRect.right = tRect.left + blobWidth;
  329.             tRect.bottom = tRect.top + blobHeight;
  330.             OffsetRect((LPRECT)&tRect, -(blobWidth >> 1) , -(blobHeight >> 1));
  331.             FillRect(hDC, (LPRECT)&tRect, hbrForeground);
  332.         }
  333.     }
  334.     InflateRect((LPRECT)&clockRect, (blobHeight >> 1), (blobWidth >> 1));
  335. }
  336.  
  337.  
  338. /*
  339.  *  DrawHand() - Draw the second hand using XOR mode.
  340.  */
  341.  
  342. void NEAR PASCAL DrawHand(register HDC hDC,
  343.               INT          pos,
  344.               HPEN         hPen,
  345.               INT          scale,
  346.               INT          patMode)
  347. {
  348. INT       radius;
  349.  
  350.     MoveToEx(hDC, clockCenter.x, clockCenter.y, NULL);
  351.     radius = (INT)(((LONG)clockRadius * scale) / 100);
  352.     SetROP2(hDC, patMode);
  353.     SelectObject(hDC, hPen);
  354.  
  355.     LineTo(hDC, clockCenter.x + (INT)(((LONG)(CirTab[pos].sin) * (radius)) / CLKSCALE),
  356.         clockCenter.y + (INT)(((LONG)(CirTab[pos].cos) * (radius)) / CLKSCALE));
  357. }
  358.  
  359.  
  360. /*
  361.  *  DrawFatHand() - Draws either hour or minute hand.
  362.  */
  363.  
  364. void NEAR PASCAL DrawFatHand(register HDC hDC, INT pos, HPEN hPen, BOOL hHand)
  365. {
  366. register INT    m;
  367. INT        n;
  368. INT        scale;
  369. POINT      tip;
  370. POINT      stip;
  371.  
  372.     SetROP2(hDC, R2_COPYPEN);
  373.  
  374.     SelectObject(hDC, hPen);
  375.  
  376.     scale = hHand ? 7 : 5;
  377.  
  378.     n = (pos+15)%60;
  379.     m = (INT)((((LONG)clockRadius*scale) / 100));
  380.     stip.y = (INT)((LONG)(CirTab[n].cos) * m / CLKSCALE);
  381.     stip.x = (INT)((LONG)(CirTab[n].sin) * m / CLKSCALE);
  382.  
  383.     scale = hHand ? 65 : 80;
  384.     tip.y = (INT)((LONG)(CirTab[pos].cos) * (((LONG)clockRadius * scale) / 100) / CLKSCALE);
  385.     tip.x = (INT)((LONG)(CirTab[pos].sin) * (((LONG)clockRadius * scale) / 100) / CLKSCALE);
  386.  
  387.     MoveToEx(hDC, clockCenter.x+stip.x, clockCenter.y+stip.y, NULL);
  388.     LineTo(hDC, clockCenter.x+tip.x,  clockCenter.y+tip.y);
  389.     MoveToEx(hDC, clockCenter.x-stip.x, clockCenter.y-stip.y, NULL);
  390.     LineTo(hDC, clockCenter.x+tip.x,  clockCenter.y+tip.y);
  391.  
  392.     scale = hHand ? 15 : 20;
  393.  
  394.     n = (pos + 30) % 60;
  395.     m = (INT)(((LONG)clockRadius * scale) / 100);
  396.     tip.y = (INT)((LONG)(CirTab[n].cos) * m / CLKSCALE);
  397.     tip.x = (INT)((LONG)(CirTab[n].sin) * m / CLKSCALE);
  398.     MoveToEx(hDC, clockCenter.x+stip.x, clockCenter.y+stip.y, NULL);
  399.     LineTo(hDC, clockCenter.x+tip.x,  clockCenter.y+tip.y);
  400.     MoveToEx(hDC, clockCenter.x-stip.x, clockCenter.y-stip.y, NULL);
  401.     LineTo(hDC, clockCenter.x+tip.x,  clockCenter.y+tip.y);
  402. }
  403.  
  404.  
  405. /*
  406.  *  ClockPaint()
  407.  */
  408.  
  409. void NEAR PASCAL ClockPaint(HWND hWnd, register HDC hDC, INT hint)
  410. {
  411. INT     hour;
  412. CHAR    *pszTime;
  413. RECT    Rect;
  414. TIME    nTime;
  415. DWORD   rgbCol;
  416. HBRUSH  hBr;
  417.  
  418.     GetClientRect(hWnd, (LPRECT) &Rect);
  419.  
  420.     GetTime(&nTime);
  421.  
  422.     if (ClockDisp.wFormat == IDM_DIGITAL) {  /* Digital Display */
  423.         if (hint == REPAINT || ClockDisp.bIconic) {
  424.             SelectObject(hDC, GetStockObject(BLACK_BRUSH));
  425.             DrawBorder(hWnd, hDC);
  426.  
  427.             /* Set old values as undefined, so entire clock updated. */
  428.             oTime.hour24 = 25;
  429.             oTime.minute = 60;
  430.             oTime.ampm   = 2;
  431.         }
  432.  
  433.         if (oTime.hour24 != nTime.hour24) {
  434.             if (ClockDisp.wTimeFormat)
  435.                 hour = nTime.hour24;
  436.             else
  437.                 hour = nTime.hour12;
  438.  
  439.             ClockDisp.szDigTime[0] = (CHAR)('0' + hour / 10);
  440.             ClockDisp.szDigTime[1] = (CHAR)('0' + hour % 10);
  441.         }
  442.  
  443.         if (oTime.minute != nTime.minute) {
  444.             ClockDisp.szDigTime[3]  = (CHAR)('0' + nTime.minute / 10);
  445.             ClockDisp.szDigTime[4]  = (CHAR)('0' + nTime.minute % 10);
  446.         }
  447.  
  448.         /* Kill Leading zero if needed. */
  449.         if (nLeadZero == 0 && ClockDisp.szDigTime[0] == '0')
  450.             pszTime = ClockDisp.szDigTime + 1;
  451.         else
  452.             pszTime = ClockDisp.szDigTime;
  453.  
  454.         SetTextColor(hDC, (ClockDisp.bColor) ? RGB(0,255,0) : RGB(255,255,255));
  455.         SetBkColor(hDC, 0L);
  456.         SetTextAlign(hDC, TA_CENTER);
  457.  
  458.         ClockDisp.wDigTimeLen = (WORD)((ClockDisp.bIconic ? 5 : 8) + ClockDisp.szDigTime - pszTime);
  459.  
  460.         /* Is the font ready yet? */
  461.         if (hFont == 0 || ClockDisp.bNewFont)  {
  462.             /* Create a suitable font */
  463.             SizeFont(hWnd, Rect.bottom - Rect.top, Rect.right - Rect.left, ClockDisp.wDigTimeLen);
  464.             ClockDisp.bNewFont = FALSE;
  465.         }
  466.  
  467.         SelectObject(hDC, hFont);
  468.         ClockDisp.szDigTime[6] = (CHAR)('0' + nTime.second / 10);
  469.         ClockDisp.szDigTime[7] = (CHAR)('0' + nTime.second % 10);
  470.  
  471.         Rect.left += 4;
  472.         Rect.right -= 4;
  473.         Rect.top = ClockDisp.line1.y;
  474.         Rect.bottom = ClockDisp.yline2;
  475.         ExtTextOut(hDC, ClockDisp.line1.x, ClockDisp.line1.y, ETO_OPAQUE | ETO_CLIPPED,
  476.           (LPRECT)&Rect, pszTime, (UINT)ClockDisp.wDigTimeLen, (LPINT)NULL);
  477.         SelectObject(hDC, GetStockObject(SYSTEM_FONT));
  478.     }
  479.     else {
  480.         /* Analog display */
  481.         SetBkMode(hDC, TRANSPARENT);
  482.         if (hint == REPAINT) {
  483.             SetBkMode(hDC, OPAQUE);
  484.             /* When switching from Digital to analog, the brush selected
  485.              *  continued to be black; So, the current background is to be
  486.              * selected;
  487.              * Fix for Bug #6385 -- SANKAR -- 11-26-89 */
  488.             SelectObject(hDC, hbrBackground);
  489.  
  490.             /* Make a temp brush to color the background.  This is to
  491.              * force use of a solid color so the hand motion is painted
  492.              * correctly.
  493.              */
  494.             rgbCol = GetNearestColor(hDC, GetSysColor(COLOR_WINDOW));
  495.             hBr = CreateSolidBrush(rgbCol);
  496.  
  497.             FillRect(hDC, &Rect, hBr);
  498.  
  499.             DeleteObject(hBr);
  500.  
  501.             SetBkMode(hDC, TRANSPARENT);
  502.  
  503.             DrawBorder(hWnd, hDC);
  504.             DrawFace(hDC);
  505.             DrawFatHand(hDC, oTime.hour * 5 + (oTime.minute / 12), hpenForeground, HHAND);
  506.             DrawFatHand(hDC, oTime.minute, hpenForeground, MHAND);
  507.  
  508.             if (!ClockDisp.bIconic)       /* Draw the second hand. */
  509.                 DrawHand(hDC, oTime.second, hpenBackground, SECONDSCALE, R2_NOT);
  510.  
  511.             /* NOTE: Don't update oTime in this case! */
  512.  
  513.             return;
  514.         }
  515.         else if (hint == HANDPAINT) {
  516.             if ((!ClockDisp.bIconic) && nTime.second != oTime.second) /* Erase the old second hand. */
  517.                 DrawHand(hDC, oTime.second, hpenBackground, SECONDSCALE, R2_NOT);
  518.  
  519.             if (nTime.minute != oTime.minute || nTime.hour != oTime.hour) {
  520.                 if (ClockDisp.bIconic) {
  521.                     DrawHand(hDC, oTime.minute, hpenBackground, MINUTESCALE, R2_COPYPEN);
  522.                     DrawHand(hDC, oTime.hour * 5 + (oTime.minute / 12), hpenBackground, HOURSCALE, R2_COPYPEN);
  523.                     DrawHand(hDC, nTime.minute, hpenForeground, MINUTESCALE, R2_COPYPEN);
  524.                     DrawHand(hDC, nTime.hour * 5 + (nTime.minute / 12), hpenForeground, HOURSCALE, R2_COPYPEN);
  525.                 }
  526.                 else {
  527.                     DrawFatHand(hDC, oTime.minute, hpenBackground, MHAND);
  528.                     DrawFatHand(hDC, oTime.hour*5+(oTime.minute/12), hpenBackground, HHAND);
  529.                    DrawFatHand(hDC, nTime.minute, hpenForeground, MHAND);
  530.                     DrawFatHand(hDC, (nTime.hour) * 5 + (nTime.minute / 12), hpenForeground, HHAND );
  531.                 }
  532.             }
  533.  
  534.             if (!ClockDisp.bIconic && nTime.second != oTime.second) /* Draw new second hand */
  535.                 DrawHand(hDC, nTime.second, hpenBackground, SECONDSCALE, R2_NOT);
  536.         }
  537.     }
  538.     oTime = nTime;
  539. }
  540.  
  541.  
  542. /*
  543.  *  ClockTimer()
  544.  *
  545.  *  msg - timer ID
  546.  *
  547.  * Called by windows to tell CLOCK there has been a time change.
  548.  *
  549.  */
  550.  
  551. void NEAR PASCAL ClockTimer(HWND hWnd, UINT msg)
  552. {
  553. HDC    hDC;
  554. TIME    nTime;
  555.  
  556.     GetTime(&nTime);
  557.  
  558.     /* It's possible to change any part of the system at any time
  559.      * through the Control Panel.  So we check everything.
  560.      */
  561.     if (((nTime.second == oTime.second) || ClockDisp.bIconic) &&
  562.         (nTime.minute == oTime.minute)          &&
  563.         (nTime.hour24 == oTime.hour24))
  564.         return;
  565.  
  566.     hDC = GetDC(hWnd);
  567.     ClockPaint(hWnd, hDC, HANDPAINT);
  568.     ReleaseDC(hWnd, hDC);
  569.     DdePostAdvise(idInst, hszTime, hszNow);
  570.       UNREFERENCED_PARAMETER(msg);
  571. }
  572.  
  573.  
  574. /*
  575.  *  ClockCreate()
  576.  */
  577.  
  578. void NEAR PASCAL ClockCreate(HWND hWnd)
  579. {
  580. INT        i;
  581. register HDC    hDC;
  582. INT        HorzSize;
  583. INT        VertSize;
  584.  
  585.     hDC = GetDC(hWnd);
  586.     VertRes = GetDeviceCaps(hDC, VERTRES);
  587.     HorzRes = GetDeviceCaps(hDC, HORZRES);
  588.     VertSize= GetDeviceCaps(hDC, VERTSIZE);
  589.     HorzSize= GetDeviceCaps(hDC, HORZSIZE);
  590.     ReleaseDC(hWnd, hDC);
  591.  
  592.     aspectN = ((LONG)VertRes * 100) / (LONG)VertSize;
  593.     aspectD = ((LONG)HorzRes * 100) / (LONG)HorzSize;
  594.  
  595.     CreateTools();
  596.  
  597.     /* Scale sines for aspect ratio if this is the first instance */
  598.     for (i=0; i < 60; i++) {
  599.         CirTab[i].sin = (SHORT)((CirTab[i].sin * aspectN) / aspectD);
  600.     }
  601. }
  602.  
  603.  
  604.  
  605. /*
  606.  *  FormatInit() -  International initialization.
  607.  */
  608.  
  609. void NEAR PASCAL FormatInit(register HANDLE hInstance, BOOL fModeChange)
  610. {
  611. WORD i, ii;
  612. CHAR szWinHeader[21], szKeyName[21], szRetVal[21];
  613.  
  614.     for (i=0; i < 11; i++)
  615.         ClockDisp.szDigTime[i] = ' ';
  616.  
  617.     LoadString(hInstance, IDS_INTL, (LPSTR)szWinHeader, 20);
  618.  
  619.     LoadString(hInstance, IDS_ITIME, (LPSTR)szKeyName, 20);
  620.     ClockDisp.wTimeFormat = (WORD)GetProfileInt((LPSTR)szWinHeader, (LPSTR)szKeyName, 0);
  621.  
  622.     LoadString(hInstance, IDS_S1159, (LPSTR)szKeyName, 20);
  623.     LoadString(hInstance, IDS_1159, (LPSTR)szRetVal, 20);
  624.     i = (WORD)GetProfileString((LPSTR)szWinHeader, (LPSTR)szKeyName, (LPSTR)szRetVal, (LPSTR)&ClockDisp.szAMPM[0][0], 7);
  625.  
  626.     LoadString(hInstance, IDS_S2359, (LPSTR)szKeyName, 20);
  627.     LoadString(hInstance, IDS_2359, (LPSTR)szRetVal, 20);
  628.     ii = (WORD)GetProfileString((LPSTR)szWinHeader, (LPSTR)szKeyName, (LPSTR)szRetVal, (LPSTR)&ClockDisp.szAMPM[1][0], 7);
  629.  
  630.     nLeadZero = GetProfileInt((LPSTR)szWinHeader, "iTLzero", 0);
  631.  
  632.     LoadString(hInstance, IDS_STIME, (LPSTR)szKeyName, 20);
  633.     LoadString(hInstance, IDS_TIMESEP, (LPSTR)szRetVal, 20);
  634.  
  635.     GetProfileString((LPSTR)szWinHeader, (LPSTR)szKeyName, (LPSTR)szRetVal, (LPSTR)szRetVal, 20);
  636.     ClockDisp.cTimeSep = szRetVal[0];
  637.  
  638.     ClockDisp.szDigTime[2] = ClockDisp.cTimeSep;
  639.     ClockDisp.szDigTime[5] = ClockDisp.cTimeSep;
  640.  
  641.     LoadString(hInstance, IDS_USNAME, (LPSTR)szWinHeader, 20);
  642.     LoadString(hInstance, IDS_CLKFORMAT, (LPSTR)szKeyName, 20);
  643.  
  644.     /* We will read the new mode (DIGITAL/ANALOG) only during init time. */
  645.     if (fModeChange)
  646.     {
  647.         if (GetPrivateProfileInt((LPSTR)szWinHeader, (LPSTR)szKeyName, 1, (LPSTR)szIniFile))
  648.             ClockDisp.wFormat = IDM_ANALOG;
  649.         else
  650.             ClockDisp.wFormat = IDM_DIGITAL;
  651.     }
  652.  
  653.     if (ClockDisp.wFormat == IDM_ANALOG)
  654.         hbrBackground = hbrColorWindow;
  655.     else
  656.         hbrBackground = hbrColorBlack;
  657.  
  658.     ClockDisp.wDigTimeLen = 2+1+2+1+2+1;
  659.  
  660.     if (!ClockDisp.wTimeFormat)
  661.         ClockDisp.wDigTimeLen += ((i > ii) ? (i) : (ii));
  662. }
  663.  
  664.  
  665. /*
  666.  *  ClockInit()
  667.  */
  668.  
  669. BOOL NEAR PASCAL ClockInit(HANDLE hInstance)
  670. {
  671. HDC       hDC;
  672. WNDCLASS  ClockClass;
  673.  
  674.     FormatInit(hInstance, TRUE);
  675.  
  676.     ClockClass.style         = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
  677.     ClockClass.lpfnWndProc   = ClockWndProc;
  678.     ClockClass.cbClsExtra    = 0;
  679.     ClockClass.cbWndExtra    = 0;
  680.     ClockClass.hInstance     = hInstance;
  681.     ClockClass.hIcon         = NULL;
  682.     ClockClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
  683.     ClockClass.hbrBackground = (HBRUSH) NULL;
  684.     ClockClass.lpszMenuName  = (LPSTR)"Clock";
  685.     ClockClass.lpszClassName = (LPSTR)"Clock";
  686.  
  687.     if (!RegisterClass((LPWNDCLASS)&ClockClass))
  688.         return(FALSE);
  689.  
  690.     LoadString(hInstance, IDS_FONTFILE, (LPSTR)szFontFile, 20);
  691.     AddFontResource(szFontFile);
  692.  
  693.     /* Check the number of colors that the display is capable of. */
  694.     hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
  695.     ClockDisp.bColor = (GetDeviceCaps(hDC, NUMCOLORS) > 2);
  696.     ClockDisp.bColor = FALSE;
  697.     DeleteDC(hDC);
  698.  
  699.     ClockDisp.bTmpHide = FALSE;
  700.     ClockDisp.bNewFont = FALSE;
  701.     ClockDisp.bColor   = TRUE;
  702.     ClockDisp.bNewFont = TRUE;
  703.  
  704.     return(TRUE);
  705. }
  706.  
  707. /*
  708.  *  SetMenuBar() - places or removes the menu bar, etc.
  709.  *
  710.  *  Based on the flag ClockDisp.bNoTitle (ie: do we want a menu/title
  711.  *  bar or not?), adds or removes the window title and menu bar:
  712.  *    Gets current style, toggles the bits, and re-sets the style.
  713.  *    Must then resize the window frame and show it.
  714.  */
  715.  
  716. void NEAR PASCAL SetMenuBar( HWND hWnd )
  717. {
  718.     static DWORD wID;
  719.     DWORD   dwStyle;
  720.  
  721.     dwStyle = GetWindowLong( hWnd, GWL_STYLE );
  722.     if( ClockDisp.bNoTitle ) {
  723.         /* remove caption & menu bar, etc. */
  724.         dwStyle &= ~(WS_DLGFRAME | WS_SYSMENU |
  725.                    WS_MINIMIZEBOX | WS_MAXIMIZEBOX );
  726.         wID = SetWindowLong( hWnd, GWL_ID, 0 );
  727.     }
  728.     else {
  729.         /* put menu bar & caption back in */
  730.         dwStyle = WS_TILEDWINDOW | dwStyle;
  731.         SetWindowLong( hWnd, GWL_ID, wID );
  732.     }
  733.     SetWindowLong( hWnd, GWL_STYLE, dwStyle );
  734.     SetWindowPos( hWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE |
  735.         SWP_NOZORDER | SWP_FRAMECHANGED );
  736.     ShowWindow( hWnd, SW_SHOW );
  737. }
  738.  
  739. /*
  740.  *  AboutDlgProc()
  741.  */
  742.  
  743. BOOL APIENTRY AboutDlgProc ( hwnd, msg, wParam, lParam )
  744. HWND          hwnd;
  745. UINT msg;
  746. WPARAM wParam;
  747. LPARAM lParam;
  748. {
  749.     switch (msg) {
  750.         case WM_INITDIALOG:
  751.             /* nothing to initialize */
  752.             break;
  753.  
  754.         case WM_COMMAND:
  755.             switch (LOWORD(wParam)) {
  756.                 case IDOK:
  757.                 case IDCANCEL:
  758.                     EndDialog(hwnd, 0);
  759.                     break;
  760.  
  761.                 default:
  762.                     return FALSE;
  763.             }
  764.             break;
  765.  
  766.         default:
  767.             return(FALSE);
  768.     }
  769.  
  770.     return TRUE;
  771. }
  772.  
  773.  
  774.  
  775. /*
  776.  *  ClockWndProc()
  777.  */
  778.  
  779. LONG APIENTRY ClockWndProc(register HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  780. {
  781. HMENU       hMenu;
  782. static CHAR szAppName[12];  /* application name buffer */
  783. PAINTSTRUCT ps;
  784.  
  785.     switch (message) {
  786.         case WM_COMMAND:
  787.         switch (wParam)  {
  788.  
  789.             case IDM_ANALOG:
  790.             case IDM_DIGITAL:
  791.                 if ((WORD)wParam != ClockDisp.wFormat) {
  792.                     /* Switch flag to other choice */
  793.                     hMenu = GetMenu(hWnd);
  794.                     CheckMenuItem(hMenu, ClockDisp.wFormat, MF_BYCOMMAND | MF_UNCHECKED);
  795.                     CheckMenuItem(hMenu, ClockDisp.wFormat = (WORD) wParam, MF_BYCOMMAND | MF_CHECKED);
  796.                     InvalidateRect(hWnd, (LPRECT) NULL, TRUE);
  797.                 }
  798.             break;
  799.  
  800.             case IDM_NOTITLE:
  801.                 goto toggle_title;
  802.  
  803.             case IDM_ABOUT: {
  804.                 DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUT), hWnd, (DLGPROC)AboutDlgProc);
  805.                 break;
  806.             }
  807.  
  808.             default:
  809.                 goto defproc;
  810.         }
  811.         break;
  812.  
  813.         case WM_SIZE:
  814.             ClockDisp.bNewFont = TRUE;
  815.             ClockSize(hWnd, LOWORD(lParam), HIWORD(lParam), (WORD)wParam);
  816.             UpdateWindow(hWnd);
  817.             break;
  818.  
  819.         case WM_QUERYDRAGICON:
  820.             return (LONG)LoadIcon(hInst, "cckk");
  821.  
  822.         case WM_CLOSE:
  823.         case WM_ENDSESSION:
  824.           DestroyWindow( hWnd );
  825.           break;
  826.  
  827.         case WM_DESTROY: {
  828.             CHAR           szInt[10];
  829.             HCURSOR        hTempCursor;
  830.  
  831.             KillTimer(hWnd, TimerID);
  832.             DeleteTools();
  833.             if (hFont)
  834.                 DeleteObject(hFont);
  835.             RemoveFontResource(szFontFile);
  836.  
  837.             SetCapture(hWnd);
  838.             hTempCursor=SetCursor(LoadCursor(NULL, IDC_WAIT));
  839.  
  840.             if (!(IsIconic(hWnd) || IsZoomed(hWnd)))
  841.                 GetWindowRect(hWnd, &rCoordRect);
  842.  
  843.             /* Write new configuration to DDEMLCLK.INI */
  844.             LoadString(hInst, IDS_CLKFORMAT, (LPSTR)szBuffer, BUFLEN-1);
  845.             szInt[0] = (CHAR)('0' + (ClockDisp.wFormat == IDM_ANALOG));
  846.             szInt[1] = 0;
  847.             WritePrivateProfileString((LPSTR)szSection,
  848.                                       (LPSTR)szBuffer,
  849.                                       (LPSTR)szInt,
  850.                                       (LPSTR)szIniFile);
  851.             wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)IsIconic(hWnd));
  852.             WritePrivateProfileString((LPSTR)szSection,
  853.                                       (LPSTR)"Minimized",
  854.                                       (LPSTR)szInt,
  855.                                       (LPSTR)szIniFile);
  856.             wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)IsZoomed(hWnd));
  857.             WritePrivateProfileString((LPSTR)szSection,
  858.                                       (LPSTR)"Maximized",
  859.                                       (LPSTR)szInt,
  860.                                       (LPSTR)szIniFile);
  861.             wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)rCoordRect.left);
  862.             WritePrivateProfileString((LPSTR)szSection,
  863.                                       (LPSTR)"Left",
  864.                                       (LPSTR)szInt,
  865.                                       (LPSTR)szIniFile);
  866.             wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)rCoordRect.top);
  867.             WritePrivateProfileString((LPSTR)szSection,
  868.                                       (LPSTR)"Top",
  869.                                       (LPSTR)szInt,
  870.                                       (LPSTR)szIniFile);
  871.             wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)rCoordRect.right);
  872.             WritePrivateProfileString((LPSTR)szSection,
  873.                                       (LPSTR)"Right",
  874.                                       (LPSTR)szInt,
  875.                                       (LPSTR)szIniFile);
  876.             wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)rCoordRect.bottom);
  877.             WritePrivateProfileString((LPSTR)szSection,
  878.                                       (LPSTR)"Bottom",
  879.                                       (LPSTR)szInt,
  880.                                       (LPSTR)szIniFile);
  881.             wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)ClockDisp.bTopMost);
  882.             WritePrivateProfileString((LPSTR)szSection,
  883.                                       (LPSTR)"TopMost",
  884.                                       (LPSTR)szInt,
  885.                                       (LPSTR)szIniFile);
  886.             wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)ClockDisp.bNoTitle);
  887.             WritePrivateProfileString((LPSTR)szSection,
  888.                                       (LPSTR)"NoTitle",
  889.                                       (LPSTR)szInt,
  890.                                       (LPSTR)szIniFile);
  891.             PostQuitMessage(0);
  892.             break;
  893.         }
  894.  
  895.         case WM_WININICHANGE:
  896.             /* FALSE indicates that we don't want to change the display format */
  897.             FormatInit(hInst, FALSE);
  898.             InvalidateRect(hWnd, (LPRECT)NULL, TRUE);
  899.             break;
  900.  
  901.         case WM_PAINT:
  902.             /* Added to force total repaint to solve
  903.              * problem of garbage under second hand when hidden
  904.              * by menu or popup.
  905.              */
  906.             InvalidateRect(hWnd, (LPRECT)NULL, TRUE);
  907.             BeginPaint(hWnd, (LPPAINTSTRUCT)&ps);
  908.  
  909.             if (ClockDisp.wFormat == IDM_DIGITAL) {
  910.                 hbrBackground = hbrColorBlack;
  911.                 FillRect(ps.hdc, (LPRECT)&clockRect, hbrBackground);
  912.             }
  913.             else
  914.                 hbrBackground=hbrColorWindow;
  915.  
  916.             ClockPaint(hWnd, ps.hdc, REPAINT);
  917.             EndPaint(hWnd, (LPPAINTSTRUCT)&ps);
  918.             break;
  919.  
  920.         case WM_TIMECHANGE:
  921.             /* Redraw. */
  922.             InvalidateRect(hWnd, (LPRECT)NULL, TRUE);
  923.  
  924.         case WM_TIMER:
  925.             ClockTimer(hWnd, (WORD)wParam);
  926.             break;
  927.  
  928.         case WM_SYSCOMMAND:
  929.             switch (wParam)  {
  930.  
  931.                 case SC_MINIMIZE:
  932.                     if (!IsZoomed(hWnd))
  933.                         GetWindowRect(hWnd, (LPRECT)&rCoordRect);
  934.                     ClockDisp.bMinimized = TRUE;
  935.                     ClockDisp.bMaximized = FALSE;
  936.                     break;
  937.                 case SC_MAXIMIZE:
  938.                     if (!IsIconic(hWnd))
  939.                         GetWindowRect(hWnd, (LPRECT)&rCoordRect);
  940.                     ClockDisp.bMinimized = FALSE;
  941.                     ClockDisp.bMaximized = TRUE;
  942.                     break;
  943.  
  944.                 case IDM_TOPMOST: {
  945.                     /* toggles topmost option
  946.                      */
  947.                     hMenu = GetSystemMenu(hWnd, FALSE);
  948.                     if( ClockDisp.bTopMost )  {
  949.                         CheckMenuItem( hMenu, IDM_TOPMOST, MF_BYCOMMAND | MF_UNCHECKED );
  950.                         SetWindowPos( hWnd, HWND_NOTOPMOST, 0, 0, 0, 0,
  951.                                        SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  952.                         ClockDisp.bTopMost = FALSE;
  953.                     }
  954.                     else {
  955.                         CheckMenuItem( hMenu, IDM_TOPMOST, MF_BYCOMMAND | MF_CHECKED );
  956.                         SetWindowPos( hWnd, HWND_TOPMOST, 0, 0, 0, 0,
  957.                                          SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  958.                         ClockDisp.bTopMost = TRUE;
  959.                     }
  960.                     break;
  961.                 }
  962.  
  963.             }
  964.             return(DefWindowProc(hWnd, message, wParam, lParam));
  965.             break;
  966.  
  967.         case WM_MOUSEACTIVATE:
  968.             /* right button temporarily hides the window if topmost is
  969.              * enabled (window re-appears when right button is released).
  970.              * When this happens, we don't want to activate the clock window
  971.              * just before hiding it (it would look really bad), so we
  972.              * intercept the activate message.
  973.              */
  974.             if( GetAsyncKeyState( VK_RBUTTON ) & 0x8000 )
  975.                 return( MA_NOACTIVATE );
  976.             else
  977.                 goto defproc;
  978.             break;
  979.  
  980.         case WM_RBUTTONDOWN:
  981.         case WM_NCRBUTTONDOWN:
  982.             /* right button temporarily hides the window, if the window
  983.              * is topmost, and if no menu is currently "active"
  984.              */
  985.             if( !ClockDisp.bTmpHide && ClockDisp.bTopMost ) {
  986.                 ShowWindow( hWnd, SW_HIDE );
  987.                 SetCapture( hWnd );
  988.                 ClockDisp.bTmpHide = TRUE;
  989.             }
  990.             break;
  991.  
  992.         case WM_RBUTTONUP:
  993.         case WM_NCRBUTTONUP:
  994.             /* if window is currently hidden, right button up brings it
  995.              * back. Must make sure we show it in its previous state - ie:
  996.              * minimized, maximized or normal.
  997.              */
  998.             if( ClockDisp.bTmpHide ) {
  999.                 ReleaseCapture();
  1000.                 if( IsIconic(hWnd) )
  1001.                     ShowWindow( hWnd, SW_SHOWMINNOACTIVE );
  1002.                 else if( IsZoomed( hWnd ) )
  1003.                     ShowWindow( hWnd, SW_SHOWMAXIMIZED );
  1004.                 else
  1005.                     ShowWindow( hWnd, SW_SHOWNOACTIVATE );
  1006.                 ClockDisp.bTmpHide = FALSE;
  1007.             }
  1008.             break;
  1009.  
  1010.         case WM_KEYDOWN:
  1011.             /* ESC key toggles the menu/title bar (just like a double click
  1012.              * on the client area of the window.
  1013.              */
  1014.             if( (wParam == VK_ESCAPE) && !(HIWORD( lParam ) & 0x4000) )
  1015.                 goto toggle_title;
  1016.             break;
  1017.  
  1018.         case WM_NCLBUTTONDBLCLK:
  1019.             if( !ClockDisp.bNoTitle )
  1020.                 /* if we have title bars etc. let the normal stuff take place */
  1021.                 goto defproc;
  1022.  
  1023.             /* else: no title bars, then this is actually a request to bring
  1024.              * the title bars back...
  1025.              */
  1026.  
  1027.             /* fall through */
  1028.  
  1029.         case WM_LBUTTONDBLCLK:
  1030. toggle_title:
  1031.             ClockDisp.bNoTitle = (ClockDisp.bNoTitle ? FALSE : TRUE );
  1032.             SetMenuBar( hWnd );
  1033.            break;
  1034.  
  1035.         case WM_NCHITTEST:
  1036.             /* if we have no title/menu bar, clicking and dragging the client
  1037.              * area moves the window. To do this, return HTCAPTION.
  1038.              * Note dragging not allowed if window maximized, or if caption
  1039.              * bar is present.
  1040.              */
  1041.             wParam = DefWindowProc(hWnd, message, wParam, lParam);
  1042.             if( ClockDisp.bNoTitle && (wParam == HTCLIENT) && !IsZoomed(hWnd) )
  1043.                 return HTCAPTION;
  1044.             else
  1045.               return wParam;
  1046.  
  1047.         case WM_SYSCOLORCHANGE:
  1048.             DeleteTools();
  1049.             CreateTools();
  1050.             break;
  1051.  
  1052.         case WM_ERASEBKGND: {
  1053.             RECT rect;
  1054.  
  1055.             GetClientRect(hWnd, (LPRECT)&rect);
  1056.             SelectObject((HDC)wParam, hbrBackground);
  1057.             FillRect((HDC)wParam, (LPRECT)&rect, hbrBackground);
  1058.             break;
  1059.         }
  1060.  
  1061.         default:
  1062. defproc:
  1063.         return(DefWindowProc(hWnd, message, wParam, lParam));
  1064.     }
  1065.     return(0L);
  1066. }
  1067.  
  1068.  
  1069. /*
  1070.  *  WinMain()
  1071.  */
  1072.  
  1073. INT PASCAL WinMain(
  1074. HINSTANCE hInstance,
  1075. HINSTANCE hPrev,
  1076. LPSTR lpszCmdLine,
  1077. INT cmdShow)
  1078. {
  1079. register HWND hWnd;
  1080. MSG           msg;
  1081. TIME          nTime;
  1082. PSTR          szTooMany;
  1083. HMENU         hMenu;
  1084. CHAR          szTopmost[80];
  1085.  
  1086.     LoadString(hInstance, IDS_USNAME, (LPSTR)szBuffer, BUFLEN);
  1087.     LoadString(hInstance, IDS_INIFILE, (LPSTR)szIniFile, 20);
  1088.     LoadString(hInstance, IDS_USNAME, (LPSTR)szSection, 30);
  1089.  
  1090.     if (!ClockInit(hInstance))
  1091.         return(FALSE);
  1092.  
  1093.     ClockCreate((HWND)NULL);
  1094.  
  1095.     LoadString(hInstance, IDS_APPNAME, (LPSTR)szBuffer, BUFLEN);
  1096.  
  1097.     rCoordRect.top=GetPrivateProfileInt((LPSTR)szSection,
  1098.                                          (LPSTR)"Top",
  1099.                                          (DWORD)-1, (LPSTR)szIniFile);
  1100.     rCoordRect.left=GetPrivateProfileInt((LPSTR)szSection,
  1101.                                          (LPSTR)"Left",
  1102.                                          (DWORD)-1, (LPSTR)szIniFile);
  1103.     rCoordRect.right=GetPrivateProfileInt((LPSTR)szSection,
  1104.                                          (LPSTR)"Right",
  1105.                                          (DWORD)-1, (LPSTR)szIniFile);
  1106.     rCoordRect.bottom=GetPrivateProfileInt((LPSTR)szSection,
  1107.                                          (LPSTR)"Bottom",
  1108.                                          (DWORD)-1, (LPSTR)szIniFile);
  1109.  
  1110.     hWnd = CreateWindow((LPSTR)"Clock",    /* The class name.           */
  1111.             (LPSTR)szBuffer,               /* The window instance name. */
  1112.             WS_TILEDWINDOW,
  1113.             (rCoordRect.left < 0) ? CW_USEDEFAULT : rCoordRect.left,
  1114.             (rCoordRect.top  < 0) ? CW_USEDEFAULT : rCoordRect.top,
  1115.             (rCoordRect.left < 0) ? (INT)( (HorzRes/3) + GetSystemMetrics(SM_CXFRAME)*2 )
  1116.                   : rCoordRect.right - rCoordRect.left,
  1117.             (rCoordRect.left < 0) ? (INT)( (((HorzRes/3)*aspectN)/aspectD)+GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME)*2 )
  1118.                   : rCoordRect.bottom - rCoordRect.top,
  1119.             NULL,
  1120.             NULL,
  1121.             hInstance,
  1122.             (LPSTR)NULL);
  1123.  
  1124.     hWindow=hWnd;
  1125.  
  1126.     // Loop if control panel time being changed.
  1127.     GetTime((TIME *)&nTime);
  1128.     do {
  1129.         GetTime((TIME *)&oTime);
  1130.     } while (nTime.second == oTime.second && nTime.minute == oTime.minute &&
  1131.            nTime.hour24 == oTime.hour24);
  1132.  
  1133.     if (!SetTimer(hWnd, TimerID, OPEN_TLEN, 0L) )  {
  1134.         /* Windows only supports 16 public timers */
  1135.         szTooMany = (PSTR)LocalAlloc(LPTR, 160);
  1136.         LoadString(hInstance, IDS_TOOMANY, (LPSTR)szTooMany, 160);
  1137.         MessageBox((HWND)NULL, (LPSTR)szTooMany, (LPSTR)szBuffer, MB_OK | MB_ICONHAND | MB_SYSTEMMODAL);
  1138.         DeleteTools();
  1139.         return(FALSE);
  1140.     }
  1141.  
  1142.     /* Add the topmost system menu item */
  1143.     hMenu = GetSystemMenu( hWnd, FALSE );
  1144.     AppendMenu( hMenu, MF_SEPARATOR, 0, NULL );
  1145.     LoadString(hInstance, IDS_TOPMOST, szTopmost, 79);
  1146.  
  1147.     /* Check the default setting to the clock as topmost or not */
  1148.     ClockDisp.bTopMost=GetPrivateProfileInt((LPSTR)szSection,
  1149.                                          (LPSTR)"TopMost",
  1150.                                          0, (LPSTR)szIniFile);
  1151.     if( ClockDisp.bTopMost ) {
  1152.         AppendMenu( hMenu, MF_ENABLED | MF_CHECKED | MF_STRING, IDM_TOPMOST,
  1153.                     szTopmost );
  1154.         SetWindowPos( hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
  1155.     }
  1156.     else
  1157.         AppendMenu( hMenu, MF_ENABLED | MF_UNCHECKED | MF_STRING, IDM_TOPMOST,
  1158.                     szTopmost );
  1159.  
  1160.     /* Check the default menu item either analog or digital */
  1161.     CheckMenuItem(GetMenu(hWnd), ClockDisp.wFormat, MF_BYCOMMAND | MF_CHECKED);
  1162.  
  1163.     /* Check the default setting to show title bar or not */
  1164.     ClockDisp.bNoTitle=GetPrivateProfileInt((LPSTR)szSection,
  1165.                                          (LPSTR)"NoTitle",
  1166.                                          0, (LPSTR)szIniFile);
  1167.     if( ClockDisp.bNoTitle ) {
  1168.         SetMenuBar( hWnd );
  1169.     }
  1170.  
  1171.     hInst = hInstance;
  1172.  
  1173.     /* Check the default minimized state, minimized or not */
  1174.     ClockDisp.bMinimized=GetPrivateProfileInt((LPSTR)szSection,
  1175.                                          (LPSTR)"Minimized",
  1176.                                          0, (LPSTR)szIniFile);
  1177.     if (!ClockDisp.bMinimized) {
  1178.         ClockDisp.bMaximized=GetPrivateProfileInt((LPSTR)szSection,
  1179.                                          (LPSTR)"Maximized",
  1180.                                          0, (LPSTR)szIniFile);
  1181.         if (ClockDisp.bMaximized)
  1182.             ShowWindow(hWnd, SW_MAXIMIZE);
  1183.         else {
  1184.             ShowWindow(hWnd, cmdShow);
  1185.             GetWindowRect(hWnd, (LPRECT)&rCoordRect);
  1186.         }
  1187.     }
  1188.     else
  1189.         ShowWindow(hWnd, SW_MINIMIZE);
  1190.  
  1191.     DdeInitialize(&idInst, (PFNCALLBACK)MakeProcInstance((FARPROC)DdeCallback, hInstance),
  1192.             CBF_FAIL_EXECUTES | CBF_SKIP_ALLNOTIFICATIONS, 0L);
  1193.     hszTime = DdeCreateStringHandle(idInst, "Time", 0);
  1194.     hszNow = DdeCreateStringHandle(idInst, "Now", 0);
  1195.     hszClock = DdeCreateStringHandle(idInst, "Clock", 0);
  1196.     DdeNameService(idInst, hszClock, 0L, DNS_REGISTER);
  1197.  
  1198.  
  1199.  
  1200.     while (GetMessage((LPMSG)&msg, NULL, 0, 0) ) {
  1201.         TranslateMessage((LPMSG)&msg);
  1202.         DispatchMessage((LPMSG)&msg);
  1203.     }
  1204.  
  1205.     DdeUninitialize(idInst);
  1206.  
  1207.     return(msg.wParam);
  1208. }
  1209.  
  1210.  
  1211. /*
  1212.  *  GetTime()
  1213.  */
  1214.  
  1215. VOID GetTime(
  1216. TIME *ptime)
  1217. {
  1218.     time_t t;
  1219.     struct tm *ptm;
  1220.  
  1221.     time(&t);
  1222.     ptm = localtime(&t);
  1223.     ptime->second = ptm->tm_sec;
  1224.     ptime->minute = ptm->tm_min;
  1225.     ptime->hour12 =
  1226.     ptime->hour = ptm->tm_hour > 12 ? ptm->tm_hour - 12 : ptm->tm_hour;
  1227.     ptime->hour24 = ptm->tm_hour;
  1228.     ptime->ampm = ptm->tm_hour > 12 ? 1 : 0;
  1229. }
  1230.  
  1231.  
  1232. /*
  1233.  *  DdeCallback()
  1234.  */
  1235.  
  1236. HDDEDATA CALLBACK DdeCallback(
  1237. WORD usType,
  1238. WORD usFmt,
  1239. HCONV hConv,
  1240. HSZ hsz1,
  1241. HSZ hsz2,
  1242. HDDEDATA hData,
  1243. DWORD lData1,
  1244. DWORD lData2)
  1245. {
  1246.  
  1247. static HANDLE           hToken;
  1248. static TOKEN_PRIVILEGES tp;
  1249. static LUID             luid;
  1250.  
  1251.     if (usType == XTYP_CONNECT) {
  1252.         return((HDDEDATA)TRUE);
  1253.     }
  1254.  
  1255.     if (usType == XTYP_WILDCONNECT) {
  1256.         HDDEDATA hData;
  1257.         HSZPAIR FAR *phszp;
  1258.         DWORD cb;
  1259.  
  1260.         if ((!hsz1 || hsz1 == hszTime) && (!hsz2 || hsz2 == hszClock)) {
  1261.             if ((hData = DdeCreateDataHandle(idInst, NULL,
  1262.                     2 * sizeof(HSZPAIR), 0L, 0, 0, 0))) {
  1263.                 phszp = (HSZPAIR FAR *)DdeAccessData(hData, &cb);
  1264.                 phszp[0].hszSvc = hszClock;
  1265.                 phszp[0].hszTopic = hszTime;
  1266.                 phszp[1].hszSvc = phszp[1].hszTopic = 0;
  1267.                 DdeUnaccessData(hData);
  1268.                 return(hData);
  1269.             }
  1270.         }
  1271.         return(0);
  1272.     }
  1273.  
  1274.     if (usFmt == CF_TEXT) {
  1275.         CHAR sz[40];
  1276.  
  1277.         if (usType == XTYP_ADVSTART || usType == XTYP_ADVSTOP) {
  1278.             return((HDDEDATA)TRUE);
  1279.         }
  1280.  
  1281.         if (hsz1 == hszTime && hsz2 == hszNow) {
  1282.             if (usType == XTYP_REQUEST || usType == XTYP_ADVREQ) {
  1283.  
  1284.                 itoa(oTime.hour, sz, 10);
  1285.                 strcat(sz, ":");
  1286.                 itoa(oTime.minute, &sz[strlen(sz)], 10);
  1287.                 strcat(sz, ":");
  1288.                 itoa(oTime.second, &sz[strlen(sz)], 10);
  1289.                 return(DdeCreateDataHandle(idInst, (LPBYTE)sz, strlen(sz) + 1, 0L,
  1290.                         hszNow, CF_TEXT, 0));
  1291.             }
  1292.             if (usType == XTYP_POKE) {
  1293.                 SYSTEMTIME SysTime;
  1294.  
  1295.                 DdeGetData(hData, (LPBYTE)sz, 40L, 0L);
  1296.                 GetLocalTime(&SysTime);
  1297.                 sscanf(sz, "%2d:%2d:%2d", &SysTime.wHour, &SysTime.wMinute, &SysTime.wSecond);
  1298.  
  1299.                 /* enable system-time privilege, set time, disable privilege */
  1300.                 OpenProcessToken( GetCurrentProcess(),
  1301.                   TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) ;
  1302.                 LookupPrivilegeValue( NULL, "SeSystemTimePrivilege", &luid );
  1303.                 tp.PrivilegeCount           = 1;
  1304.                 tp.Privileges[0].Luid       = luid;
  1305.                 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  1306.                 AdjustTokenPrivileges( hToken, FALSE, &tp,
  1307.                   sizeof(TOKEN_PRIVILEGES), NULL, NULL );
  1308.                 SetLocalTime(&SysTime);
  1309.                 AdjustTokenPrivileges( hToken, TRUE, &tp,
  1310.                   sizeof(TOKEN_PRIVILEGES), NULL, NULL );
  1311.  
  1312.                 DdePostAdvise(idInst, hszTime, hszNow);
  1313.                 return((HDDEDATA)DDE_FACK);
  1314.             }
  1315.         }
  1316.     }
  1317.     return(0);
  1318.  
  1319.     UNREFERENCED_PARAMETER(lData1);
  1320.     UNREFERENCED_PARAMETER(lData2);
  1321.     UNREFERENCED_PARAMETER(hConv);
  1322. }
  1323.