home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c480 / 18.ddi / SAMPLES / DDEML / CLOCK / DDEMLCLK.C_ / DDEMLCLK.C
Encoding:
C/C++ Source or Header  |  1993-02-08  |  32.3 KB  |  861 lines

  1. /*
  2.  * DDEML CLOCK
  3.  *
  4.  * This application shows how to use DDEML to create a simple DDE server.
  5.  *
  6.  * This code uses the wrapper code to further simplify DDEML use for
  7.  * simple applications.  See wrapper.h for details on its interface.
  8.  */
  9.  
  10. #include <windows.h>
  11. #include <ddeml.h> 
  12. #include <string.h>
  13. #include "wrapper.h" 
  14. #include "ddemlclk.h"
  15. #include <stdlib.h>
  16. #include <stdio.h>
  17.  
  18. BOOL PASCAL PokeTime(HDDEDATA hData);
  19. HDDEDATA PASCAL RequestTime(HDDEDATA hDataOut);
  20. HDDEDATA PASCAL RequestHelp(HDDEDATA hDataOut);
  21.  
  22. DDEFORMATTBL ClockSystopicHelpFormats[] = {
  23.     "TEXT", CF_TEXT, 0, NULL, RequestHelp
  24. };
  25.  
  26. DDEITEMTBL ClockSystopicItems[] = {
  27.     SZDDESYS_ITEM_HELP, 0, NULL, 1, 0, ClockSystopicHelpFormats
  28. }; 
  29.  
  30. DDEFORMATTBL ClockTimeNowFormats[] = {
  31.     { "TEXT",   CF_TEXT, 0, PokeTime, RequestTime },
  32.     { "Dummy1", CF_TEXT, 0, PokeTime, RequestTime },
  33.     { "Dummy2", CF_TEXT, 0, PokeTime, RequestTime },
  34. };
  35.  
  36. DDEITEMTBL ClockTimeItems[] = {
  37.     "Now", 0, NULL, 3, 0, ClockTimeNowFormats
  38. };
  39.  
  40. DDETOPICTBL ClockTopics[] = {
  41.     { "Time",         0, 1, 0, ClockTimeItems     },
  42.     { SZDDESYS_TOPIC, 0, 1, 0, ClockSystopicItems },
  43. };
  44.  
  45. DDESERVICETBL MyServiceInfo[] = {
  46.     "Clock", 0, 2, 0, ClockTopics
  47. };
  48.  
  49.  
  50. DWORD idInst = 0;
  51. /* Structure for holding time (in hours, minutes, and seconds) */
  52.  
  53.  
  54. TIME oTime;             /* the time currently displayed on the clock          */
  55.  
  56. HBRUSH hbrForegnd;      /* foreground brush -- system window text color       */
  57. HBRUSH hbrBackgnd;      /* background brush -- system window backbround color */
  58. HPEN   hpenForegnd;     /* foreground pen   -- system window text color       */
  59. HPEN   hpenBackgnd;     /* background pen   -- system window background color */
  60.  
  61. HANDLE hInst;           /* instance of the CLOCK program being executed       */
  62. BOOL   bFirst = TRUE;   /* TRUE if this is the 1st instance; FALSE otherwise  */
  63.  
  64. HANDLE    hCirTab;      /* Circle table for the circular clock face positions */
  65. POINT FAR *lpCirTab;    /* Pointer to the circle table                        */
  66.  
  67. int   TimerID = 1;      /* number used for timer-id                           */
  68. char  szBuffer[BUFLEN]; /* buffer for stringtable data                        */
  69. RECT  ClockRect;        /* rectangle that EXACTLY bounds the clock face       */
  70. long  ClockRadius;      /* clock face radius                                  */
  71. POINT ClockCenter;      /* clock face center                                  */
  72. BOOL  bIconic = FALSE;  /* TRUE if clock is currently iconic; FALSE otherwise */
  73.  
  74. int   HorzRes;          /* width of the display (in pixels)                   */
  75. int   VertRes;          /* height of the display (in raster lines)            */
  76. long  AspectH;          /* number of pixels per decimeter on the display      */
  77. long  AspectV;          /* number of raster lines per decimeter on the display*/
  78.  
  79. /***************************************************************************
  80.  *                                                                         *
  81.  *  MACRO    : HourHandPos (TIME)                                          *
  82.  *                                                                         *
  83.  *  PURPOSE  : Computes the hour hand position based on both the hour and  *
  84.  *             minute values in the given time record.                     *
  85.  *                                                                         *
  86.  ***************************************************************************/
  87.  
  88. #define HourHandPos(time)  (time.hour * 5) + (time.minute /12)
  89.  
  90.  
  91. /***************************************************************************
  92.  *                                                                         *
  93.  *  MACRO    : VertEquiv (int)                                             *
  94.  *                                                                         *
  95.  *  PURPOSE  : Computes the raster line (vertical) equivalent to the given *
  96.  *             pixel (horizontal) value.                                   *
  97.  *                                                                         *
  98.  ***************************************************************************/
  99.  
  100. #define VertEquiv(lengthH) ((lengthH * AspectV) / AspectH)
  101.  
  102.  
  103. /***************************************************************************
  104.  *                                                                         *
  105.  *  MACRO    : HorzEquiv (int)                                             *
  106.  *                                                                         *
  107.  *  PURPOSE  : Computes the pixel (horizontal) equivalent to the given     *
  108.  *             raster line (vertical) value.                               *
  109.  *                                                                         *
  110.  ***************************************************************************/
  111.  
  112. #define HorzEquiv(lengthV) ((lengthV * AspectH) / AspectV)
  113.  
  114.  
  115. /***************************************************************************
  116.  *                                                                         *
  117.  *  FUNCTION : About (HWND, unsigned, WORD, LONG)                          *
  118.  *                                                                         *
  119.  *  PURPOSE  : Dialog function for the "About..." menu item dialog.        *
  120.  *                                                                         *
  121.  ***************************************************************************/
  122.  
  123. BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
  124.     HWND     hDlg;
  125.     unsigned message;
  126.     WORD     wParam;
  127.     LONG     lParam;
  128. {
  129.     switch (message)
  130.     {
  131.         case WM_COMMAND:
  132.             EndDialog(hDlg, TRUE);
  133.             /* fall through */
  134.  
  135.         case WM_INITDIALOG:
  136.             return(TRUE);
  137.  
  138.         default:
  139.             return(FALSE);
  140.     }
  141. }
  142.  
  143.  
  144. /***************************************************************************
  145.  *                                                                         *
  146.  *  FUNCTION : ClockWndProc (HWND, UINT, WPARAM, LPARAM)           *
  147.  *                                                                         *
  148.  *  PURPOSE  : Window function for the application.                        *
  149.  *                                                                         *
  150.  ***************************************************************************/
  151.  
  152. long FAR PASCAL __export ClockWndProc(hWnd, message, wParam, lParam)
  153.     HWND     hWnd;
  154.     UINT     message;
  155.     WPARAM   wParam;
  156.     LPARAM     lParam;
  157. {
  158.     switch (message)
  159.     {
  160.         case WM_SYSCOMMAND:
  161.             if (wParam == IDM_ABOUT)
  162.             {
  163.                 /* Draw and handle messages for the "About..." Dialog */
  164.                 DialogBox(hInst,
  165.                       MAKEINTRESOURCE(1),
  166.                       hWnd,
  167.                       MakeProcInstance((FARPROC) About, hInst));
  168.             }
  169.             else
  170.             {
  171.                 /* Perform the default window processing */
  172.                 return(DefWindowProc(hWnd, message, wParam, lParam));
  173.             }
  174.             break;
  175.  
  176.         case WM_SIZE:
  177.             /* Resize clock based on window size and redraw */
  178.             ClockSize(hWnd, LOWORD(lParam), HIWORD(lParam), wParam);
  179.             UpdateWindow(hWnd);
  180.             break;
  181.  
  182.         case WM_DESTROY:
  183.             /* Destroy clock's timer and tools before exiting */
  184.             KillTimer(hWnd, TimerID);
  185.             DeleteTools();
  186.             PostQuitMessage(0);
  187.             break;
  188.  
  189.         case WM_PAINT:
  190.             {
  191.                 PAINTSTRUCT ps;
  192.  
  193.                 /* Paint clock displaying current time */
  194.                 InvalidateRect(hWnd, (LPRECT) NULL, TRUE);
  195.                 BeginPaint(hWnd, (LPPAINTSTRUCT) &ps);
  196.                 ClockPaint(hWnd, ps.hdc, PAINTALL);
  197.                 EndPaint(hWnd, (LPPAINTSTRUCT) &ps);
  198.             }
  199.             break;
  200.  
  201.         case WM_TIMECHANGE:
  202.         case WM_TIMER:
  203.             /* Update clock to display new time */
  204.             ClockTimer(hWnd);
  205.             break;
  206.  
  207.         case WM_SYSCOLORCHANGE:
  208.             /* Change tools to coincide with system window colors */
  209.             DeleteTools();
  210.             CreateTools();
  211.             break;
  212.  
  213.         case WM_ERASEBKGND:
  214.             {
  215.                 RECT rc;
  216.  
  217.                 /* Paint over the entire client area */
  218.                 GetClientRect(hWnd, (LPRECT) &rc);
  219.                 FillRect((HDC) wParam, (LPRECT) &rc, hbrBackgnd);
  220.             }
  221.             break;
  222.  
  223.         default:
  224.             /* Perform the default window processing */
  225.             return(DefWindowProc(hWnd, message, wParam, lParam));
  226.     }
  227.     return(0L);
  228. }
  229.  
  230.  
  231. /***************************************************************************
  232.  *                                                                         *
  233.  *  FUNCTION : CreateTools ()                                              *
  234.  *                                                                         *
  235.  *  PURPOSE  : Creates brushes and pens to coincide with the current       *
  236.  *             system colors.                                              *
  237.  *                                                                         *
  238.  ***************************************************************************/
  239.  
  240. VOID CreateTools()
  241. {
  242.     hbrForegnd  = CreateSolidBrush(GetSysColor(COLOR_WINDOWTEXT));
  243.     hbrBackgnd  = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
  244.     hpenForegnd = CreatePen(0, 1, GetSysColor(COLOR_WINDOWTEXT));
  245.     hpenBackgnd = CreatePen(0, 1, GetSysColor(COLOR_WINDOW));
  246. }
  247.  
  248.  
  249. /***************************************************************************
  250.  *                                                                         *
  251.  *  FUNCTION : DeleteTools ()                                              *
  252.  *                                                                         *
  253.  *  PURPOSE  : Destroys the brushes and pens created by CreateTools.       *
  254.  *                                                                         *
  255.  ***************************************************************************/
  256.  
  257. VOID DeleteTools()
  258. {
  259.     DeleteObject(hbrForegnd);
  260.     DeleteObject(hbrBackgnd);
  261.     DeleteObject(hpenForegnd);
  262.     DeleteObject(hpenBackgnd);
  263. }
  264.  
  265.  
  266. /***************************************************************************
  267.  *                                                                         *
  268.  *  FUNCTION : ClockCreate ()                                              *
  269.  *                                                                         *
  270.  *  PURPOSE  : First, for drawing the clock, ClockCreate computes the      *
  271.  *             aspect ratio and creates the necessary pens and brushes.    *
  272.  *             Then, if this is the first instance of the app running,     *
  273.  *             ClockCreate scales the circle table values according to the *
  274.  *             aspect ratio. Finally, ClockCreate gets the initial time.   *
  275.  *                                                                         *
  276.  ***************************************************************************/
  277.  
  278. VOID ClockCreate()
  279. {
  280.     int  pos;      /* hand position index into the circle table */
  281.     int  vertSize; /* height of the display in millimeters      */
  282.     int  horzSize; /* width of the display in millimeters       */
  283.     HDC  hDC;
  284.  
  285.     /* Get display size in (pixels X raster lines) */
  286.     /* and in (millimeters X millimeters)          */
  287.     hDC = GetDC(NULL);
  288.     VertRes = GetDeviceCaps(hDC, VERTRES);
  289.     HorzRes = GetDeviceCaps(hDC, HORZRES);
  290.     vertSize= GetDeviceCaps(hDC, VERTSIZE);
  291.     horzSize= GetDeviceCaps(hDC, HORZSIZE);
  292.     ReleaseDC(NULL, hDC);
  293.  
  294.     /* Compute (raster lines / decimeter) and (pixels / decimeter) */
  295.     AspectV = ((long) VertRes * MMPERDM) / (long) vertSize;
  296.     AspectH = ((long) HorzRes * MMPERDM) / (long) horzSize;
  297.  
  298.     CreateTools();
  299.  
  300.     /* Scale cosines for aspect ratio if this is the first instance */
  301.     if (bFirst)
  302.     {
  303.         lpCirTab = (POINT far *) GlobalLock(hCirTab);
  304.         for (pos = 0; pos < HANDPOSITIONS; pos++)
  305.         {
  306.             lpCirTab[pos].y = (int)(VertEquiv(lpCirTab[pos].y));
  307.         }
  308.         GlobalUnlock(hCirTab);
  309.         }
  310.  
  311.     GetTime(&oTime);
  312. }
  313.  
  314.  
  315. /***************************************************************************
  316.  *                                                                         *
  317.  *  FUNCTION : ClockSize (HWND, int, int, WORD)                            *
  318.  *                                                                         *
  319.  *  PURPOSE  : Resize the clock to the largest possible circle that will   *
  320.  *             fit in the client area. If switching from not iconic to     *
  321.  *             iconic, alter the timer to update every minute.  And if     *
  322.  *             switching back to non iconic, restore the timer to update   *
  323.  *             every second.                                               *
  324.  *                                                                         *
  325.  ***************************************************************************/
  326. VOID ClockSize(
  327. HWND hWnd,
  328. int  newWidth,
  329. int  newHeight,
  330. WORD sizeType)
  331. {
  332.     /* Set ClockRect to bound the largest possible circle in the window */
  333.     SetRect((LPRECT) &(ClockRect), 0, 0, newWidth, newHeight);
  334.     CircleClock(newWidth, newHeight);
  335.  
  336.     if(sizeType == SIZEICONIC)
  337.     {
  338.         /* Update once every minute in the iconic state */
  339.         KillTimer(hWnd, TimerID);
  340.         SetTimer(hWnd, TimerID, (unsigned) ICON_TLEN, 0L);
  341.         bIconic = TRUE;
  342.     }
  343.     else if (bIconic)
  344.     {
  345.         /* Update every second in the opened state (ignore tiling) */
  346.         KillTimer(hWnd, TimerID);
  347.         SetTimer(hWnd, TimerID, OPEN_TLEN, 0L);
  348.         bIconic = FALSE;
  349.     }
  350. }
  351.  
  352.  
  353. /***************************************************************************
  354.  *                                                                         *
  355.  *  FUNCTION : ClockTimer (HWND)                                           *
  356.  *                                                                         *
  357.  *  PURPOSE  : Update the clock to reflect the most recent time.           *
  358.  *                                                                         *
  359.  ***************************************************************************/
  360.  
  361. VOID ClockTimer(
  362. HWND hWnd)
  363. {
  364.     TIME nTime;
  365.     HDC  hDC;
  366.  
  367.     GetTime(&nTime);
  368.  
  369.     /* It's possible to change any part of the system at any time through */
  370.     /* the Control Panel. Check for any change in second, minute, or hour */
  371.     if ((nTime.second != oTime.second) ||
  372.         (nTime.minute != oTime.minute) ||
  373.         (nTime.hour   != oTime.hour))
  374.     {
  375.         /* The time has changed -- update the clock */
  376.         hDC = GetDC(hWnd);
  377.         ClockPaint(hWnd, hDC, HANDPAINT);
  378.         ReleaseDC(hWnd, hDC);
  379.         DdePostAdvise(idInst, MyServiceInfo->topic[0].hszTopic,
  380.                 MyServiceInfo->topic[0].item[0].hszItem);
  381.     }
  382. }
  383.  
  384.  
  385. /***************************************************************************
  386.  *                                                                         *
  387.  *  FUNCTION : ClockPaint (HWND, HDC, int)                                 *
  388.  *                                                                         *
  389.  *  PURPOSE  : Paint the clock to display the most recent time.            *
  390.  *                                                                         *
  391.  ***************************************************************************/
  392.  
  393. VOID ClockPaint(
  394. HWND hWnd,
  395. HDC  hDC,
  396. int  paintType)
  397. {
  398.     TIME nTime;
  399.  
  400.     SetBkMode(hDC, TRANSPARENT);
  401.  
  402.     lpCirTab = (POINT far *) GlobalLock(hCirTab);
  403.  
  404.     if (paintType == PAINTALL)
  405.     {
  406.         /* Paint entire clock -- face and hands */
  407.         FillRect(hDC, (LPRECT) &ClockRect, hbrBackgnd);
  408.         DrawFace(hDC);
  409.         DrawFatHand(hDC, HourHandPos(oTime), hpenForegnd, HHAND);
  410.         DrawFatHand(hDC, oTime.minute, hpenForegnd, MHAND);
  411.         if (!bIconic)
  412.         {
  413.             /* Erase old second hand */
  414.             DrawHand(hDC, oTime.second, hpenBackgnd, SECONDTIP, R2_NOT);
  415.         }
  416.         }
  417.     else if (paintType == HANDPAINT)
  418.     {
  419.         GetTime(&nTime);
  420.  
  421.         if ((!bIconic) && (nTime.second != oTime.second))
  422.         {
  423.             /* Second has changed -- erase old second hand */
  424.             DrawHand(hDC, oTime.second, hpenBackgnd, SECONDTIP, R2_NOT);
  425.         }
  426.  
  427.         if ((nTime.minute != oTime.minute) || (nTime.hour != oTime.hour))
  428.         {
  429.             /* Hour and/or minute have changed -- update hands */
  430.             if (bIconic)
  431.             {
  432.                 /* Erase old minute and hour hands */
  433.                 DrawHand(hDC, oTime.minute,
  434.                          hpenBackgnd, MINUTETIP, R2_COPYPEN);
  435.                 DrawHand(hDC, HourHandPos(oTime),
  436.                          hpenBackgnd, HOURTIP, R2_COPYPEN);
  437.  
  438.                 /* Draw new minute and hour hands */
  439.                 DrawHand(hDC, nTime.minute,
  440.                          hpenForegnd, MINUTETIP, R2_COPYPEN);
  441.                 DrawHand(hDC, HourHandPos(nTime),
  442.                          hpenForegnd, HOURTIP, R2_COPYPEN);
  443.                     
  444.             }
  445.             else
  446.             {
  447.                 /* Erase old minute and hour fat hands */
  448.                 DrawFatHand(hDC, oTime.minute,
  449.                         hpenBackgnd, MHAND);
  450.                 DrawFatHand(hDC, HourHandPos(oTime),
  451.                         hpenBackgnd, HHAND);
  452.  
  453.                 /* Draw new minute and hour fat hands */
  454.                 DrawFatHand(hDC, nTime.minute,
  455.                         hpenForegnd, MHAND);
  456.                 DrawFatHand(hDC, HourHandPos(nTime),
  457.                             hpenForegnd, HHAND);
  458.             }
  459.         }
  460.  
  461.         if ((!bIconic) && (nTime.second != oTime.second))
  462.         {
  463.             /* second has changed -- draw new second hand */
  464.             DrawHand(hDC, nTime.second,
  465.                      hpenBackgnd, SECONDTIP, R2_NOT);
  466.         }
  467.  
  468.         /* Store most recent time */
  469.         oTime.minute = nTime.minute;
  470.         oTime.hour   = nTime.hour;
  471.         oTime.second = nTime.second;
  472.     }
  473.     GlobalUnlock(hCirTab);
  474. }
  475.  
  476.  
  477. /***************************************************************************
  478.  *                                                                         *
  479.  *  FUNCTION : DrawFace (HDC)                                              *
  480.  *                                                                         *
  481.  *  PURPOSE  : Draws the clock face.                                       *
  482.  *                                                                         *
  483.  ***************************************************************************/
  484.  
  485. VOID DrawFace(
  486. HDC hDC)
  487. {
  488.     int    pos;       /* hand position index into the circle table */
  489.     int    dotHeight; /* height of the hour-marking dot            */
  490.     int    dotWidth;  /* width of the hour-marking dot             */
  491.     POINT  dotCenter; /* center point of the hour-marking dot      */
  492.     RECT   rc;
  493.  
  494.     /* Compute hour-marking dot width, height, and center point */
  495.     dotWidth = (int)((MAXDOTWIDTH * (long) (ClockRect.right - ClockRect.left)) / HorzRes);
  496.     dotHeight = (int)(VertEquiv(dotWidth));
  497.  
  498.     if (dotHeight < MINDOTHEIGHT)
  499.     {
  500.         dotHeight = MINDOTHEIGHT;
  501.     }
  502.  
  503.     if (dotWidth < MINDOTWIDTH)
  504.     {
  505.         dotWidth = MINDOTWIDTH;
  506.     }
  507.  
  508.     dotCenter.x = dotWidth >> 1;
  509.     dotCenter.y = dotHeight >> 1;
  510.  
  511.     /* Compute the clock center and radius */
  512.     InflateRect((LPRECT) &ClockRect, -dotCenter.y, -dotCenter.x);
  513.     ClockRadius = (long) ((ClockRect.right - ClockRect.left) >> 1);
  514.     ClockCenter.x = (int)(ClockRect.left + ClockRadius);
  515.     ClockCenter.y = ClockRect.top + ((ClockRect.bottom - ClockRect.top) >> 1);
  516.     InflateRect((LPRECT) &ClockRect, dotCenter.y, dotCenter.x);
  517.  
  518.     /* Draw the large hour-marking dots and small minute-marking dots */
  519.     for(pos = 0; pos < HANDPOSITIONS; pos++)
  520.     {
  521.         rc.top = (int)((lpCirTab[pos].y * ClockRadius) / CIRTABSCALE + ClockCenter.y);
  522.         rc.left = (int)((lpCirTab[pos].x * ClockRadius) / CIRTABSCALE + ClockCenter.x);
  523.         if (pos % 5)
  524.         {
  525.             if ((dotWidth > MINDOTWIDTH) && (dotHeight > MINDOTHEIGHT))
  526.             {
  527.                 /* Draw small minute-marking dot */
  528.                 rc.right = rc.left + 1;
  529.                 rc.bottom = rc.top + 1;
  530.                 FillRect(hDC, (LPRECT) &rc, hbrForegnd);
  531.             }
  532.         }
  533.         else
  534.         {
  535.             /* Draw large hour-marking dot */
  536.             rc.right = rc.left + dotWidth;
  537.             rc.bottom = rc.top + dotHeight;
  538.             OffsetRect((LPRECT) &rc, -dotCenter.x, -dotCenter.y);
  539.             FillRect(hDC, (LPRECT) &rc, hbrForegnd);
  540.         }
  541.     }
  542. }
  543.  
  544.  
  545. /***************************************************************************
  546.  *                                                                         *
  547.  *  FUNCTION : DrawHand (HDC, int, HPEN, int, int)                         *
  548.  *                                                                         *
  549.  *  PURPOSE  : Draws a thin hand with the specified pen in the specified   *
  550.  *             hand position.                                              *
  551.  *                                                                         *
  552.  ***************************************************************************/
  553.  
  554. VOID DrawHand(
  555. HDC  hDC,
  556. int  pos,
  557. HPEN hPen,
  558. int  scale,
  559. int  patMode)
  560. {
  561.     long radius;
  562.  
  563.     /* scale length of hand */
  564.     radius = (ClockRadius * scale) / 100;
  565.  
  566.     /* set pattern mode for hand */
  567.     SetROP2(hDC, patMode);
  568.  
  569.     /* select pen for hand */
  570.     SelectObject(hDC, hPen);
  571.  
  572.     /* Draw thin hand */
  573.     MoveTo(hDC, ClockCenter.x, ClockCenter.y);
  574.     LineTo(hDC, ClockCenter.x + (int)((lpCirTab[pos].x * radius) / CIRTABSCALE),
  575.             ClockCenter.y + (int)((lpCirTab[pos].y * radius) / CIRTABSCALE));
  576. }
  577.  
  578.  
  579. /***************************************************************************
  580.  *                                                                         *
  581.  *  FUNCTION : DrawFatHand (HDC, int, HPEN, BOOL)                          *
  582.  *                                                                         *
  583.  *  PURPOSE  : Draws a fat hand with the specified pen in the specified    *
  584.  *             hand position.                                              *
  585.  *                                                                         *
  586.  ***************************************************************************/
  587.  
  588. VOID DrawFatHand(
  589. HDC  hDC,
  590. int  pos,
  591. HPEN hPen,
  592. BOOL hHand)
  593. {
  594.     POINT ptTip;  /* coordinates for the tip of the hand        */
  595.     POINT ptTail; /* coordinates for the tail of the hand       */
  596.     POINT ptSide; /* coordinates for the side of the hand       */
  597.     int   index;  /* position index into the circle table       */
  598.     long  scale;  /* ClockRadius percentage to scale drawing to */
  599.  
  600.     /* set pattern mode for hand */
  601.     SetROP2(hDC, 13);
  602.  
  603.     /* select pen for hand */
  604.     SelectObject(hDC, hPen);
  605.  
  606.     /* compute coordinates for the side of the hand */
  607.     scale = (ClockRadius * (hHand ? HOURSIDE : MINUTESIDE)) / 100;
  608.     index = (pos + SIDESHIFT) % HANDPOSITIONS;
  609.     ptSide.y = (int)((lpCirTab[index].y * scale) / CIRTABSCALE);
  610.     ptSide.x = (int)((lpCirTab[index].x * scale) / CIRTABSCALE);
  611.  
  612.     /* compute coordinates for the tip of the hand */
  613.     scale = (ClockRadius * (hHand ? HOURTIP : MINUTETIP)) / 100;
  614.     ptTip.y = (int)((lpCirTab[pos].y * scale) / CIRTABSCALE);
  615.     ptTip.x = (int)((lpCirTab[pos].x * scale) / CIRTABSCALE);
  616.  
  617.     /* compute coordinates for the tail of the hand */
  618.     scale = (ClockRadius * (hHand ? HOURTAIL : MINUTETAIL)) / 100;
  619.     index = (pos + TAILSHIFT) % HANDPOSITIONS;
  620.     ptTail.y = (int)((lpCirTab[index].y * scale) / CIRTABSCALE);
  621.     ptTail.x = (int)((lpCirTab[index].x * scale) / CIRTABSCALE);
  622.  
  623.     /* Draw tip of hand */
  624.     MoveTo(hDC, ClockCenter.x + ptSide.x, ClockCenter.y + ptSide.y);
  625.     LineTo(hDC, ClockCenter.x +  ptTip.x, ClockCenter.y +  ptTip.y);
  626.     MoveTo(hDC, ClockCenter.x - ptSide.x, ClockCenter.y - ptSide.y);
  627.     LineTo(hDC, ClockCenter.x +  ptTip.x, ClockCenter.y +  ptTip.y);
  628.  
  629.     /* Draw tail of hand */
  630.     MoveTo(hDC, ClockCenter.x + ptSide.x, ClockCenter.y + ptSide.y);
  631.     LineTo(hDC, ClockCenter.x + ptTail.x, ClockCenter.y + ptTail.y);
  632.     MoveTo(hDC, ClockCenter.x - ptSide.x, ClockCenter.y - ptSide.y);
  633.     LineTo(hDC, ClockCenter.x + ptTail.x, ClockCenter.y + ptTail.y);
  634. }
  635.  
  636.  
  637. /***************************************************************************
  638.  *                                                                         *
  639.  *  FUNCTION : CircleClock (int, int)                                      *
  640.  *                                                                         *
  641.  *  PURPOSE  : Resizes the clock rectangle to keep the face circular.      *
  642.  *                                                                         *
  643.  ***************************************************************************/
  644.  
  645. VOID CircleClock(
  646. int maxWidth,
  647. int maxHeight)
  648. {
  649.     int clockHeight; /* tallest height that will keep face circular */
  650.     int clockWidth;  /* widest width that will keep face circular   */
  651.  
  652.     if (maxWidth > (int)HorzEquiv(maxHeight))
  653.     {
  654.         /* too wide -- decrease width to keep face circular */
  655.         clockWidth = (int)HorzEquiv(maxHeight);
  656.         ClockRect.left += (maxWidth - clockWidth) >> 1;
  657.         ClockRect.right = ClockRect.left + clockWidth;
  658.     }
  659.     else
  660.     {
  661.         /* too tall -- decrease height to keep face circular */
  662.         clockHeight = (int)VertEquiv(maxWidth);
  663.         ClockRect.top += (maxHeight - clockHeight) >> 1;
  664.         ClockRect.bottom = ClockRect.top + clockHeight;
  665.     }
  666. }
  667.  
  668.  
  669. /***************************************************************************
  670.  *                                                                         *
  671.  *  FUNCTION : WinMain (HANDLE, HANDLE, LPSTR, int)                        *
  672.  *                                                                         *
  673.  *  PURPOSE  : Calls the initialization function, creates the main appli-  *
  674.  *             cation window, and enters the message loop.                 *
  675.  *                                                                         *
  676.  ***************************************************************************/
  677.  
  678. int PASCAL WinMain(
  679. HANDLE hInstance,
  680. HANDLE hPrev,
  681. LPSTR  lpszCmdLine,
  682. int    cmdShow)
  683. {
  684.     HWND  hWnd;
  685.     MSG   msg;
  686.     HMENU hMenu;
  687.     TIME  nTime;
  688.     int   sysWidth;  /* width of left and right frames                  */
  689.     int   sysHeight; /* height of caption bar and top and bottom frames */
  690.     int   width;     /* width of entire clock window                    */
  691.     int   height;    /* height of entire clock window                   */
  692.  
  693.     hInst = hInstance;
  694.  
  695.     LoadString(hInst, IDS_APPNAME, (LPSTR) szBuffer, BUFLEN);
  696.  
  697.     if (!hPrev)
  698.     {
  699.         /* First instance -- register window class */
  700.         if (!ClockInit())
  701.             return(FALSE);
  702.         }
  703.     else
  704.     {
  705.         /* Not first instance -- get circle table and reset bFirst flag */
  706.         GetInstanceData(hPrev, (PSTR) &hCirTab, sizeof(HANDLE));
  707.         bFirst = FALSE;
  708.         }
  709.  
  710.     ClockCreate();
  711.     /* compute window height and width */
  712.     sysWidth  = GetSystemMetrics(SM_CXFRAME) * 2;
  713.     sysHeight = GetSystemMetrics(SM_CYCAPTION) + (GetSystemMetrics(SM_CYFRAME) * 2);
  714.     width = (HorzRes / 3) + sysWidth;
  715.     height = (int)(VertEquiv(width) + sysHeight);
  716.  
  717.     hWnd = CreateWindow( (LPSTR) szBuffer, /* class name              */
  718.                              (LPSTR) szBuffer, /* The window name         */
  719.                              WS_TILEDWINDOW,   /* window style            */
  720.                              CW_USEDEFAULT,    /* use default positioning */
  721.                              0,                /* y not used              */
  722.                              width,            /* window width            */
  723.                  height,           /* window height           */
  724.                              NULL,             /* NULL parent handle      */
  725.                              NULL,             /* NULL menu/child handle  */
  726.                              hInst,            /* program instance        */
  727.                              NULL              /* NULL data structure ref.*/
  728.                );
  729.  
  730.     GetTime(&nTime);
  731.     GetTime(&oTime);
  732.     while ((nTime.second == oTime.second) &&
  733.                (nTime.minute == oTime.minute) &&
  734.                (nTime.hour   == oTime.hour)     )
  735.     {
  736.         GetTime(&oTime);
  737.     }
  738.  
  739.     if (!SetTimer(hWnd, TimerID, OPEN_TLEN, 0L))
  740.     {
  741.         LPSTR szTooMany;
  742.  
  743.         /* 16 public timers already in use -- post error and exit */
  744.         szTooMany = (LPSTR)(unsigned long) LocalAlloc(LPTR, 40);
  745.         LoadString(hInst, IDS_TOOMANY, szTooMany, 40);
  746.         MessageBox((HWND) NULL, szTooMany, (LPSTR) szBuffer,
  747.                MB_OK | MB_ICONHAND | MB_SYSTEMMODAL);
  748.         DeleteTools();
  749.         return(FALSE);
  750.     }
  751.  
  752.     /* Add the "About..." menu item to the bottom of the system menu */
  753.     LoadString(hInst, IDS_ABOUTMENU, (LPSTR) szBuffer, BUFLEN);
  754.     hMenu = GetSystemMenu(hWnd, FALSE);
  755.     ChangeMenu(hMenu, 0, (LPSTR) szBuffer, IDM_ABOUT, MF_APPEND | MF_STRING);
  756.  
  757.     ShowWindow(hWnd, cmdShow);
  758.  
  759.     InitializeDDE(NULL, &idInst, MyServiceInfo,
  760.             CBF_FAIL_EXECUTES | CBF_SKIP_ALLNOTIFICATIONS, hInstance);
  761.  
  762.     /* Process messages until program termination */
  763.     while (GetMessage((LPMSG) &msg, NULL, 0, 0))
  764.     {
  765.         TranslateMessage((LPMSG) &msg);
  766.         DispatchMessage((LPMSG) &msg);
  767.     }
  768.  
  769.     UninitializeDDE();
  770.     
  771.     return(msg.wParam);
  772. }
  773.  
  774.  
  775. /***************************************************************************
  776.  *                                                                         *
  777.  *  FUNCTION : ClockInit ()                                                *
  778.  *                                                                         *
  779.  *  PURPOSE  : Registers the applicatoin window class and initializes the  *
  780.  *             circle values for the clock face.                           *
  781.  *                                                                         *
  782.  ***************************************************************************/
  783.  
  784. BOOL ClockInit()
  785. {
  786.     PWNDCLASS pClockClass;
  787.     HANDLE    hRes;
  788.     char      szData[5];
  789.  
  790.     pClockClass = (PWNDCLASS) LocalAlloc(LPTR, sizeof(WNDCLASS));
  791.  
  792.     pClockClass->lpszClassName = (LPSTR) szBuffer;
  793.     pClockClass->hbrBackground = (HBRUSH) NULL;
  794.     pClockClass->style         = CS_VREDRAW | CS_HREDRAW | CS_BYTEALIGNCLIENT;
  795.     pClockClass->hInstance     = hInst;
  796.     pClockClass->lpfnWndProc   = ClockWndProc;
  797.     pClockClass->hCursor       = LoadCursor(NULL, IDC_ARROW);
  798.     pClockClass->hIcon         = NULL;
  799.  
  800.     if (!RegisterClass((LPWNDCLASS) pClockClass))
  801.     {
  802.         /* Error registering class -- return */
  803.         return(FALSE);
  804.     }
  805.  
  806.     LocalFree((HANDLE) pClockClass);
  807.  
  808.     /* Load in pre-computed circle table cosine values from resource file */
  809.     LoadString(hInst, IDS_DATA, (LPSTR) szData, 5);
  810.     hRes = FindResource(hInst, (LPSTR) szBuffer, (LPSTR) szData);
  811.     if (!hRes)
  812.     {
  813.         /* Could not find circle table resource data -- return */
  814.         return(FALSE);
  815.     }
  816.  
  817.     hCirTab = LoadResource(hInst, hRes);
  818.     LockResource(hCirTab);
  819.  
  820.     return(TRUE);
  821. }
  822.  
  823.                  
  824. BOOL PASCAL PokeTime(
  825. HDDEDATA hData)
  826. {
  827.     TIME nTime;
  828.     char sz[40];
  829.     
  830.     DdeGetData(hData, (LPBYTE)sz, 40L, 0L);
  831.     sscanf(sz, "%d:%d:%d", &nTime.hour, &nTime.minute, &nTime.second);
  832.     SetTime(&nTime);
  833.     return(TRUE);
  834. }
  835.  
  836.  
  837.           
  838. HDDEDATA PASCAL RequestTime(
  839. HDDEDATA hDataOut)
  840. {
  841.     char sz[40];
  842.     
  843.     itoa(oTime.hour, sz, 10);
  844.     strcat(sz, ":");
  845.     itoa(oTime.minute, &sz[strlen(sz)], 10);
  846.     strcat(sz, ":");
  847.     itoa(oTime.second, &sz[strlen(sz)], 10);
  848.     return(DdeAddData(hDataOut, (LPBYTE)sz, strlen(sz) + 1, 0L));
  849. }
  850.  
  851. HDDEDATA PASCAL RequestHelp(
  852. HDDEDATA hDataOut)
  853. {
  854.     static char szHelp[] = "DDE Help for the Clock Service.\r\r\n\t"
  855.         "Poke to 'Time!Now' to reset the clock.\r\n\t"
  856.         "Request or advise on 'Time!Now' to get the time.";
  857.  
  858.     return(DdeAddData(hDataOut, szHelp, sizeof(szHelp), 0));
  859. }
  860.  
  861.