home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1993 #2 / Image.iso / os2 / memsz200.zip / MEMSIZE.CC < prev    next >
C/C++ Source or Header  |  1993-07-06  |  130KB  |  3,922 lines

  1. /***************************************************************** MEMSIZE.CC
  2.  *                                        *
  3.  * System Resources Monitor                            *
  4.  *                                        *
  5.  * (C) Copyright 1991-1993 by Richard W. Papo.                    *
  6.  *                                        *
  7.  * This is 'FreeWare'.    As such, it may be copied and distributed        *
  8.  * freely.  If you want to use part of it in your own program, please        *
  9.  * give credit where credit is due.  If you want to change the            *
  10.  * program, please refer the change request to me or send me the        *
  11.  * modified source code.  I can be reached at CompuServe 72607,3111.        *
  12.  *                                        *
  13.  ****************************************************************************/
  14.  
  15. #define INCL_BASE
  16. #define INCL_PM
  17. #include <os2.h>
  18.  
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22.  
  23. #include "debug.h"
  24. #include "support.h"
  25. #include "about.h"
  26. #include "profile.h"
  27. #include "settimer.h"
  28.  
  29. #include "memsize.h"
  30.  
  31. #define STATIC extern 
  32.  
  33. /****************************************************************************
  34.  *                                        *
  35.  *             Definitions & Declarations                *
  36.  *                                        *
  37.  ****************************************************************************/
  38.  
  39.   // Constants
  40.  
  41. #define PROGRAM_NAME       "MEMSIZE"
  42. #define CLASS_NAME        PROGRAM_NAME
  43.  
  44. #define DATEFMT_MM_DD_YY    (0x0000)
  45. #define DATEFMT_DD_MM_YY    (0x0001)
  46. #define DATEFMT_YY_MM_DD    (0x0002)
  47.  
  48. #define WM_REFRESH        (WM_USER)
  49.  
  50. #define MAX_DRIVES      (26)
  51. #define DRIVE_ERROR      (0xFFFFFFFFL)
  52.  
  53. enum
  54. {
  55.   ITEM_CLOCK,
  56.   ITEM_ELAPSEDTIME,
  57.   ITEM_MEMORYFREE,
  58.   ITEM_SWAPFILESIZE,
  59.   ITEM_SWAPDISKFREE,
  60.   ITEM_SPOOLFILESIZE,
  61.   ITEM_CPULOAD,
  62.   ITEM_TASKCOUNT,
  63.   ITEM_BASE_COUNT
  64. } ;
  65.  
  66.  
  67.   // Macros
  68.  
  69. #define max(a,b)    (((a) > (b)) ? (a) : (b))
  70. #define min(a,b)    (((a) < (b)) ? (a) : (b))
  71.  
  72.  
  73.   // Data Types
  74.  
  75. typedef struct          // Data structure for item to be monitored.
  76. {
  77.   BYTE     Name [80] ;           // Text for items profile name.
  78.   BOOL     Flag ;            // Flag: Show this item at this time?
  79.   BYTE     Label [80] ;           // Text to display on left part of line.
  80.   ULONG  Value ;           // Value to display on right part of line.
  81.   BOOL     Error ;           // Flag: Drive has had an error.
  82.   BYTE     MenuOption [80] ;     // Text to display in system menu.
  83.   USHORT MenuId ;           // ID for use in system menu.
  84.   ULONG  (*NewValue)           // Function to determine new value.
  85.     (PVOID,USHORT) ;
  86.   USHORT Parm ;            // Parameter to pass to NewValue function.
  87.   USHORT Divisor ;           // Amount to divide value by before display.
  88.   BYTE     Suffix ;           // Character to place after value.
  89. }
  90. ITEM, *PITEM ;
  91.  
  92. typedef struct          // Parameters saved to system.
  93. {
  94.   PITEM  Items ;        // Items to display.
  95.   int     ItemCount ;
  96.  
  97.   SWP     Position ;        // Window size & location.
  98.   BOOL     fPosition ;
  99.  
  100.   BOOL     HideControls ;     // User options.
  101.   BOOL     fHideControls ;
  102.  
  103.   USHORT TimerInterval ;
  104.   BOOL     fTimerInterval ;
  105.  
  106.   BYTE     FontNameSize [80] ;    // Presentation Parameters
  107.   BOOL     fFontNameSize ;
  108.  
  109.   COLOR  BackColor ;
  110.   BOOL     fBackColor ;
  111.  
  112.   COLOR  TextColor ;
  113.   BOOL     fTextColor ;
  114. }
  115. PROFILE, *PPROFILE ;
  116.  
  117. typedef struct        // Data structure for window.
  118. {
  119.   HAB         Anchor ;
  120.   HMODULE     Library ;
  121.   HINI           ProfileHandle ;
  122.  
  123.   ULONG      MaxCount ;
  124.   ULONG      IdleCounter ;
  125.   ULONG      IdleCount ;
  126.   TID         IdleLoopTID ;
  127.   TID         MonitorLoopTID ;
  128.  
  129.   PROFILE     Profile ;
  130.  
  131.   HWND           hwndTitleBar ;
  132.   HWND           hwndSysMenu ;
  133.   HWND         hwndMinMax ;
  134.  
  135.   ULONG      Drives ;
  136.  
  137.   BYTE           SwapPath [_MAX_PATH] ;
  138.   ULONG          MinFree ;
  139.  
  140.   PCHAR      SpoolPath ;
  141.  
  142.   long         Width ;
  143.   long         Height ;
  144.  
  145.   COUNTRYINFO     CountryInfo ;
  146.  
  147.   BYTE         Day [40] ;
  148.   BYTE         Days [40] ;
  149.   BYTE         DriveError [40] ;
  150. }
  151. DATA, *PDATA ;
  152.  
  153. typedef struct
  154. {
  155.   HAB Anchor ;
  156.   HMODULE Library ;
  157.   HINI ProfileHandle ;
  158. }
  159. PARMS, *PPARMS ;
  160.  
  161. typedef struct
  162. {
  163.   volatile PULONG Counter ;
  164.   PUSHORT Interval ;
  165.   HWND Owner ;
  166. }
  167. MONITOR_PARMS, *PMONITOR_PARMS ;
  168.  
  169.  
  170.   // Function Prototypes
  171.  
  172. extern INT main ( INT argc, PCHAR argv[] ) ;
  173.  
  174. STATIC MRESULT EXPENTRY MessageProcessor
  175. (
  176.   HWND hwnd,
  177.   USHORT msg,
  178.   MPARAM mp1,
  179.   MPARAM mp2
  180. ) ;
  181.  
  182. STATIC METHODFUNCTION Create ;
  183. STATIC METHODFUNCTION Destroy ;
  184. STATIC METHODFUNCTION Size ;
  185. STATIC METHODFUNCTION SaveApplication ;
  186. STATIC METHODFUNCTION Paint ;
  187. STATIC METHODFUNCTION Command ;
  188. STATIC METHODFUNCTION ResetDefaults ;
  189. STATIC METHODFUNCTION HideControlsCmd ;
  190. STATIC METHODFUNCTION SetTimer ;
  191. STATIC METHODFUNCTION About ;
  192. STATIC METHODFUNCTION ButtonDown ;
  193. STATIC METHODFUNCTION ButtonDblClick ;
  194. STATIC METHODFUNCTION PresParamChanged ;
  195. STATIC METHODFUNCTION SysColorChange ;
  196. STATIC METHODFUNCTION QueryKeysHelp ;
  197. STATIC METHODFUNCTION HelpError ;
  198. STATIC METHODFUNCTION ExtHelpUndefined ;
  199. STATIC METHODFUNCTION HelpSubitemNotFound ;
  200. STATIC METHODFUNCTION Refresh ;
  201.  
  202. STATIC int GetProfile ( HAB Anchor, HMODULE Library, HINI ProfileHandle, PPROFILE Profile ) ;
  203. STATIC VOID PutProfile ( HINI ProfileHandle, PPROFILE Profile ) ;
  204.  
  205. STATIC PSZ ScanSystemConfig ( HAB Anchor, PSZ Keyword ) ;
  206.  
  207. STATIC void ResizeWindow ( HWND hwnd, PPROFILE Profile ) ;
  208.  
  209. STATIC void HideControls
  210. (
  211.   BOOL fHide,
  212.   HWND hwndFrame,
  213.   HWND hwndSysMenu,
  214.   HWND hwndTitleBar,
  215.   HWND hwndMinMax
  216. ) ;
  217.  
  218. STATIC void UpdateWindow ( HWND hwnd, PDATA Data, BOOL All ) ;
  219.  
  220. STATIC ULONG ComputeTime       ( PVOID Data, USHORT Dummy ) ;
  221. STATIC ULONG ComputeElapsed    ( PVOID Data, USHORT Dummy ) ;
  222. STATIC ULONG ComputeFreeMemory ( PVOID Data, USHORT Dummy ) ;
  223. STATIC ULONG ComputeSwapSize   ( PVOID Data, USHORT Dummy ) ;
  224. STATIC ULONG ComputeSwapFree   ( PVOID Data, USHORT Dummy ) ;
  225. STATIC ULONG ComputeSpoolSize  ( PVOID Data, USHORT Dummy ) ;
  226. STATIC ULONG ComputeCpuLoad    ( PVOID Data, USHORT Dummy ) ;
  227. STATIC ULONG ComputeTaskCount  ( PVOID Data, USHORT Dummy ) ;
  228. STATIC ULONG ComputeDriveFree  ( PVOID Data, USHORT Drive ) ;
  229.  
  230. STATIC VOID MonitorLoopThread ( PMONITOR_PARMS Parms ) ;
  231.  
  232. STATIC VOID UpdateDriveList
  233. (
  234.   HAB Anchor,
  235.   HMODULE Library,
  236.   HINI ProfileHandle,
  237.   PPROFILE Profile,
  238.   ULONG OldDrives,
  239.   ULONG NewDrives
  240. ) ;
  241.  
  242. STATIC BOOL CheckDrive ( USHORT Drive, PBYTE FileSystem ) ;
  243.  
  244. STATIC VOID RebuildDisplayItems ( HWND hwnd, PDATA Data ) ;
  245.  
  246. STATIC ULONG CalibrateLoadMeter ( VOID ) ;
  247.  
  248. STATIC VOID CounterThread ( PULONG Counter ) ;
  249.  
  250. STATIC HINI OpenProfile ( PSZ Name, HAB Anchor, HMODULE Library, HWND HelpInstance ) ;
  251.  
  252.  
  253.   // Global Data (private)
  254.  
  255. static HMTX ItemSemaphore ;
  256.  
  257. static ITEM Items [ ITEM_BASE_COUNT + MAX_DRIVES ] =
  258. {
  259.   {
  260.     "ShowTime",      TRUE, "", 0, FALSE, "", IDM_SHOW_CLOCK,
  261.     ComputeTime,       0,    0, ' '
  262.   },
  263.  
  264.   {
  265.     "ShowElapsed",   TRUE, "", 0, FALSE, "", IDM_SHOW_ELAPSED,
  266.     ComputeElapsed,    0,    0, ' '
  267.   },
  268.  
  269.   {
  270.     "ShowMemory",    TRUE, "", 0, FALSE, "", IDM_SHOW_MEMORY,
  271.     ComputeFreeMemory, 0, 1024, 'K'
  272.   },
  273.  
  274.   {
  275.     "ShowSwapsize",  TRUE, "", 0, FALSE, "", IDM_SHOW_SWAPSIZE,
  276.     ComputeSwapSize,   0, 1024, 'K'
  277.   },
  278.  
  279.   {
  280.     "ShowSwapfree",  TRUE, "", 0, FALSE, "", IDM_SHOW_SWAPFREE,
  281.     ComputeSwapFree,   0, 1024, 'K'
  282.   },
  283.  
  284.   {
  285.     "ShowSpoolSize", TRUE, "", 0, FALSE, "", IDM_SHOW_SPOOLSIZE,
  286.     ComputeSpoolSize,  0, 1024, 'K'
  287.   },
  288.  
  289.   {
  290.     "ShowCpuLoad",   TRUE, "", 0, FALSE, "", IDM_SHOW_CPULOAD,
  291.     ComputeCpuLoad,    0,    0, '%'
  292.   },
  293.  
  294.   {
  295.     "ShowTaskCount", TRUE, "", 0, FALSE, "", IDM_SHOW_TASKCOUNT,
  296.     ComputeTaskCount,  0,    0, ' '
  297.   }
  298. } ;
  299.  
  300.  
  301. /****************************************************************************
  302.  *                                        *
  303.  *    Program Mainline                            *
  304.  *                                        *
  305.  ****************************************************************************/
  306.  
  307. extern INT main ( INT argc, PCHAR argv[] )
  308. {
  309.  /***************************************************************************
  310.   * Initialize for PM.    Abort if unable to do so.                *
  311.   ***************************************************************************/
  312.  
  313.   HAB Anchor = WinInitialize ( 0 ) ;
  314.   if ( Anchor == NULL )
  315.   {
  316.     return ( 1 ) ;
  317.   }
  318.  
  319.  /***************************************************************************
  320.   * Create the application message queue.  Abort if unable to do so.        *
  321.   ***************************************************************************/
  322.  
  323.   HMQ MessageQueue = WinCreateMsgQueue ( Anchor, 0 ) ;
  324.   if ( MessageQueue == NULL )
  325.   {
  326.     WinTerminate ( Anchor ) ;
  327.     return ( 1 ) ;
  328.   }
  329.  
  330.  /***************************************************************************
  331.   * Now WIN and GPI calls will work.  Open the language DLL.            *
  332.   ***************************************************************************/
  333.  
  334.   HMODULE Library ;
  335.   if ( DosLoadModule ( NULL, 0, (PSZ)PROGRAM_NAME, &Library ) )
  336.   {
  337.     Debug ( HWND_DESKTOP, "ERROR: Unable to load " PROGRAM_NAME ".DLL." ) ;
  338.     WinDestroyMsgQueue ( MessageQueue ) ;
  339.     WinTerminate ( Anchor ) ;
  340.     return ( 1 ) ;
  341.   }
  342.  
  343.  /***************************************************************************
  344.   * Get the program title.                                        *
  345.   ***************************************************************************/
  346.  
  347.   BYTE Title [80] ;
  348.   WinLoadString ( Anchor, Library, IDS_TITLE, sizeof(Title), Title ) ;
  349.  
  350.  /***************************************************************************
  351.   * Decipher command-line parameters.                        *
  352.   ***************************************************************************/
  353.  
  354.   BOOL Reset = FALSE ;
  355.   BYTE ResetCommand [40] ;
  356.  
  357.   WinLoadString ( Anchor, Library, IDS_PARMS_RESET, sizeof(ResetCommand), ResetCommand ) ;
  358.   WinUpper ( Anchor, NULL, NULL, ResetCommand ) ;
  359.  
  360.   while ( --argc )
  361.   {
  362.     argv ++ ;
  363.  
  364.     WinUpper ( Anchor, NULL, NULL, (PSZ)*argv ) ;
  365.  
  366.     if ( *argv[0] == '?' )
  367.     {
  368.       BYTE Message [200] ;
  369.  
  370.       WinLoadString ( Anchor, Library, IDS_PARAMETERLIST,
  371.     sizeof(Message), Message ) ;
  372.       WinMessageBox ( HWND_DESKTOP, HWND_DESKTOP, Message,
  373.     Title, 0, MB_ENTER | MB_NOICON ) ;
  374.  
  375.       WinDestroyMsgQueue ( MessageQueue ) ;
  376.       WinTerminate ( Anchor ) ;
  377.       return ( 1 ) ;
  378.     }
  379.  
  380.     if ( !strcmp ( *argv, (PCHAR)ResetCommand ) )
  381.     {
  382.       Reset = TRUE ;
  383.       continue ;
  384.     }
  385.  
  386.     {
  387.       BYTE Format [200] ;
  388.       BYTE Message [200] ;
  389.  
  390.       WinLoadString ( Anchor, Library, IDS_ERROR_INVALIDPARM,
  391.     sizeof(Format), Format ) ;
  392.       sprintf ( (PCHAR)Message, (PCHAR)Format, *argv ) ;
  393.       WinMessageBox ( HWND_DESKTOP, HWND_DESKTOP, Message,
  394.     Title, 0, MB_ENTER | MB_ICONEXCLAMATION ) ;
  395.  
  396.       WinDestroyMsgQueue ( MessageQueue ) ;
  397.       WinTerminate ( Anchor ) ;
  398.       return ( 1 ) ;
  399.     }
  400.   }
  401.  
  402.  /***************************************************************************
  403.   * Create the help instance.                            *
  404.   ***************************************************************************/
  405.  
  406.   HELPINIT HelpInit =
  407.   {
  408.     sizeof ( HELPINIT ),
  409.     0L,
  410.     NULL,
  411.     MAKEP ( 0xFFFF, ID_MAIN ),
  412.     0,
  413.     0,
  414.     0,
  415.     0,
  416.     NULL,
  417.     CMIC_HIDE_PANEL_ID,
  418.     (PSZ) ( PROGRAM_NAME ".HLP" )
  419.   } ;
  420.  
  421.   BYTE HelpTitle [80] ;
  422.   WinLoadString ( Anchor, Library, IDS_HELPTITLE, sizeof(HelpTitle), HelpTitle ) ;
  423.   HelpInit.pszHelpWindowTitle = HelpTitle ;
  424.  
  425.   HWND hwndHelp = WinCreateHelpInstance ( Anchor, &HelpInit ) ;
  426.  
  427.   if ( hwndHelp == NULL )
  428.   {
  429.     BYTE Message [200] ;
  430.  
  431.     WinLoadString ( Anchor, Library, IDS_ERROR_WINCREATEHELPINSTANCE,
  432.       sizeof(Message), Message ) ;
  433.     WinMessageBox ( HWND_DESKTOP, HWND_DESKTOP, Message,
  434.       Title, 0, MB_ENTER | MB_ICONEXCLAMATION ) ;
  435.   }
  436.  
  437.  /***************************************************************************
  438.   * Open/create the profile file.                                       *
  439.   ***************************************************************************/
  440.  
  441.   HINI ProfileHandle = OpenProfile ( PSZ(PROGRAM_NAME), Anchor, Library, hwndHelp ) ;
  442.  
  443.   if ( ProfileHandle == NULL )
  444.   {
  445.     BYTE Message [200] ;
  446.  
  447.     WinLoadString ( Anchor, Library, IDS_ERROR_PRFOPENPROFILE,
  448.       sizeof(Message), Message ) ;
  449.     Log ( "%s\r\n", Message ) ;
  450.     WinMessageBox ( HWND_DESKTOP, HWND_DESKTOP, Message,
  451.       Title, 0, MB_ENTER | MB_ICONEXCLAMATION ) ;
  452.     DosFreeModule ( Library ) ;
  453.     WinDestroyMsgQueue ( MessageQueue ) ;
  454.     WinTerminate ( Anchor ) ;
  455.     return ( 1 ) ;
  456.   }
  457.  
  458.  /***************************************************************************
  459.   * If we're going to reset the program's profile, do it now.               *
  460.   ***************************************************************************/
  461.  
  462.   if ( Reset )
  463.   {
  464.     PrfWriteProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, NULL, NULL, 0 ) ;
  465.   }
  466.  
  467.  /***************************************************************************
  468.   * Create the frame window.                            *
  469.   ***************************************************************************/
  470.  
  471.   #pragma pack(2)
  472.   struct
  473.   {
  474.     USHORT Filler ;
  475.     USHORT cb ;
  476.     ULONG  flCreateFlags ;
  477.     USHORT hmodResources ;
  478.     USHORT idResources ;
  479.   }
  480.   fcdata ;
  481.   #pragma pack()
  482.  
  483.   fcdata.cb = sizeof(fcdata) - sizeof(fcdata.Filler) ;
  484.   fcdata.flCreateFlags =
  485.     FCF_TITLEBAR | FCF_SYSMENU | FCF_BORDER |
  486.     FCF_ICON | FCF_MINBUTTON | FCF_NOBYTEALIGN | FCF_ACCELTABLE ;
  487.   fcdata.hmodResources = 0 ;
  488.   fcdata.idResources = ID_MAIN ;
  489.  
  490.   HWND hwndFrame = WinCreateWindow
  491.   (
  492.     HWND_DESKTOP,
  493.     WC_FRAME,
  494.     Title,
  495.     0,
  496.     0, 0, 0, 0,
  497.     HWND_DESKTOP,
  498.     HWND_TOP,
  499.     ID_MAIN,
  500.     &fcdata.cb,
  501.     NULL
  502.   ) ;
  503.  
  504.   if ( hwndFrame == NULL )
  505.   {
  506.     BYTE Message [200] ;
  507.  
  508.     WinLoadString ( Anchor, Library, IDS_ERROR_WINCREATEFRAME,
  509.       sizeof(Message), Message ) ;
  510.     WinMessageBox ( HWND_DESKTOP, HWND_DESKTOP, Message,
  511.       Title, 0, MB_ENTER | MB_ICONEXCLAMATION ) ;
  512.  
  513.     PrfCloseProfile ( ProfileHandle ) ;
  514.     DosFreeModule ( Library ) ;
  515.     WinDestroyMsgQueue ( MessageQueue ) ;
  516.     WinTerminate ( Anchor ) ;
  517.     return ( 1 ) ;
  518.   }
  519.  
  520.  /***************************************************************************
  521.   * Associate the help instance with the frame window.                *
  522.   ***************************************************************************/
  523.  
  524.   if ( hwndHelp )
  525.   {
  526.     WinAssociateHelpInstance ( hwndHelp, hwndFrame ) ;
  527.   }
  528.  
  529.  /***************************************************************************
  530.   * Register the window class.                            *
  531.   ***************************************************************************/
  532.  
  533.   if ( NOT WinRegisterClass ( Anchor, (PSZ)CLASS_NAME, MessageProcessor,
  534.     CS_MOVENOTIFY, sizeof(PVOID) ) )
  535.   {
  536.     BYTE Format [200] ;
  537.     BYTE Message [200] ;
  538.  
  539.     WinLoadString ( Anchor, Library, IDS_ERROR_WINREGISTERCLASS,
  540.       sizeof(Format), Format ) ;
  541.     sprintf ( PCHAR(Message), PCHAR(Format), CLASS_NAME ) ;
  542.     WinMessageBox ( HWND_DESKTOP, HWND_DESKTOP, Message,
  543.       Title, 0, MB_ENTER | MB_ICONEXCLAMATION ) ;
  544.  
  545.     PrfCloseProfile ( ProfileHandle ) ;
  546.     DosFreeModule ( Library ) ;
  547.     WinDestroyMsgQueue ( MessageQueue ) ;
  548.     WinTerminate ( Anchor ) ;
  549.     return ( 1 ) ;
  550.   }
  551.  
  552.  /***************************************************************************
  553.   * Create client window.  If this fails, destroy frame and return.        *
  554.   ***************************************************************************/
  555.  
  556.   PARMS Parms ;
  557.   Parms.Anchor = Anchor ;
  558.   Parms.Library = Library ;
  559.   Parms.ProfileHandle = ProfileHandle ;
  560.  
  561.   HWND hwndClient = WinCreateWindow
  562.   (
  563.     hwndFrame,
  564.     (PSZ)CLASS_NAME,
  565.     (PSZ)"",
  566.     0,
  567.     0, 0, 0, 0,
  568.     hwndFrame,
  569.     HWND_BOTTOM,
  570.     FID_CLIENT,
  571.     &Parms,
  572.     NULL
  573.   ) ;
  574.  
  575.   if ( hwndClient == NULL )
  576.   {
  577.     BYTE Message [200] ;
  578.  
  579.     WinLoadString ( Anchor, Library, IDS_ERROR_WINCREATEWINDOW,
  580.       sizeof(Message), Message ) ;
  581.     WinMessageBox ( HWND_DESKTOP, HWND_DESKTOP, Message,
  582.       Title, 0, MB_ENTER | MB_ICONEXCLAMATION ) ;
  583.  
  584.     WinDestroyWindow ( hwndFrame ) ;
  585.     if ( hwndHelp )
  586.     {
  587.       WinDestroyHelpInstance ( hwndHelp ) ;
  588.     }
  589.     PrfCloseProfile ( ProfileHandle ) ;
  590.     DosFreeModule ( Library ) ;
  591.     WinDestroyMsgQueue ( MessageQueue ) ;
  592.     WinTerminate ( Anchor ) ;
  593.     return ( 1 ) ;
  594.   }
  595.  
  596.  /***************************************************************************
  597.   * Wait for and process messages to the window's queue.  Terminate         *
  598.   *   when the WM_QUIT message is received.                    *
  599.   ***************************************************************************/
  600.  
  601.   QMSG QueueMessage ;
  602.   while ( WinGetMsg ( Anchor, &QueueMessage, NULL, 0, 0 ) )
  603.   {
  604.     WinDispatchMsg ( Anchor, &QueueMessage ) ;
  605.   }
  606.  
  607.  /***************************************************************************
  608.   * Discard all that was requested of the system and terminate.         *
  609.   ***************************************************************************/
  610.  
  611.   WinDestroyWindow ( hwndFrame ) ;
  612.  
  613.   if ( hwndHelp )
  614.   {
  615.     WinDestroyHelpInstance ( hwndHelp ) ;
  616.   }
  617.  
  618.   PrfCloseProfile ( ProfileHandle ) ;
  619.  
  620.   DosFreeModule ( Library ) ;
  621.  
  622.   WinDestroyMsgQueue ( MessageQueue ) ;
  623.  
  624.   WinTerminate ( Anchor ) ;
  625.  
  626.   return ( 0 ) ;
  627. }
  628.  
  629. /****************************************************************************
  630.  *                                        *
  631.  *    Window Message Processor                        *
  632.  *                                        *
  633.  ****************************************************************************/
  634.  
  635. STATIC MRESULT EXPENTRY MessageProcessor
  636. (
  637.   HWND hwnd,
  638.   USHORT msg,
  639.   MPARAM mp1,
  640.   MPARAM mp2
  641. )
  642. {
  643.  /***************************************************************************
  644.   * Dispatch the message according to the method table and return the        *
  645.   *   result.  Any messages not defined above get handled by the system     *
  646.   *   default window processor.                         *
  647.   ***************************************************************************/
  648.  
  649.   static METHOD Methods [] =
  650.   {
  651.     { WM_CREATE,        Create            },
  652.     { WM_DESTROY,        Destroy         },
  653.     { WM_SIZE,            Size            },
  654.     { WM_MOVE,            Size            },
  655.     { WM_SAVEAPPLICATION,    SaveApplication     },
  656.     { WM_PAINT,         Paint            },
  657.     { WM_BUTTON1DOWN,        ButtonDown        },
  658.     { WM_BUTTON2DOWN,        ButtonDown        },
  659.     { WM_BUTTON1DBLCLK,     ButtonDblClick        },
  660.     { WM_BUTTON2DBLCLK,     ButtonDblClick        },
  661.     { WM_PRESPARAMCHANGED,    PresParamChanged    },
  662.     { WM_SYSCOLORCHANGE,    SysColorChange        },
  663.     { WM_COMMAND,        Command         },
  664.     { HM_QUERY_KEYS_HELP,    QueryKeysHelp        },
  665.     { HM_ERROR,         HelpError        },
  666.     { HM_EXT_HELP_UNDEFINED,    ExtHelpUndefined    },
  667.     { HM_HELPSUBITEM_NOT_FOUND, HelpSubitemNotFound },
  668.     { WM_REFRESH,        Refresh         }
  669.   } ;
  670.  
  671.   return ( DispatchMessage ( hwnd, msg, mp1, mp2, Methods, sizeof(Methods)/sizeof(Methods[0]), WinDefWindowProc ) ) ;
  672. }
  673.  
  674. /****************************************************************************
  675.  *                                        *
  676.  *    Create the main window.                         *
  677.  *                                        *
  678.  ****************************************************************************/
  679.  
  680. STATIC MRESULT APIENTRY Create
  681. (
  682.   HWND hwnd,
  683.   USHORT msg,
  684.   MPARAM mp1,
  685.   MPARAM mp2
  686. )
  687. {
  688.  /***************************************************************************
  689.   * Allocate instance data.                            *
  690.   ***************************************************************************/
  691.  
  692.   PDATA Data = malloc ( sizeof(DATA) ) ;
  693.  
  694.   memset ( Data, 0, sizeof(DATA) ) ;
  695.  
  696.   WinSetWindowPtr ( hwnd, QWL_USER, Data ) ;
  697.  
  698.  /***************************************************************************
  699.   * Grab any parameters from the WM_CREATE message.                *
  700.   ***************************************************************************/
  701.  
  702.   PPARMS Parms = (PPARMS) PVOIDFROMMP ( mp1 ) ;
  703.  
  704.   Data->Anchor = Parms->Anchor ;
  705.   Data->Library = Parms->Library ;
  706.   Data->ProfileHandle = Parms->ProfileHandle ;
  707.  
  708.  /***************************************************************************
  709.   * Create the item list MUTEX semaphore.                                   *
  710.   ***************************************************************************/
  711.  
  712.   DosCreateMutexSem ( NULL, &ItemSemaphore, DC_SEM_SHARED, 0 ) ;
  713.  
  714.  /***************************************************************************
  715.   * Get the current drive mask.                         *
  716.   ***************************************************************************/
  717.  
  718.   ULONG Drive ;
  719.   DosQueryCurrentDisk ( &Drive, &Data->Drives ) ;
  720.  
  721.  /***************************************************************************
  722.   * Get profile data. Try the OS2.INI first, then try for private INI.      *
  723.   *   If obtained from OS2.INI, erase it afterwards.                        *
  724.   ***************************************************************************/
  725.  
  726.   if ( GetProfile ( Data->Anchor, Data->Library, HINI_USERPROFILE, &Data->Profile ) )
  727.   {
  728.     GetProfile ( Data->Anchor, Data->Library, Data->ProfileHandle, &Data->Profile ) ;
  729.   }
  730.   else
  731.   {
  732.     PrfWriteProfileData ( HINI_USERPROFILE, (PSZ)PROGRAM_NAME, NULL, NULL, 0 ) ;
  733.   }
  734.  
  735.  /***************************************************************************
  736.   * Get country information.                            *
  737.   ***************************************************************************/
  738.  
  739.   {
  740.     COUNTRYCODE CountryCode ;
  741.     ULONG Count ;
  742.     ULONG Status ;
  743.  
  744.     CountryCode.country = 0 ;
  745.     CountryCode.codepage = 0 ;
  746.  
  747.     Status = DosGetCtryInfo ( sizeof(Data->CountryInfo), &CountryCode,
  748.       &Data->CountryInfo, &Count ) ;
  749.     if ( Status )
  750.     {
  751.       BYTE Message [80] ;
  752.       WinLoadMessage ( Data->Anchor, Data->Library, IDS_ERROR_DOSGETCTRYINFO,
  753.     sizeof(Message), Message ) ;
  754.       Debug ( hwnd, (PCHAR)Message, Status ) ;
  755.       Data->CountryInfo.fsDateFmt = DATEFMT_MM_DD_YY ;
  756.       Data->CountryInfo.fsTimeFmt = 0 ;
  757.       Data->CountryInfo.szDateSeparator[0] = '/' ;
  758.       Data->CountryInfo.szDateSeparator[1] = 0 ;
  759.       Data->CountryInfo.szTimeSeparator[0] = ':' ;
  760.       Data->CountryInfo.szTimeSeparator[1] = 0 ;
  761.       Data->CountryInfo.szThousandsSeparator[0] = ',' ;
  762.       Data->CountryInfo.szThousandsSeparator[1] = 0 ;
  763.     }
  764.   }
  765.  
  766.  /***************************************************************************
  767.   * Get the texts that may be needed every second.                *
  768.   ***************************************************************************/
  769.  
  770.   WinLoadString ( Data->Anchor, Data->Library, IDS_DAY, sizeof(Data->Day), Data->Day ) ;
  771.   WinLoadString ( Data->Anchor, Data->Library, IDS_DAYS, sizeof(Data->Days), Data->Days ) ;
  772.   WinLoadString ( Data->Anchor, Data->Library, IDS_DRIVEERROR, sizeof(Data->DriveError), Data->DriveError ) ;
  773.  
  774.  /***************************************************************************
  775.   * Get the frame handle.                            *
  776.   ***************************************************************************/
  777.  
  778.   HWND hwndFrame = WinQueryWindow ( hwnd, QW_PARENT ) ;
  779.  
  780.  /***************************************************************************
  781.   * Get the control window handles.                        *
  782.   ***************************************************************************/
  783.  
  784.   Data->hwndSysMenu  = WinWindowFromID ( hwndFrame, FID_SYSMENU  ) ;
  785.   Data->hwndTitleBar = WinWindowFromID ( hwndFrame, FID_TITLEBAR ) ;
  786.   Data->hwndMinMax   = WinWindowFromID ( hwndFrame, FID_MINMAX   ) ;
  787.  
  788.  /***************************************************************************
  789.   * Create the submenu window for Display Items.                *
  790.   ***************************************************************************/
  791.  
  792.   static MENUITEM MenuItems [] =
  793.   {
  794.     { MIT_END, MIS_TEXT,      0, IDM_SAVE_APPLICATION, NULL, 0 },
  795.     { MIT_END, MIS_TEXT,      0, IDM_RESET_DEFAULTS,   NULL, 0 },
  796.     { MIT_END, MIS_TEXT,      0, IDM_HIDE_CONTROLS,    NULL, 0 },
  797.     { MIT_END, MIS_TEXT,      0, IDM_SET_TIMER,        NULL, 0 },
  798.     { MIT_END, MIS_SUBMENU,   0, IDM_DISPLAY_ITEMS,    NULL, 0 },
  799.   } ;
  800.  
  801.   SHORT idSysMenu = SHORT1FROMMR ( WinSendMsg ( Data->hwndSysMenu, MM_ITEMIDFROMPOSITION, NULL, NULL ) ) ;
  802.  
  803.   MENUITEM MenuItem ;
  804.   WinSendMsg ( Data->hwndSysMenu, MM_QUERYITEM, MPFROM2SHORT(idSysMenu,FALSE), MPFROMP(&MenuItem) ) ;
  805.  
  806.   HWND hwndSysSubMenu = MenuItem.hwndSubMenu ;
  807.  
  808.   HWND hwndSubMenu = WinCreateWindow ( hwndSysSubMenu, WC_MENU, (PSZ)"",
  809.     WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
  810.     0, 0, 0, 0, hwndSysSubMenu, HWND_TOP, IDM_DISPLAY_ITEMS, NULL, NULL ) ;
  811.  
  812.   MenuItems[IDM_DISPLAY_ITEMS-MenuItems[0].id].hwndSubMenu = hwndSubMenu ;
  813.  
  814.  /***************************************************************************
  815.   * Add basic extensions to the system menu.                    *
  816.   ***************************************************************************/
  817.  
  818.   static MENUITEM MenuSeparator =
  819.     { MIT_END, MIS_SEPARATOR, 0, 0, NULL, 0 } ;
  820.  
  821.   AddSysMenuItem ( hwndFrame, &MenuSeparator, NULL ) ;
  822.  
  823.   for ( int i=0; i<sizeof(MenuItems)/sizeof(MenuItems[0]); i++ )
  824.   {
  825.     BYTE MenuText [80] ;
  826.     WinLoadString ( Data->Anchor, Data->Library, i+IDS_SAVE_APPLICATION, sizeof(MenuText), MenuText ) ;
  827.     AddSysMenuItem ( hwndFrame, MenuItems+i, MenuText ) ;
  828.   }
  829.  
  830.  /***************************************************************************
  831.   * Add 'About' to the system menu.                        *
  832.   ***************************************************************************/
  833.  
  834.   static MENUITEM MenuAbout =
  835.     { MIT_END, MIS_TEXT, 0, IDM_ABOUT, NULL, 0 } ;
  836.  
  837.   AddSysMenuItem ( hwndFrame, &MenuSeparator, NULL ) ;
  838.  
  839.   BYTE AboutText [80] ;
  840.   WinLoadString ( Data->Anchor, Data->Library, IDS_ABOUT, sizeof(AboutText), AboutText ) ;
  841.  
  842.   AddSysMenuItem ( hwndFrame, &MenuAbout, AboutText ) ;
  843.  
  844.  /***************************************************************************
  845.   * Add 'Help' to the system menu.                        *
  846.   ***************************************************************************/
  847.  
  848.   static MENUITEM MenuHelp =
  849.     { MIT_END, MIS_HELP, 0, 0, NULL, 0 } ;
  850.  
  851.   BYTE HelpText [80] ;
  852.   WinLoadString ( Data->Anchor, Data->Library, IDS_HELP, sizeof(HelpText), HelpText ) ;
  853.  
  854.   AddSysMenuItem ( hwndFrame, &MenuHelp, HelpText ) ;
  855.  
  856.  /***************************************************************************
  857.   * Build the display items menu.                        *
  858.   ***************************************************************************/
  859.  
  860.   RebuildDisplayItems ( hwnd, Data ) ;
  861.  
  862.  /***************************************************************************
  863.   * Get the SWAPPATH statement from CONFIG.SYS.                 *
  864.   ***************************************************************************/
  865.  
  866.   PSZ Swappath = ScanSystemConfig ( Data->Anchor, (PSZ)"SWAPPATH" ) ;
  867.  
  868.   if ( Swappath == NULL )
  869.   {
  870.     Swappath = (PSZ) "C:\\OS2\\SYSTEM 0" ;
  871.   }
  872.  
  873.   sscanf ( (PCHAR)Swappath, "%s %li", Data->SwapPath, &Data->MinFree ) ;
  874.  
  875.  /***************************************************************************
  876.   * Find out where the spool work directory is.                 *
  877.   ***************************************************************************/
  878.  
  879.   Data->SpoolPath = NULL ;
  880.  
  881.   ULONG Size ;
  882.   if ( PrfQueryProfileSize ( HINI_PROFILE, (PSZ)"PM_SPOOLER", (PSZ)"DIR", &Size ) )
  883.   {
  884.     Data->SpoolPath = malloc ( (int)Size ) ;
  885.     if ( Data->SpoolPath )
  886.     {
  887.       if ( PrfQueryProfileData ( HINI_PROFILE, (PSZ)"PM_SPOOLER", (PSZ)"DIR", Data->SpoolPath, &Size ) )
  888.       {
  889.     PBYTE p = (PBYTE) strchr ( (PCHAR)Data->SpoolPath, ';' ) ;
  890.     if ( p )
  891.     {
  892.       *p = 0 ;
  893.     }
  894.       }
  895.       else
  896.       {
  897.     free ( Data->SpoolPath ) ;
  898.     Data->SpoolPath = NULL ;
  899.       }
  900.     }
  901.   }
  902.  
  903.  /***************************************************************************
  904.   * Calibrate the old-style load meter, if the high resolution timer's      *
  905.   *   available.                                *
  906.   ***************************************************************************/
  907.  
  908.   Data->MaxCount = CalibrateLoadMeter ( ) ;
  909.   Data->MaxCount = (ULONG) max ( 1L, Data->MaxCount ) ;
  910.  
  911.  /***************************************************************************
  912.   * Start the new load meter.                            *
  913.   ***************************************************************************/
  914.  
  915.   DosCreateThread ( &Data->IdleLoopTID, CounterThread, (ULONG)&Data->IdleCounter, 0, 4096 ) ;
  916.   DosSetPrty ( PRTYS_THREAD, PRTYC_IDLETIME, PRTYD_MINIMUM, Data->IdleLoopTID ) ;
  917.   DosSuspendThread ( Data->IdleLoopTID ) ;
  918.  
  919.   Data->IdleCount = 0 ;
  920.   Data->IdleCounter = 0 ;
  921.  
  922.   if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
  923.   {
  924.     Log ( "ERROR: Unable to lock item list to check CPU load item status.\r\n" ) ;
  925.     DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
  926.   }
  927.   if ( Data->Profile.Items[ITEM_CPULOAD].Flag )
  928.   {
  929.     DosResumeThread ( Data->IdleLoopTID ) ;
  930.   }
  931.   DosReleaseMutexSem ( ItemSemaphore ) ;
  932.  
  933.   PMONITOR_PARMS MonitorParms ;
  934.   MonitorParms = malloc ( sizeof(*MonitorParms) ) ;
  935.   MonitorParms->Counter = & Data->IdleCounter ;
  936.   MonitorParms->Interval = & Data->Profile.TimerInterval ;
  937.   MonitorParms->Owner = hwnd ;
  938.   DosCreateThread ( &Data->MonitorLoopTID, MonitorLoopThread, (ULONG)MonitorParms, 2, 8192 ) ;
  939.  
  940.  /***************************************************************************
  941.   * Add the program to the system task list.                    *
  942.   ***************************************************************************/
  943.  
  944.   BYTE Title [80] ;
  945.   WinLoadString ( Data->Anchor, Data->Library, IDS_TITLE, sizeof(Title), Title ) ;
  946.   Add2TaskList ( hwndFrame, Title ) ;
  947.  
  948.  /***************************************************************************
  949.   * Position & size the window.  For some reason, we must move and size     *
  950.   *   the window to the saved position before applying the resizing        *
  951.   *   function as fine-tuning.    Maybe the positioning request fails if        *
  952.   *   the window has no size?                            *
  953.   ***************************************************************************/
  954.  
  955.   WinSetWindowPos ( hwndFrame, HWND_BOTTOM,
  956.     Data->Profile.Position.x, Data->Profile.Position.y,
  957.     Data->Profile.Position.cx, Data->Profile.Position.cy,
  958.     SWP_SIZE | SWP_MOVE | SWP_ZORDER |
  959.     ( Data->Profile.Position.fl & SWP_MINIMIZE ) |
  960.     ( Data->Profile.Position.fl & SWP_RESTORE ) ) ;
  961.  
  962.   ResizeWindow ( hwnd, &Data->Profile ) ;
  963.  
  964.  /***************************************************************************
  965.   * Hide the controls if so configured.                     *
  966.   ***************************************************************************/
  967.  
  968.   if ( Data->Profile.HideControls
  969.     AND NOT ( Data->Profile.Position.fl & SWP_MINIMIZE ) )
  970.   {
  971.     CheckMenuItem ( hwndFrame, FID_SYSMENU, IDM_HIDE_CONTROLS, Data->Profile.HideControls ) ;
  972.  
  973.     HideControls
  974.     (
  975.       TRUE,
  976.       hwndFrame,
  977.       Data->hwndSysMenu,
  978.       Data->hwndTitleBar,
  979.       Data->hwndMinMax
  980.     ) ;
  981.   }
  982.  
  983.  /***************************************************************************
  984.   * Get the saved presentation parameters and reinstate them.            *
  985.   ***************************************************************************/
  986.  
  987.   if ( Data->Profile.fFontNameSize )
  988.   {
  989.     WinSetPresParam ( hwnd, PP_FONTNAMESIZE,
  990.       strlen((PCHAR)Data->Profile.FontNameSize)+1, Data->Profile.FontNameSize ) ;
  991.   }
  992.  
  993.   if ( Data->Profile.fBackColor )
  994.   {
  995.     WinSetPresParam ( hwnd, PP_BACKGROUNDCOLOR,
  996.       sizeof(Data->Profile.BackColor), &Data->Profile.BackColor ) ;
  997.   }
  998.  
  999.   if ( Data->Profile.fTextColor )
  1000.   {
  1001.     WinSetPresParam ( hwnd, PP_FOREGROUNDCOLOR,
  1002.       sizeof(Data->Profile.TextColor), &Data->Profile.TextColor ) ;
  1003.   }
  1004.  
  1005.  /***************************************************************************
  1006.   * Determine our font size.                            *
  1007.   ***************************************************************************/
  1008.  
  1009.   HPS hPS = WinGetPS ( hwnd ) ;
  1010.   RECTL Rectangle ;
  1011.   WinQueryWindowRect ( HWND_DESKTOP, &Rectangle ) ;
  1012.   WinDrawText ( hPS, 1, (PSZ)" ", &Rectangle, 0L, 0L, DT_LEFT | DT_BOTTOM | DT_QUERYEXTENT ) ;
  1013.   Data->Width  = Rectangle.xRight - Rectangle.xLeft ;
  1014.   Data->Height = Rectangle.yTop - Rectangle.yBottom ;
  1015.   WinReleasePS ( hPS ) ;
  1016.  
  1017.  /***************************************************************************
  1018.   * Now that the window's in order, make it visible.                        *
  1019.   ***************************************************************************/
  1020.  
  1021.   WinShowWindow ( hwndFrame, TRUE ) ;
  1022.  
  1023.  /***************************************************************************
  1024.   * Success?  Return no error.                            *
  1025.   ***************************************************************************/
  1026.  
  1027.   return ( 0 ) ;
  1028. }
  1029.  
  1030. /****************************************************************************
  1031.  *                                        *
  1032.  *    Destroy main window.                            *
  1033.  *                                        *
  1034.  ****************************************************************************/
  1035.  
  1036. STATIC MRESULT APIENTRY Destroy
  1037. (
  1038.   HWND hwnd,
  1039.   USHORT msg,
  1040.   MPARAM mp1,
  1041.   MPARAM mp2
  1042. )
  1043. {
  1044.  /***************************************************************************
  1045.   * Find the instance data.                            *
  1046.   ***************************************************************************/
  1047.  
  1048.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1049.  
  1050.  /***************************************************************************
  1051.   * Release the instance memory.                        *
  1052.   ***************************************************************************/
  1053.  
  1054.   free ( Data ) ;
  1055.  
  1056.  /***************************************************************************
  1057.   * We're done.                                                             *
  1058.   ***************************************************************************/
  1059.  
  1060.   return ( MRFROMSHORT ( 0 ) ) ;
  1061. }
  1062.  
  1063. /****************************************************************************
  1064.  *                                        *
  1065.  *    Process window resize message.                        *
  1066.  *                                        *
  1067.  ****************************************************************************/
  1068.  
  1069. STATIC MRESULT APIENTRY Size
  1070. (
  1071.   HWND hwnd,
  1072.   USHORT msg,
  1073.   MPARAM mp1,
  1074.   MPARAM mp2
  1075. )
  1076. {
  1077.  /***************************************************************************
  1078.   * Find the instance data.                            *
  1079.   ***************************************************************************/
  1080.  
  1081.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1082.  
  1083.  /***************************************************************************
  1084.   * Find out the window's new position and size.                            *
  1085.   ***************************************************************************/
  1086.  
  1087.   HWND hwndFrame = WinQueryWindow ( hwnd, QW_PARENT ) ;
  1088.  
  1089.   SWP Position ;
  1090.   WinQueryWindowPos ( hwndFrame, &Position ) ;
  1091.  
  1092.   if ( NOT ( Position.fl & SWP_MINIMIZE )
  1093.     AND NOT ( Position.fl & SWP_MAXIMIZE ) )
  1094.   {
  1095.     Data->Profile.Position.x = Position.x ;
  1096.     Data->Profile.Position.y = Position.y ;
  1097.  
  1098.     Data->Profile.Position.cx = Position.cx ;
  1099.     Data->Profile.Position.cy = Position.cy ;
  1100.   }
  1101.  
  1102.  /***************************************************************************
  1103.   * If hiding the controls . . .                        *
  1104.   ***************************************************************************/
  1105.  
  1106.   if ( Data->Profile.HideControls )
  1107.   {
  1108.  
  1109.    /*************************************************************************
  1110.     * If changing to or from minimized state . . .                *
  1111.     *************************************************************************/
  1112.  
  1113.     if ( ( Position.fl & SWP_MINIMIZE ) != ( Data->Profile.Position.fl & SWP_MINIMIZE ) )
  1114.     {
  1115.  
  1116.      /***********************************************************************
  1117.       * Hide the controls if no longer minimized.                *
  1118.       ***********************************************************************/
  1119.  
  1120.       HideControls
  1121.       (
  1122.     NOT ( Position.fl & SWP_MINIMIZE ),
  1123.     hwndFrame,
  1124.     Data->hwndSysMenu,
  1125.     Data->hwndTitleBar,
  1126.     Data->hwndMinMax
  1127.       ) ;
  1128.     }
  1129.   }
  1130.  
  1131.   Data->Profile.Position.fl = Position.fl ;
  1132.  
  1133.  /***************************************************************************
  1134.   * We're done.                                                             *
  1135.   ***************************************************************************/
  1136.  
  1137.   return ( 0 ) ;
  1138. }
  1139.  
  1140. /****************************************************************************
  1141.  *                                        *
  1142.  *    Process SAVE APPLICATION message.                    *
  1143.  *                                        *
  1144.  ****************************************************************************/
  1145.  
  1146. STATIC MRESULT APIENTRY SaveApplication
  1147. (
  1148.   HWND hwnd,
  1149.   USHORT msg,
  1150.   MPARAM mp1,
  1151.   MPARAM mp2
  1152. )
  1153. {
  1154.  /***************************************************************************
  1155.   * Find the instance data.                            *
  1156.   ***************************************************************************/
  1157.  
  1158.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1159.  
  1160.  /***************************************************************************
  1161.   * Call function to put all profile data out to the system.            *
  1162.   ***************************************************************************/
  1163.  
  1164.   PutProfile ( Data->ProfileHandle, &Data->Profile ) ;
  1165.  
  1166.  /***************************************************************************
  1167.   * We're done.  Let the system complete default processing.                *
  1168.   ***************************************************************************/
  1169.  
  1170.   return ( WinDefWindowProc ( hwnd, WM_SAVEAPPLICATION, 0, 0 ) ) ;
  1171. }
  1172.  
  1173. /****************************************************************************
  1174.  *                                        *
  1175.  *    Repaint entire window.                            *
  1176.  *                                        *
  1177.  ****************************************************************************/
  1178.  
  1179. STATIC MRESULT APIENTRY Paint
  1180. (
  1181.   HWND hwnd,
  1182.   USHORT msg,
  1183.   MPARAM mp1,
  1184.   MPARAM mp2
  1185. )
  1186. {
  1187.  /***************************************************************************
  1188.   * Find the instance data.                            *
  1189.   ***************************************************************************/
  1190.  
  1191.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1192.  
  1193.  /***************************************************************************
  1194.   * Get presentation space and make it use RGB colors.                *
  1195.   ***************************************************************************/
  1196.  
  1197.   HPS hPS = WinBeginPaint ( hwnd, NULL, NULL ) ;
  1198.   GpiCreateLogColorTable ( hPS, LCOL_RESET, LCOLF_RGB, 0L, 0L, NULL ) ;
  1199.  
  1200.  /***************************************************************************
  1201.   * Clear the window.                                *
  1202.   ***************************************************************************/
  1203.  
  1204.   RECTL Rectangle ;
  1205.   WinQueryWindowRect ( hwnd, &Rectangle ) ;
  1206.  
  1207.   GpiMove ( hPS, (PPOINTL) &Rectangle.xLeft ) ;
  1208.   GpiSetColor ( hPS, Data->Profile.BackColor ) ;
  1209.   GpiBox ( hPS, DRO_FILL, (PPOINTL) &Rectangle.xRight, 0L, 0L ) ;
  1210.  
  1211.  /***************************************************************************
  1212.   * Release presentation space.                         *
  1213.   ***************************************************************************/
  1214.  
  1215.   WinEndPaint ( hPS ) ;
  1216.  
  1217.  /***************************************************************************
  1218.   * Update the window and return.                        *
  1219.   ***************************************************************************/
  1220.  
  1221.   UpdateWindow ( hwnd, Data, TRUE ) ;
  1222.  
  1223.   return ( 0 ) ;
  1224. }
  1225.  
  1226. /****************************************************************************
  1227.  *                                        *
  1228.  *    Process commands received by Main Window                *
  1229.  *                                        *
  1230.  ****************************************************************************/
  1231.  
  1232. STATIC MRESULT APIENTRY Command
  1233. (
  1234.   HWND hwnd,
  1235.   USHORT msg,
  1236.   MPARAM mp1,
  1237.   MPARAM mp2
  1238. )
  1239. {
  1240.  /***************************************************************************
  1241.   * Find the instance data.                            *
  1242.   ***************************************************************************/
  1243.  
  1244.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1245.  
  1246.  /***************************************************************************
  1247.   * Process indicated command . . .                        *
  1248.   ***************************************************************************/
  1249.  
  1250.   USHORT Command = SHORT1FROMMP ( mp1 ) ;
  1251.  
  1252.  /***************************************************************************
  1253.   * Process display item commands.                        *
  1254.   ***************************************************************************/
  1255.  
  1256.   if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
  1257.   {
  1258.     Log ( "ERROR: Unable to lock item list to process display item commands.\r\n" ) ;
  1259.     DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
  1260.   }
  1261.  
  1262.   for ( int i=0; i<Data->Profile.ItemCount; i++ )
  1263.   {
  1264.     PITEM Item = Data->Profile.Items + i ;
  1265.  
  1266.     if ( Command == Item->MenuId )
  1267.     {
  1268.       HWND hwndFrame = WinQueryWindow ( hwnd, QW_PARENT ) ;
  1269.  
  1270.       Item->Flag = Item->Flag ? FALSE : TRUE ;
  1271.  
  1272.       if ( i == ITEM_CPULOAD )
  1273.       {
  1274.     if ( Item->Flag )
  1275.       DosResumeThread ( Data->IdleLoopTID ) ;
  1276.     else
  1277.       DosSuspendThread ( Data->IdleLoopTID ) ;
  1278.       }
  1279.  
  1280.       CheckMenuItem ( hwndFrame, FID_SYSMENU, Item->MenuId, Item->Flag ) ;
  1281.  
  1282.       DosReleaseMutexSem ( ItemSemaphore ) ;
  1283.  
  1284.       ResizeWindow ( hwnd, &Data->Profile ) ;
  1285.  
  1286.       return ( MRFROMSHORT ( 0 ) ) ;
  1287.     }
  1288.   }
  1289.   DosReleaseMutexSem ( ItemSemaphore ) ;
  1290.  
  1291.  /***************************************************************************
  1292.   * Dispatch all other commands through the method table.            *
  1293.   ***************************************************************************/
  1294.  
  1295.   static METHOD Methods [] =
  1296.   {
  1297.     { IDM_SAVE_APPLICATION, SaveApplication },
  1298.     { IDM_RESET_DEFAULTS,   ResetDefaults   },
  1299.     { IDM_HIDE_CONTROLS,    HideControlsCmd },
  1300.     { IDM_SET_TIMER,        SetTimer        },
  1301.     { IDM_EXIT,         Exit        },
  1302.     { IDM_ABOUT,        About        },
  1303.   } ;
  1304.  
  1305.   return ( DispatchMessage ( hwnd, SHORT1FROMMP(mp1), mp1, mp2, Methods, sizeof(Methods)/sizeof(Methods[0]), NULL ) ) ;
  1306. }
  1307.  
  1308. /****************************************************************************
  1309.  *                                        *
  1310.  *    Process Reset Defaults menu command.                    *
  1311.  *                                        *
  1312.  ****************************************************************************/
  1313.  
  1314. STATIC MRESULT APIENTRY ResetDefaults
  1315.   HWND hwnd, 
  1316.   USHORT msg, 
  1317.   MPARAM mp1, 
  1318.   MPARAM mp2
  1319. )
  1320. {
  1321.  /***************************************************************************
  1322.   * Find the instance data.                            *
  1323.   ***************************************************************************/
  1324.  
  1325.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1326.  
  1327.  /***************************************************************************
  1328.   * Reset all profile data for this program.                    *
  1329.   ***************************************************************************/
  1330.  
  1331.   PrfWriteProfileData ( Data->ProfileHandle, (PSZ)PROGRAM_NAME, NULL, NULL, 0 ) ;
  1332.  
  1333.  /***************************************************************************
  1334.   * Reset the program's presentation parameters.                            *
  1335.   ***************************************************************************/
  1336.  
  1337.   WinRemovePresParam ( hwnd, PP_FONTNAMESIZE ) ;
  1338.   WinRemovePresParam ( hwnd, PP_FOREGROUNDCOLOR ) ;
  1339.   WinRemovePresParam ( hwnd, PP_BACKGROUNDCOLOR ) ;
  1340.  
  1341.  /***************************************************************************
  1342.   * Done.                                    *
  1343.   ***************************************************************************/
  1344.  
  1345.   return ( MRFROMSHORT ( 0 ) ) ;
  1346. }
  1347.  
  1348. /****************************************************************************
  1349.  *                                        *
  1350.  *    Process Hide Controls menu command.                    *
  1351.  *                                        *
  1352.  ****************************************************************************/
  1353.  
  1354. STATIC MRESULT APIENTRY HideControlsCmd
  1355.   HWND hwnd, 
  1356.   USHORT msg, 
  1357.   MPARAM mp1, 
  1358.   MPARAM mp2
  1359. )
  1360. {
  1361.  /***************************************************************************
  1362.   * Find the instance data.                            *
  1363.   ***************************************************************************/
  1364.  
  1365.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1366.  
  1367.  /***************************************************************************
  1368.   * Toggle the Hide Controls setting.                        *
  1369.   ***************************************************************************/
  1370.  
  1371.   Data->Profile.HideControls = Data->Profile.HideControls ? FALSE : TRUE ;
  1372.   Data->Profile.fHideControls = TRUE ;
  1373.  
  1374.  /***************************************************************************
  1375.   * Get the frame handle.                                *
  1376.   ***************************************************************************/
  1377.  
  1378.   HWND hwndFrame = WinQueryWindow ( hwnd, QW_PARENT ) ;
  1379.  
  1380.  /***************************************************************************
  1381.   * If controls aren't hidden yet, update the menu check-mark.              *
  1382.   ***************************************************************************/
  1383.  
  1384.   if ( Data->Profile.HideControls )
  1385.     CheckMenuItem ( hwndFrame, FID_SYSMENU, IDM_HIDE_CONTROLS, Data->Profile.HideControls ) ;
  1386.  
  1387.  /***************************************************************************
  1388.   * If not minimized right now, hide or reveal the controls.            *
  1389.   ***************************************************************************/
  1390.  
  1391.   if ( NOT ( Data->Profile.Position.fl & SWP_MINIMIZE ) )
  1392.   {
  1393.     HideControls
  1394.     (
  1395.       Data->Profile.HideControls,
  1396.       hwndFrame,
  1397.       Data->hwndSysMenu,
  1398.       Data->hwndTitleBar,
  1399.       Data->hwndMinMax
  1400.     ) ;
  1401.   }
  1402.  
  1403.  /***************************************************************************
  1404.   * If controls are no longer hidden, update the menu check-mark.        *
  1405.   ***************************************************************************/
  1406.  
  1407.   if ( NOT Data->Profile.HideControls )
  1408.     CheckMenuItem ( hwndFrame, FID_SYSMENU, IDM_HIDE_CONTROLS, Data->Profile.HideControls ) ;
  1409.  
  1410.  /***************************************************************************
  1411.   * Done.                                    *
  1412.   ***************************************************************************/
  1413.  
  1414.   return ( MRFROMSHORT ( 0 ) ) ;
  1415. }
  1416.  
  1417. /****************************************************************************
  1418.  *                                        *
  1419.  *    Process Set Timer menu command.                     *
  1420.  *                                        *
  1421.  ****************************************************************************/
  1422.  
  1423. STATIC MRESULT APIENTRY SetTimer
  1424.   HWND hwnd, 
  1425.   USHORT msg, 
  1426.   MPARAM mp1, 
  1427.   MPARAM mp2
  1428. )
  1429. {
  1430.  /***************************************************************************
  1431.   * Find the instance data.                            *
  1432.   ***************************************************************************/
  1433.  
  1434.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1435.  
  1436.  /***************************************************************************
  1437.   * Invoke the Set Timer dialog.                        *
  1438.   ***************************************************************************/
  1439.  
  1440.   SETTIMER_PARMS Parms ;
  1441.   Parms.id = IDD_SET_TIMER ;
  1442.   Parms.hwndHelp = WinQueryHelpInstance ( hwnd ) ;
  1443.   Parms.TimerInterval = & Data->Profile.TimerInterval ;
  1444.  
  1445.   WinDlgBox ( HWND_DESKTOP, hwnd, SetTimerProcessor,
  1446.     Data->Library, IDD_SET_TIMER, &Parms ) ;
  1447.  
  1448.  /***************************************************************************
  1449.   * Done.                                    *
  1450.   ***************************************************************************/
  1451.  
  1452.   return ( MRFROMSHORT ( 0 ) ) ;
  1453. }
  1454.  
  1455. /****************************************************************************
  1456.  *                                        *
  1457.  *    Process About menu command.                        *
  1458.  *                                        *
  1459.  ****************************************************************************/
  1460.  
  1461. STATIC MRESULT APIENTRY About
  1462.   HWND hwnd, 
  1463.   USHORT msg, 
  1464.   MPARAM mp1, 
  1465.   MPARAM mp2
  1466. )
  1467. {
  1468.  /***************************************************************************
  1469.   * Find the instance data.                            *
  1470.   ***************************************************************************/
  1471.  
  1472.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1473.  
  1474.  /***************************************************************************
  1475.   * Invoke the About dialog.                            *
  1476.   ***************************************************************************/
  1477.  
  1478.   ABOUT_PARMS Parms ;
  1479.   Parms.id = IDD_ABOUT ;
  1480.   Parms.hwndHelp = WinQueryHelpInstance ( hwnd ) ;
  1481.  
  1482.   WinDlgBox ( HWND_DESKTOP, hwnd, AboutProcessor,
  1483.     Data->Library, IDD_ABOUT, &Parms ) ;
  1484.  
  1485.  /***************************************************************************
  1486.   * Done.                                    *
  1487.   ***************************************************************************/
  1488.  
  1489.   return ( MRFROMSHORT ( 0 ) ) ;
  1490. }
  1491.  
  1492. /****************************************************************************
  1493.  *                                        *
  1494.  *    Process Mouse Button being pressed.                    *
  1495.  *                                        *
  1496.  ****************************************************************************/
  1497.  
  1498. STATIC MRESULT APIENTRY ButtonDown
  1499. (
  1500.   HWND hwnd,
  1501.   USHORT msg,
  1502.   MPARAM mp1,
  1503.   MPARAM mp2
  1504. )
  1505. {
  1506.  /***************************************************************************
  1507.   * Find the instance data.                            *
  1508.   ***************************************************************************/
  1509.  
  1510.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1511.  
  1512.  /***************************************************************************
  1513.   * Determine the new window position.                        *
  1514.   ***************************************************************************/
  1515.  
  1516.   TRACKINFO TrackInfo ;
  1517.   memset ( &TrackInfo, 0, sizeof(TrackInfo) ) ;
  1518.  
  1519.   TrackInfo.cxBorder = 1 ;
  1520.   TrackInfo.cyBorder = 1 ;
  1521.   TrackInfo.cxGrid = 1 ;
  1522.   TrackInfo.cyGrid = 1 ;
  1523.   TrackInfo.cxKeyboard = 8 ;
  1524.   TrackInfo.cyKeyboard = 8 ;
  1525.  
  1526.   HWND hwndFrame = WinQueryWindow ( hwnd, QW_PARENT ) ;
  1527.  
  1528.   SWP Position ;
  1529.   WinQueryWindowPos ( hwndFrame, &Position ) ;
  1530.   TrackInfo.rclTrack.xLeft   = Position.x ;
  1531.   TrackInfo.rclTrack.xRight  = Position.x + Position.cx ;
  1532.   TrackInfo.rclTrack.yBottom = Position.y ;
  1533.   TrackInfo.rclTrack.yTop    = Position.y + Position.cy ;
  1534.  
  1535.   WinQueryWindowPos ( HWND_DESKTOP, &Position ) ;
  1536.   TrackInfo.rclBoundary.xLeft   = Position.x ;
  1537.   TrackInfo.rclBoundary.xRight  = Position.x + Position.cx ;
  1538.   TrackInfo.rclBoundary.yBottom = Position.y ;
  1539.   TrackInfo.rclBoundary.yTop    = Position.y + Position.cy ;
  1540.  
  1541.   TrackInfo.ptlMinTrackSize.x = 0 ;
  1542.   TrackInfo.ptlMinTrackSize.y = 0 ;
  1543.   TrackInfo.ptlMaxTrackSize.x = Position.cx ;
  1544.   TrackInfo.ptlMaxTrackSize.y = Position.cy ;
  1545.  
  1546.   TrackInfo.fs = TF_MOVE | TF_STANDARD | TF_ALLINBOUNDARY ;
  1547.  
  1548.   if ( WinTrackRect ( HWND_DESKTOP, NULL, &TrackInfo ) )
  1549.   {
  1550.     WinSetWindowPos ( hwndFrame, NULL,
  1551.       (SHORT) TrackInfo.rclTrack.xLeft,
  1552.       (SHORT) TrackInfo.rclTrack.yBottom,
  1553.       0, 0, SWP_MOVE ) ;
  1554.   }
  1555.  
  1556.  /***************************************************************************
  1557.   * Return through the default processor, letting window activation        *
  1558.   *   and other system functions occur.                     *
  1559.   ***************************************************************************/
  1560.  
  1561.   return ( WinDefWindowProc ( hwnd, msg, mp1, mp2 ) ) ;
  1562. }
  1563.  
  1564. /****************************************************************************
  1565.  *                                        *
  1566.  *    Process Mouse Button having been double-clicked.            *
  1567.  *                                        *
  1568.  ****************************************************************************/
  1569.  
  1570. STATIC MRESULT APIENTRY ButtonDblClick
  1571. (
  1572.   HWND hwnd,
  1573.   USHORT msg,
  1574.   MPARAM mp1,
  1575.   MPARAM mp2
  1576. )
  1577. {
  1578.  /***************************************************************************
  1579.   * Send message to self to stop hiding the controls.                *
  1580.   ***************************************************************************/
  1581.  
  1582.   WinPostMsg ( hwnd, WM_COMMAND,
  1583.     MPFROM2SHORT ( IDM_HIDE_CONTROLS, 0 ),
  1584.     MPFROM2SHORT ( CMDSRC_OTHER, TRUE ) ) ;
  1585.  
  1586.  /***************************************************************************
  1587.   * Return through the default processor, letting window activation        *
  1588.   *   and other system functions occur.                     *
  1589.   ***************************************************************************/
  1590.  
  1591.   return ( WinDefWindowProc ( hwnd, msg, mp1, mp2 ) ) ;
  1592. }
  1593.  
  1594. /****************************************************************************
  1595.  *                                        *
  1596.  *    Process Presentation Parameter Changed notification.            *
  1597.  *                                        *
  1598.  ****************************************************************************/
  1599.  
  1600. STATIC MRESULT APIENTRY PresParamChanged
  1601. (
  1602.   HWND hwnd,
  1603.   USHORT msg,
  1604.   MPARAM mp1,
  1605.   MPARAM mp2
  1606. )
  1607. {
  1608.  /***************************************************************************
  1609.   * Find the instance data.                            *
  1610.   ***************************************************************************/
  1611.  
  1612.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1613.  
  1614.  /***************************************************************************
  1615.   * Get the presentation parameter that changed.                *
  1616.   ***************************************************************************/
  1617.  
  1618.   switch ( LONGFROMMP(mp1) )
  1619.   {
  1620.  
  1621.    /*************************************************************************
  1622.     * If font, note the fact that we now have a font to be saved as        *
  1623.     *    part of the configuration.  Get the font metrics and resize        *
  1624.     *    the window appropriately.                        *
  1625.     *************************************************************************/
  1626.  
  1627.     case PP_FONTNAMESIZE:
  1628.     {
  1629.       ULONG ppid ;
  1630.       if ( WinQueryPresParam ( hwnd, PP_FONTNAMESIZE, 0, &ppid,
  1631.     sizeof(Data->Profile.FontNameSize), &Data->Profile.FontNameSize,
  1632.     0 ) )
  1633.       {
  1634.     Data->Profile.fFontNameSize = TRUE ;
  1635.       }
  1636.       else
  1637.       {
  1638.     strcpy ( (PCHAR)Data->Profile.FontNameSize, "" ) ;
  1639.     Data->Profile.fFontNameSize = FALSE ;
  1640.     PrfWriteProfileData ( Data->ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"FontNameSize", NULL, 0 ) ;
  1641.       }
  1642.  
  1643.       HPS hPS = WinGetPS ( hwnd ) ;
  1644.       RECTL Rectangle ;
  1645.       WinQueryWindowRect ( HWND_DESKTOP, &Rectangle ) ;
  1646.       WinDrawText ( hPS, 1, (PSZ)" ", &Rectangle, 0L, 0L, DT_LEFT | DT_BOTTOM | DT_QUERYEXTENT ) ;
  1647.       Data->Width  = Rectangle.xRight - Rectangle.xLeft ;
  1648.       Data->Height = Rectangle.yTop - Rectangle.yBottom ;
  1649.       WinReleasePS ( hPS ) ;
  1650.       ResizeWindow ( hwnd, &Data->Profile ) ;
  1651.       break ;
  1652.     }
  1653.  
  1654.    /*************************************************************************
  1655.     * If background color, note the fact and repaint the window.        *
  1656.     *************************************************************************/
  1657.  
  1658.     case PP_BACKGROUNDCOLOR:
  1659.     {
  1660.       ULONG ppid ;
  1661.       if ( WinQueryPresParam ( hwnd, PP_BACKGROUNDCOLOR, 0, &ppid,
  1662.     sizeof(Data->Profile.BackColor), &Data->Profile.BackColor, 0 ) )
  1663.       {
  1664.     Data->Profile.fBackColor = TRUE ;
  1665.       }
  1666.       else
  1667.       {
  1668.     Data->Profile.BackColor = WinQuerySysColor ( HWND_DESKTOP, SYSCLR_WINDOW, 0L ) ;
  1669.     Data->Profile.fBackColor = FALSE ;
  1670.     PrfWriteProfileData ( Data->ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"BackgroundColor", NULL, 0 ) ;
  1671.       }
  1672.       WinInvalidateRect ( hwnd, NULL, TRUE ) ;
  1673.       break ;
  1674.     }
  1675.  
  1676.    /*************************************************************************
  1677.     * If foreground color, note the fact and repaint the window.        *
  1678.     *************************************************************************/
  1679.  
  1680.     case PP_FOREGROUNDCOLOR:
  1681.     {
  1682.       ULONG ppid ;
  1683.       if ( WinQueryPresParam ( hwnd, PP_FOREGROUNDCOLOR, 0, &ppid,
  1684.     sizeof(Data->Profile.TextColor), &Data->Profile.TextColor, 0 ) )
  1685.       {
  1686.     Data->Profile.fTextColor = TRUE ;
  1687.       }
  1688.       else
  1689.       {
  1690.     Data->Profile.TextColor = WinQuerySysColor ( HWND_DESKTOP, SYSCLR_OUTPUTTEXT, 0L ) ;
  1691.     Data->Profile.fTextColor = FALSE ;
  1692.     PrfWriteProfileData ( Data->ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"ForegroundColor", NULL, 0 ) ;
  1693.       }
  1694.       WinInvalidateRect ( hwnd, NULL, TRUE ) ;
  1695.       break ;
  1696.     }
  1697.   }
  1698.  
  1699.  /***************************************************************************
  1700.   * Return through the default processor, letting window activation        *
  1701.   *   and other system functions occur.                     *
  1702.   ***************************************************************************/
  1703.  
  1704.   return ( WinDefWindowProc ( hwnd, msg, mp1, mp2 ) ) ;
  1705. }
  1706.  
  1707. /****************************************************************************
  1708.  *                                        *
  1709.  *    Process System Color Change notification.                *
  1710.  *                                        *
  1711.  ****************************************************************************/
  1712.  
  1713. STATIC MRESULT APIENTRY SysColorChange
  1714. (
  1715.   HWND hwnd,
  1716.   USHORT msg,
  1717.   MPARAM mp1,
  1718.   MPARAM mp2
  1719. )
  1720. {
  1721.  /***************************************************************************
  1722.   * Find the instance data.                            *
  1723.   ***************************************************************************/
  1724.  
  1725.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1726.  
  1727.  /***************************************************************************
  1728.   * If we aren't using custom colors, then query for the new defaults.      *
  1729.   ***************************************************************************/
  1730.  
  1731.   if ( NOT Data->Profile.fBackColor )
  1732.   {
  1733.     Data->Profile.BackColor = WinQuerySysColor ( HWND_DESKTOP, SYSCLR_WINDOW, 0L ) ;
  1734.   }
  1735.  
  1736.   if ( NOT Data->Profile.fTextColor )
  1737.   {
  1738.     Data->Profile.TextColor = WinQuerySysColor ( HWND_DESKTOP, SYSCLR_OUTPUTTEXT, 0L ) ;
  1739.   }
  1740.  
  1741.  /***************************************************************************
  1742.   * Return value must be NULL, according to the documentation.            *
  1743.   ***************************************************************************/
  1744.  
  1745.   return ( MRFROMP ( NULL ) ) ;
  1746. }
  1747.  
  1748. /****************************************************************************
  1749.  *                                        *
  1750.  *    Process Query for Keys Help resource id.                *
  1751.  *                                        *
  1752.  ****************************************************************************/
  1753.  
  1754. STATIC MRESULT APIENTRY QueryKeysHelp
  1755. (
  1756.   HWND hwnd,
  1757.   USHORT msg,
  1758.   MPARAM mp1,
  1759.   MPARAM mp2
  1760. )
  1761. {
  1762.  /***************************************************************************
  1763.   * Simply return the ID of the Keys Help panel.                *
  1764.   ***************************************************************************/
  1765.  
  1766.   return ( (MRESULT) IDM_KEYS_HELP ) ;
  1767. }
  1768.  
  1769. /****************************************************************************
  1770.  *                                        *
  1771.  *    Process Help Manager Error                        *
  1772.  *                                        *
  1773.  ****************************************************************************/
  1774.  
  1775. STATIC MRESULT APIENTRY HelpError
  1776.   HWND hwnd, 
  1777.   USHORT msg, 
  1778.   MPARAM mp1, 
  1779.   MPARAM mp2
  1780. )
  1781. {
  1782.  /***************************************************************************
  1783.   * Local Declarations                                *
  1784.   ***************************************************************************/
  1785.  
  1786.   static struct
  1787.   {
  1788.     ULONG Error ;
  1789.     USHORT StringId ;
  1790.   }
  1791.   HelpErrors [] =
  1792.   {
  1793.     { HMERR_NO_FRAME_WND_IN_CHAIN,     IDS_HMERR_NO_FRAME_WND_IN_CHAIN },
  1794.     { HMERR_INVALID_ASSOC_APP_WND,     IDS_HMERR_INVALID_ASSOC_APP_WND },
  1795.     { HMERR_INVALID_ASSOC_HELP_INST,   IDS_HMERR_INVALID_ASSOC_HELP_IN },
  1796.     { HMERR_INVALID_DESTROY_HELP_INST, IDS_HMERR_INVALID_DESTROY_HELP_ },
  1797.     { HMERR_NO_HELP_INST_IN_CHAIN,     IDS_HMERR_NO_HELP_INST_IN_CHAIN },
  1798.     { HMERR_INVALID_HELP_INSTANCE_HDL, IDS_HMERR_INVALID_HELP_INSTANCE },
  1799.     { HMERR_INVALID_QUERY_APP_WND,     IDS_HMERR_INVALID_QUERY_APP_WND },
  1800.     { HMERR_HELP_INST_CALLED_INVALID,  IDS_HMERR_HELP_INST_CALLED_INVA },
  1801.     { HMERR_HELPTABLE_UNDEFINE,        IDS_HMERR_HELPTABLE_UNDEFINE    },
  1802.     { HMERR_HELP_INSTANCE_UNDEFINE,    IDS_HMERR_HELP_INSTANCE_UNDEFIN },
  1803.     { HMERR_HELPITEM_NOT_FOUND,        IDS_HMERR_HELPITEM_NOT_FOUND    },
  1804.     { HMERR_INVALID_HELPSUBITEM_SIZE,  IDS_HMERR_INVALID_HELPSUBITEM_S },
  1805.     { HMERR_HELPSUBITEM_NOT_FOUND,     IDS_HMERR_HELPSUBITEM_NOT_FOUND },
  1806.     { HMERR_INDEX_NOT_FOUND,           IDS_HMERR_INDEX_NOT_FOUND       },
  1807.     { HMERR_CONTENT_NOT_FOUND,           IDS_HMERR_CONTENT_NOT_FOUND     },
  1808.     { HMERR_OPEN_LIB_FILE,           IDS_HMERR_OPEN_LIB_FILE           },
  1809.     { HMERR_READ_LIB_FILE,           IDS_HMERR_READ_LIB_FILE           },
  1810.     { HMERR_CLOSE_LIB_FILE,           IDS_HMERR_CLOSE_LIB_FILE        },
  1811.     { HMERR_INVALID_LIB_FILE,           IDS_HMERR_INVALID_LIB_FILE      },
  1812.     { HMERR_NO_MEMORY,               IDS_HMERR_NO_MEMORY           },
  1813.     { HMERR_ALLOCATE_SEGMENT,           IDS_HMERR_ALLOCATE_SEGMENT      },
  1814.     { HMERR_FREE_MEMORY,           IDS_HMERR_FREE_MEMORY           },
  1815.     { HMERR_PANEL_NOT_FOUND,           IDS_HMERR_PANEL_NOT_FOUND       },
  1816.     { HMERR_DATABASE_NOT_OPEN,           IDS_HMERR_DATABASE_NOT_OPEN     },
  1817.     { 0,                   IDS_HMERR_UNKNOWN           }
  1818.   } ;
  1819.  
  1820.   ULONG ErrorCode = (ULONG) LONGFROMMP ( mp1 ) ;
  1821.  
  1822.  /***************************************************************************
  1823.   * Find the instance data.                            *
  1824.   ***************************************************************************/
  1825.  
  1826.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1827.  
  1828.  /***************************************************************************
  1829.   * Find the error code in the message table.                    *
  1830.   ***************************************************************************/
  1831.  
  1832.   int Index = 0 ;
  1833.   while ( HelpErrors[Index].Error
  1834.     AND ( HelpErrors[Index].Error != ErrorCode ) )
  1835.   {
  1836.     Index ++ ;
  1837.   }
  1838.  
  1839.  /***************************************************************************
  1840.   * Get the message texts.                            *
  1841.   ***************************************************************************/
  1842.  
  1843.   BYTE Title [80] ;
  1844.   WinLoadString ( Data->Anchor, Data->Library, IDS_HMERR, sizeof(Title), Title ) ;
  1845.  
  1846.   BYTE Message [200] ;
  1847.   WinLoadString ( Data->Anchor, Data->Library, HelpErrors[Index].StringId, sizeof(Message), Message ) ;
  1848.  
  1849.  /***************************************************************************
  1850.   * Display the error message.                            *
  1851.   ***************************************************************************/
  1852.  
  1853.   WinMessageBox
  1854.   (
  1855.     HWND_DESKTOP,
  1856.     hwnd,
  1857.     Message,
  1858.     Title,
  1859.     0,
  1860.     MB_OK | MB_WARNING
  1861.   ) ;
  1862.  
  1863.  /***************************************************************************
  1864.   * Return zero, indicating that the message was processed.            *
  1865.   ***************************************************************************/
  1866.  
  1867.   return ( MRFROMSHORT ( 0 ) ) ;
  1868. }
  1869.  
  1870. /****************************************************************************
  1871.  *                                        *
  1872.  *    Process "Extended Help Undefined" notification                *
  1873.  *                                        *
  1874.  ****************************************************************************/
  1875.  
  1876. STATIC MRESULT APIENTRY ExtHelpUndefined
  1877.   HWND hwnd, 
  1878.   USHORT msg, 
  1879.   MPARAM mp1, 
  1880.   MPARAM mp2
  1881. )
  1882. {
  1883.  /***************************************************************************
  1884.   * Find the instance data.                            *
  1885.   ***************************************************************************/
  1886.  
  1887.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1888.  
  1889.  /***************************************************************************
  1890.   * Get the message texts.                            *
  1891.   ***************************************************************************/
  1892.  
  1893.   BYTE Title [80] ;
  1894.   WinLoadString ( Data->Anchor, Data->Library, IDS_HMERR, sizeof(Title), Title ) ;
  1895.  
  1896.   BYTE Message [200] ;
  1897.   WinLoadString ( Data->Anchor, Data->Library, IDS_HMERR_EXTHELPUNDEFINED, sizeof(Message), Message ) ;
  1898.  
  1899.  /***************************************************************************
  1900.   * Display the error message.                            *
  1901.   ***************************************************************************/
  1902.  
  1903.   WinMessageBox
  1904.   (
  1905.     HWND_DESKTOP,
  1906.     hwnd,
  1907.     Message,
  1908.     Title,
  1909.     0,
  1910.     MB_OK | MB_WARNING
  1911.   ) ;
  1912.  
  1913.  /***************************************************************************
  1914.   * Return zero, indicating that the message was processed.            *
  1915.   ***************************************************************************/
  1916.  
  1917.   return ( MRFROMSHORT ( 0 ) ) ;
  1918. }
  1919.  
  1920. /****************************************************************************
  1921.  *                                        *
  1922.  *    Process "Help Subitem Not Found" notification                *
  1923.  *                                        *
  1924.  ****************************************************************************/
  1925.  
  1926. STATIC MRESULT APIENTRY HelpSubitemNotFound
  1927.   HWND hwnd, 
  1928.   USHORT msg, 
  1929.   MPARAM mp1, 
  1930.   MPARAM mp2
  1931. )
  1932. {
  1933.  /***************************************************************************
  1934.   * Find the instance data.                            *
  1935.   ***************************************************************************/
  1936.  
  1937.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  1938.  
  1939.  /***************************************************************************
  1940.   * Get the title text.                             *
  1941.   ***************************************************************************/
  1942.  
  1943.   BYTE Title [80] ;
  1944.   WinLoadString ( Data->Anchor, Data->Library, IDS_HMERR, sizeof(Title), Title ) ;
  1945.  
  1946.  /***************************************************************************
  1947.   * Format the error message.                            *
  1948.   ***************************************************************************/
  1949.  
  1950.   USHORT Topic = (USHORT) SHORT1FROMMP ( mp2 ) ;
  1951.   USHORT Subtopic = (USHORT) SHORT2FROMMP ( mp2 ) ;
  1952.  
  1953.   BYTE Mode [40] ;
  1954.   switch ( SHORT1FROMMP ( mp1 ) )
  1955.   {
  1956.     case HLPM_FRAME:
  1957.       WinLoadString ( Data->Anchor, Data->Library, IDS_HELPMODE_FRAME, sizeof(Mode), Mode ) ;
  1958.       break ;
  1959.  
  1960.     case HLPM_MENU:
  1961.       WinLoadString ( Data->Anchor, Data->Library, IDS_HELPMODE_MENU, sizeof(Mode), Mode ) ;
  1962.       break ;
  1963.  
  1964.     case HLPM_WINDOW:
  1965.       WinLoadString ( Data->Anchor, Data->Library, IDS_HELPMODE_WINDOW, sizeof(Mode), Mode ) ;
  1966.       break ;
  1967.  
  1968.     default:
  1969.       WinLoadString ( Data->Anchor, Data->Library, IDS_HELPMODE_UNKNOWN, sizeof(Mode), Mode ) ;
  1970.   }
  1971.  
  1972.   BYTE Format [200] ;
  1973.   WinLoadString ( Data->Anchor, Data->Library, IDS_HELPSUBITEMNOTFOUND, sizeof(Format), Format ) ;
  1974.  
  1975.   BYTE Message [200] ;
  1976.   sprintf ( (PCHAR)Message, (PCHAR)Format, Mode, Topic, Subtopic ) ;
  1977.  
  1978.  /***************************************************************************
  1979.   * Display the error message.                            *
  1980.   ***************************************************************************/
  1981.  
  1982.   WinMessageBox
  1983.   (
  1984.     HWND_DESKTOP,
  1985.     hwnd,
  1986.     Message,
  1987.     Title,
  1988.     0,
  1989.     MB_OK | MB_WARNING
  1990.   ) ;
  1991.  
  1992.  /***************************************************************************
  1993.   * Return zero, indicating that the message was processed.            *
  1994.   ***************************************************************************/
  1995.  
  1996.   return ( MRFROMSHORT ( 0 ) ) ;
  1997. }
  1998.  
  1999. /****************************************************************************
  2000.  *                                        *
  2001.  *    Process Refresh message.                        *
  2002.  *                                        *
  2003.  ****************************************************************************/
  2004.  
  2005. STATIC MRESULT APIENTRY Refresh
  2006.   HWND hwnd, 
  2007.   USHORT msg, 
  2008.   MPARAM mp1, 
  2009.   MPARAM mp2
  2010. )
  2011. {
  2012.  /***************************************************************************
  2013.   * Find the instance data.                            *
  2014.   ***************************************************************************/
  2015.  
  2016.   PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;
  2017.  
  2018.  /***************************************************************************
  2019.   * Save the idle counter.                            *
  2020.   ***************************************************************************/
  2021.  
  2022.   Data->IdleCount = LONGFROMMP ( mp1 ) ;
  2023.  
  2024.  /***************************************************************************
  2025.   * Determine if drive mask has changed.                    *
  2026.   ***************************************************************************/
  2027.  
  2028.   ULONG Drive ;
  2029.   ULONG Drives ;
  2030.   DosQueryCurrentDisk ( &Drive, &Drives ) ;
  2031.  
  2032.   if ( Drives != Data->Drives )
  2033.   {
  2034.    /*************************************************************************
  2035.     * It has.  First save the display options.                    *
  2036.     *************************************************************************/
  2037.  
  2038.     SaveApplication ( hwnd, WM_SAVEAPPLICATION, 0, 0 ) ;
  2039.  
  2040.    /*************************************************************************
  2041.     * Next, update the drive item list.                     *
  2042.     *************************************************************************/
  2043.  
  2044.     UpdateDriveList ( Data->Anchor, Data->Library, Data->ProfileHandle, 
  2045.       &Data->Profile, Data->Drives, Drives ) ;
  2046.  
  2047.    /*************************************************************************
  2048.     * If the controls are hidden, hide the whole window and reveal the        *
  2049.     *    controls.  Otherwise the menu wouldn't get updated correctly.       *
  2050.     *************************************************************************/
  2051.  
  2052.     if ( Data->Profile.HideControls )
  2053.     {
  2054.       WinShowWindow ( WinQueryWindow(hwnd,QW_PARENT), FALSE ) ;
  2055.       HideControls
  2056.       (
  2057.     FALSE,
  2058.     WinQueryWindow ( hwnd, QW_PARENT ),
  2059.     Data->hwndSysMenu,
  2060.     Data->hwndTitleBar,
  2061.     Data->hwndMinMax
  2062.       ) ;
  2063.     }
  2064.  
  2065.    /*************************************************************************
  2066.     * Update the menu.                                *
  2067.     *************************************************************************/
  2068.  
  2069.     RebuildDisplayItems ( hwnd, Data ) ;
  2070.  
  2071.    /*************************************************************************
  2072.     * If the controls were supposed to be hidden, hide them once more and   *
  2073.     *    show the window to the world again.                    *
  2074.     *************************************************************************/
  2075.  
  2076.     if ( Data->Profile.HideControls )
  2077.     {
  2078.       HideControls
  2079.       (
  2080.     TRUE,
  2081.     WinQueryWindow ( hwnd, QW_PARENT ),
  2082.     Data->hwndSysMenu,
  2083.     Data->hwndTitleBar,
  2084.     Data->hwndMinMax
  2085.       ) ;
  2086.       WinShowWindow ( WinQueryWindow(hwnd,QW_PARENT), TRUE ) ;
  2087.     }
  2088.  
  2089.    /*************************************************************************
  2090.     * Save the updated drive mask.                        *
  2091.     *************************************************************************/
  2092.  
  2093.     Data->Drives = Drives ;
  2094.  
  2095.    /*************************************************************************
  2096.     * Resize the window to accommodate the new option list.            *
  2097.     *************************************************************************/
  2098.  
  2099.     ResizeWindow ( hwnd, &Data->Profile ) ;
  2100.   }
  2101.  
  2102.  /***************************************************************************
  2103.   * Update the statistics.                            *
  2104.   ***************************************************************************/
  2105.  
  2106.   UpdateWindow ( hwnd, Data, FALSE ) ;
  2107.  
  2108.  /***************************************************************************
  2109.   * Return zero, indicating that the message was processed.            *
  2110.   ***************************************************************************/
  2111.  
  2112.   return ( MRFROMSHORT ( 0 ) ) ;
  2113. }
  2114.  
  2115.  
  2116. /****************************************************************************
  2117.  *                                        *
  2118.  *                 Get Profile Data                    *
  2119.  *                                        *
  2120.  ****************************************************************************/
  2121.  
  2122. STATIC int GetProfile ( HAB Anchor, HMODULE Library, HINI ProfileHandle, PPROFILE Profile )
  2123. {
  2124.  /***************************************************************************
  2125.   * Get the window's current size and position.                             *
  2126.   ***************************************************************************/
  2127.  
  2128.   #pragma pack(2)
  2129.   typedef struct {
  2130.     USHORT Filler ;
  2131.     USHORT fs ;
  2132.     USHORT cy, cx, y, x ;
  2133.     HWND hwndInsertBehind ;
  2134.     HWND hwnd ;
  2135.   } OLDSWP ;
  2136.   #pragma pack()
  2137.  
  2138.   ULONG Size ;
  2139.   memset ( &Profile->Position, 0, sizeof(Profile->Position) ) ;
  2140.   Profile->fPosition = FALSE ;
  2141.   if ( PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"Position", &Size ) )
  2142.   {
  2143.     if ( Size == sizeof(OLDSWP)-sizeof(USHORT) )
  2144.     {
  2145.       OLDSWP OldPosition ;
  2146.       if ( PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"Position", &OldPosition.fs, &Size ) )
  2147.       {
  2148.         Profile->Position.fl = OldPosition.fs ;
  2149.         Profile->Position.cy = OldPosition.cy ;
  2150.         Profile->Position.cx = OldPosition.cx ;
  2151.         Profile->Position.y = OldPosition.y ;
  2152.         Profile->Position.x = OldPosition.x ;
  2153.         Profile->Position.hwndInsertBehind = OldPosition.hwndInsertBehind ;
  2154.         Profile->Position.hwnd = OldPosition.hwnd ;
  2155.         Profile->fPosition = TRUE ;
  2156.       }
  2157.     }
  2158.     else if ( Size == sizeof(Profile->Position) )
  2159.     {
  2160.       if ( PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"Position", &Profile->Position, &Size ) )
  2161.       {
  2162.         Profile->fPosition = TRUE ;
  2163.       }
  2164.     }
  2165.   }
  2166.  
  2167.   if ( NOT Profile->fPosition )
  2168.   {
  2169.     if ( ProfileHandle == HINI_USERPROFILE )
  2170.     {
  2171.       return ( 1 ) ;
  2172.     }
  2173.   }
  2174.  
  2175.  /***************************************************************************
  2176.   * Get the program options.                            *
  2177.   ***************************************************************************/
  2178.  
  2179.   Profile->HideControls = FALSE ;
  2180.   Profile->fHideControls = FALSE ;
  2181.   if 
  2182.   ( 
  2183.     PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"HideControls", &Size )
  2184.     AND
  2185.     ( ( Size == sizeof(Profile->HideControls) ) OR ( Size == sizeof(short) ) )
  2186.     AND
  2187.     PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"HideControls", &Profile->HideControls, &Size )
  2188.   )
  2189.   {
  2190.     Profile->fHideControls = TRUE ;
  2191.   }
  2192.  
  2193.   Profile->TimerInterval = 1000 ;
  2194.   Profile->fTimerInterval = FALSE ;
  2195.   if 
  2196.   ( 
  2197.     PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"TimerInterval", &Size )
  2198.     AND
  2199.     ( ( Size == sizeof(Profile->TimerInterval) ) OR ( Size == sizeof(short) ) )
  2200.     AND
  2201.     PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"TimerInterval", &Profile->TimerInterval, &Size ) 
  2202.   )
  2203.   {
  2204.     Profile->fTimerInterval = TRUE ;
  2205.   }
  2206.  
  2207.  /***************************************************************************
  2208.   * Get the presentation parameters.                        *
  2209.   ***************************************************************************/
  2210.  
  2211.   strcpy ( (PCHAR)Profile->FontNameSize, "" ) ;
  2212.   Profile->fFontNameSize = FALSE ;
  2213.   if
  2214.   (
  2215.     PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"FontNameSize", &Size )
  2216.     AND
  2217.     ( Size == sizeof(Profile->FontNameSize) )
  2218.     AND
  2219.     PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"FontNameSize", &Profile->FontNameSize, &Size )
  2220.   )
  2221.   {
  2222.     Profile->fFontNameSize = TRUE ;
  2223.   }
  2224.  
  2225.   Profile->BackColor = WinQuerySysColor ( HWND_DESKTOP, SYSCLR_WINDOW, 0L ) ;
  2226.   Profile->fBackColor = FALSE ;
  2227.   if
  2228.   (
  2229.     PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"BackgroundColor", &Size )
  2230.     AND
  2231.     ( Size == sizeof(Profile->BackColor) )
  2232.     AND
  2233.     PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"BackgroundColor", &Profile->BackColor, &Size )
  2234.   )
  2235.   {
  2236.     Profile->fBackColor = TRUE ;
  2237.   }
  2238.  
  2239.   Profile->TextColor = WinQuerySysColor ( HWND_DESKTOP, SYSCLR_OUTPUTTEXT, 0L ) ;
  2240.   Profile->fTextColor = FALSE ;
  2241.   if
  2242.   (
  2243.     PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"ForegroundColor", &Size )
  2244.     AND
  2245.     ( Size == sizeof(Profile->TextColor) )
  2246.     AND
  2247.     PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"ForegroundColor", &Profile->TextColor, &Size )
  2248.   )
  2249.   {
  2250.     Profile->fTextColor = TRUE ;
  2251.   }
  2252.  
  2253.  /***************************************************************************
  2254.   * Lock the item list.                             *
  2255.   ***************************************************************************/
  2256.  
  2257.   if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
  2258.   {
  2259.     Log ( "ERROR: Unable to lock item list to build it.\r\n" ) ;
  2260.     DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
  2261.   }
  2262.  
  2263.  /***************************************************************************
  2264.   * Build the fixed portion of the item list.                    *
  2265.   ***************************************************************************/
  2266.  
  2267.   for ( int i=0; i<ITEM_BASE_COUNT; i++ )
  2268.   {
  2269.     WinLoadString ( Anchor, Library, i*2+IDS_SHOW_CLOCK_LABEL,
  2270.       sizeof(Items[i].Label), Items[i].Label ) ;
  2271.  
  2272.     WinLoadString ( Anchor, Library, i*2+IDS_SHOW_CLOCK_OPTION,
  2273.       sizeof(Items[i].MenuOption), Items[i].MenuOption ) ;
  2274.  
  2275.     Items[i].Flag = TRUE ;
  2276.     if 
  2277.     ( 
  2278.       PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, Items[i].Name, &Size ) 
  2279.       AND
  2280.       ( ( Size == sizeof(Profile->HideControls) ) OR ( Size == sizeof(short) ) )
  2281.       AND 
  2282.       PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, Items[i].Name, &Items[i].Flag, &Size )
  2283.     )
  2284.     {
  2285.       ;
  2286.     }
  2287.   }
  2288.  
  2289.  /***************************************************************************
  2290.   * Release the item list.                            *
  2291.   ***************************************************************************/
  2292.  
  2293.   DosReleaseMutexSem ( ItemSemaphore ) ;
  2294.  
  2295.  /***************************************************************************
  2296.   * Add items for each drive on the system.                    *
  2297.   ***************************************************************************/
  2298.  
  2299.   ULONG Drive, Drives ;
  2300.   DosQueryCurrentDisk ( &Drive, &Drives ) ;
  2301.   UpdateDriveList ( Anchor, Library, ProfileHandle, Profile, 0, Drives ) ;
  2302.  
  2303.   return ( 0 ) ;
  2304. }
  2305.  
  2306. /****************************************************************************
  2307.  *                                        *
  2308.  *                 Put Profile Data                    *
  2309.  *                                        *
  2310.  ****************************************************************************/
  2311.  
  2312. STATIC void PutProfile ( HINI ProfileHandle, PPROFILE Profile )
  2313. {
  2314.  /***************************************************************************
  2315.   * Save the window's current size and position.                            *
  2316.   ***************************************************************************/
  2317.  
  2318.   PrfWriteProfileData
  2319.   (
  2320.     ProfileHandle,
  2321.     (PSZ)PROGRAM_NAME,
  2322.     (PSZ)"Position",
  2323.     &Profile->Position,
  2324.     sizeof(Profile->Position)
  2325.   ) ;
  2326.  
  2327.  /***************************************************************************
  2328.   * Save the program options.                            *
  2329.   ***************************************************************************/
  2330.  
  2331.   if ( Profile->fHideControls )
  2332.   {
  2333.     PrfWriteProfileData
  2334.     (
  2335.       ProfileHandle,
  2336.       (PSZ)PROGRAM_NAME,
  2337.       (PSZ)"HideControls",
  2338.       &Profile->HideControls,
  2339.       sizeof(Profile->HideControls)
  2340.     ) ;
  2341.   }
  2342.  
  2343.   if ( Profile->fTimerInterval )
  2344.   {
  2345.     PrfWriteProfileData
  2346.     (
  2347.       ProfileHandle,
  2348.       (PSZ)PROGRAM_NAME,
  2349.       (PSZ)"TimerInterval",
  2350.       &Profile->TimerInterval,
  2351.       sizeof(Profile->TimerInterval)
  2352.     ) ;
  2353.   }
  2354.  
  2355.  /***************************************************************************
  2356.   * Save the item options.                            *
  2357.   ***************************************************************************/
  2358.  
  2359.   if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
  2360.   {
  2361.     Log ( "ERROR: Unable to lock item list to save display options.\r\n" ) ;
  2362.     DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
  2363.   }
  2364.  
  2365.   for ( int i=0; i<Profile->ItemCount; i++ )
  2366.   {
  2367.     PITEM Item = Profile->Items + i ;
  2368.  
  2369.     PrfWriteProfileData
  2370.     (
  2371.       ProfileHandle,
  2372.       (PSZ)PROGRAM_NAME,
  2373.       Item->Name,
  2374.       &Item->Flag,
  2375.       sizeof(Item->Flag)
  2376.     ) ;
  2377.   }
  2378.  
  2379.   DosReleaseMutexSem ( ItemSemaphore ) ;
  2380.  
  2381.  /***************************************************************************
  2382.   * Save the presentation parameters.                        *
  2383.   ***************************************************************************/
  2384.  
  2385.   if ( Profile->fFontNameSize )
  2386.   {
  2387.     PrfWriteProfileData
  2388.     (
  2389.       ProfileHandle,
  2390.       (PSZ)PROGRAM_NAME,
  2391.       (PSZ)"FontNameSize",
  2392.       Profile->FontNameSize,
  2393.       sizeof(Profile->FontNameSize)
  2394.     ) ;
  2395.   }
  2396.  
  2397.   if ( Profile->fBackColor )
  2398.   {
  2399.     PrfWriteProfileData
  2400.     (
  2401.       ProfileHandle,
  2402.       (PSZ)PROGRAM_NAME,
  2403.       (PSZ)"BackgroundColor",
  2404.       &Profile->BackColor,
  2405.       sizeof(Profile->BackColor)
  2406.     ) ;
  2407.   }
  2408.  
  2409.   if ( Profile->fTextColor )
  2410.   {
  2411.     PrfWriteProfileData
  2412.     (
  2413.       ProfileHandle,
  2414.       (PSZ)PROGRAM_NAME,
  2415.       (PSZ)"ForegroundColor",
  2416.       &Profile->TextColor,
  2417.       sizeof(Profile->TextColor)
  2418.     ) ;
  2419.   }
  2420. }
  2421.  
  2422. /****************************************************************************
  2423.  *                                        *
  2424.  *    Scan CONFIG.SYS for a keyword.    Return the value.            *
  2425.  *                                        *
  2426.  ****************************************************************************/
  2427.  
  2428. STATIC PSZ ScanSystemConfig ( HAB Anchor, PSZ Keyword )
  2429. {
  2430.  /***************************************************************************
  2431.   * Get the boot drive number from the global information segment.        *
  2432.   ***************************************************************************/
  2433.  
  2434.   ULONG BootDrive ;
  2435.   DosQuerySysInfo ( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &BootDrive, sizeof(BootDrive) ) ;
  2436.  
  2437.  /***************************************************************************
  2438.   * Convert the keyword to upper case.                                      *
  2439.   ***************************************************************************/
  2440.  
  2441.   WinUpper ( Anchor, NULL, NULL, Keyword ) ;
  2442.  
  2443.  /***************************************************************************
  2444.   * Build the CONFIG.SYS path.                            *
  2445.   ***************************************************************************/
  2446.  
  2447.   char Path [_MAX_PATH] ;
  2448.   Path[0] = (char) ( BootDrive + 'A' - 1 ) ;
  2449.   Path[1] = 0 ;
  2450.   strcat ( Path, ":\\CONFIG.SYS" ) ;
  2451.  
  2452.  /***************************************************************************
  2453.   * Open CONFIG.SYS for reading.                        *
  2454.   ***************************************************************************/
  2455.  
  2456.   FILE *File = fopen ( Path, "rt" ) ;
  2457.   if ( NOT File )
  2458.   {
  2459.     return ( NULL ) ;
  2460.   }
  2461.  
  2462.  /***************************************************************************
  2463.   * While there're more lines in CONFIG.SYS, read a line and check it.      *
  2464.   ***************************************************************************/
  2465.  
  2466.   static char Buffer [500] ;
  2467.   while ( fgets ( Buffer, sizeof(Buffer), File ) )
  2468.   {
  2469.  
  2470.    /*************************************************************************
  2471.     * Clean any trailing newline character from the input string.        *
  2472.     *************************************************************************/
  2473.  
  2474.     if ( Buffer[strlen(Buffer)-1] == '\n' )
  2475.     {
  2476.       Buffer[strlen(Buffer)-1] = 0 ;
  2477.     }
  2478.  
  2479.    /*************************************************************************
  2480.     * If keyword starts the line, we've found the line we want.  Close      *
  2481.     *    the file and return a pointer to the parameter text.            *
  2482.     *************************************************************************/
  2483.  
  2484.     WinUpper ( Anchor, NULL, NULL, (PSZ)Buffer ) ;
  2485.  
  2486.     if ( NOT strncmp ( Buffer, (PCHAR)Keyword, strlen((PCHAR)Keyword) )
  2487.       AND ( Buffer[strlen((PCHAR)Keyword)] == '=' ) )
  2488.     {
  2489.       fclose ( File ) ;
  2490.       return ( (PSZ) ( Buffer + strlen((PCHAR)Keyword) + 1 ) ) ;
  2491.     }
  2492.   }
  2493.  
  2494.  /***************************************************************************
  2495.   * Close the file.  We haven't found the line we wanted.                   *
  2496.   ***************************************************************************/
  2497.  
  2498.   fclose ( File ) ;
  2499.  
  2500.   return ( NULL ) ;
  2501. }
  2502.  
  2503. /****************************************************************************
  2504.  *                                        *
  2505.  *             Resize Client Window                    *
  2506.  *                                        *
  2507.  ****************************************************************************/
  2508.  
  2509. STATIC void ResizeWindow ( HWND hwnd, PPROFILE Profile )
  2510. {
  2511.  /***************************************************************************
  2512.   * If the window is visible and minimized, restore it invisibly.        *
  2513.   ***************************************************************************/
  2514.  
  2515.   HWND hwndFrame = WinQueryWindow ( hwnd, QW_PARENT ) ;
  2516.  
  2517.   SHORT fHadToHide = FALSE ;
  2518.   SHORT fHadToRestore = FALSE ;
  2519.   if ( Profile->Position.fl & SWP_MINIMIZE )
  2520.   {
  2521.     if ( WinIsWindowVisible ( hwndFrame ) )
  2522.     {
  2523.       WinShowWindow ( hwndFrame, FALSE ) ;
  2524.       fHadToHide = TRUE ;
  2525.     }
  2526.     WinSetWindowPos ( hwndFrame, NULL, 0, 0, 0, 0, SWP_RESTORE ) ;
  2527.     fHadToRestore = TRUE ;
  2528.   }
  2529.  
  2530.  /***************************************************************************
  2531.   * Determine how many items are to be displayed.                *
  2532.   ***************************************************************************/
  2533.  
  2534.   if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
  2535.   {
  2536.     Log ( "ERROR: Unable to lock item list to determine window size.\r\n" ) ;
  2537.     DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
  2538.   }
  2539.  
  2540.   HPS hPS = WinGetPS ( hwnd ) ;
  2541.  
  2542.   int Count = 0 ;
  2543.   LONG Widest = 0 ;
  2544.   LONG Height = 0 ;
  2545.  
  2546.   for ( int i=0; i<Profile->ItemCount; i++ )
  2547.   {
  2548.     PITEM Item = Profile->Items + i ;
  2549.  
  2550.     if ( Item->Flag )
  2551.     {
  2552.       Count ++ ;
  2553.  
  2554.       BYTE Text [100] ;
  2555.       sprintf ( (PCHAR)Text, "%s 1,234,567K", Item->Label ) ;
  2556.  
  2557.       RECTL Rectangle ;
  2558.       WinQueryWindowRect ( HWND_DESKTOP, &Rectangle ) ;
  2559.  
  2560.       WinDrawText ( hPS, strlen((PCHAR)Text), Text,
  2561.     &Rectangle, 0L, 0L, DT_LEFT | DT_BOTTOM | DT_QUERYEXTENT ) ;
  2562.  
  2563.       Widest = max ( Widest, (Rectangle.xRight-Rectangle.xLeft+1) ) ;
  2564.  
  2565.       Height += Rectangle.yTop - Rectangle.yBottom ;
  2566.     }
  2567.   }
  2568.  
  2569.   WinReleasePS ( hPS ) ;
  2570.  
  2571.   DosReleaseMutexSem ( ItemSemaphore ) ;
  2572.  
  2573.  /***************************************************************************
  2574.   * Get the window's current size & position.                               *
  2575.   ***************************************************************************/
  2576.  
  2577.   RECTL Rectangle ;
  2578.   WinQueryWindowRect ( hwndFrame, &Rectangle ) ;
  2579.  
  2580.   WinCalcFrameRect ( hwndFrame, &Rectangle, TRUE ) ;
  2581.  
  2582.  /***************************************************************************
  2583.   * Adjust the window's width & height.                                     *
  2584.   ***************************************************************************/
  2585.  
  2586.   Rectangle.xRight  = Rectangle.xLeft + Widest ;
  2587.  
  2588.   Rectangle.yTop    = Rectangle.yBottom + Height ;
  2589.  
  2590.  /***************************************************************************
  2591.   * Compute new frame size and apply it.                    *
  2592.   ***************************************************************************/
  2593.  
  2594.   WinCalcFrameRect ( hwndFrame, &Rectangle, FALSE ) ;
  2595.  
  2596.   WinSetWindowPos ( hwndFrame, NULL, 0, 0,
  2597.     (SHORT) (Rectangle.xRight-Rectangle.xLeft),
  2598.     (SHORT) (Rectangle.yTop-Rectangle.yBottom),
  2599.     SWP_SIZE ) ;
  2600.  
  2601.  /***************************************************************************
  2602.   * Return the window to its original state.                    *
  2603.   ***************************************************************************/
  2604.  
  2605.   if ( fHadToRestore )
  2606.   {
  2607.     WinSetWindowPos ( hwndFrame, NULL,
  2608.       Profile->Position.x, Profile->Position.y,
  2609.       Profile->Position.cx, Profile->Position.cy,
  2610.       SWP_MOVE | SWP_SIZE | SWP_MINIMIZE ) ;
  2611.   }
  2612.  
  2613.   if ( fHadToHide )
  2614.   {
  2615.     WinShowWindow ( hwndFrame, TRUE ) ;
  2616.   }
  2617.  
  2618.  /***************************************************************************
  2619.   * Invalidate the window so that it gets repainted.                *
  2620.   ***************************************************************************/
  2621.  
  2622.   WinInvalidateRect ( hwnd, NULL, TRUE ) ;
  2623. }
  2624.  
  2625. /****************************************************************************
  2626.  *                                        *
  2627.  *            Hide Window Controls                    *
  2628.  *                                        *
  2629.  ****************************************************************************/
  2630.  
  2631. STATIC void HideControls
  2632. (
  2633.   BOOL fHide,
  2634.   HWND hwndFrame,
  2635.   HWND hwndSysMenu,
  2636.   HWND hwndTitleBar,
  2637.   HWND hwndMinMax
  2638. )
  2639. {
  2640.  /***************************************************************************
  2641.   * Get original window position and state.                    *
  2642.   ***************************************************************************/
  2643.  
  2644.   SWP OldPosition ;
  2645.   WinQueryWindowPos ( hwndFrame, &OldPosition ) ;
  2646.  
  2647.   BOOL WasVisible = WinIsWindowVisible ( hwndFrame ) ;
  2648.  
  2649.  /***************************************************************************
  2650.   * Restore and hide the window.                        *
  2651.   ***************************************************************************/
  2652.  
  2653.   WinSetWindowPos ( hwndFrame, NULL, 0, 0, 0, 0, SWP_RESTORE | SWP_HIDE ) ;
  2654.  
  2655.  /***************************************************************************
  2656.   * Determine client window and location.                    *
  2657.   ***************************************************************************/
  2658.  
  2659.   SWP Position ;
  2660.   WinQueryWindowPos ( hwndFrame, &Position ) ;
  2661.  
  2662.   RECTL Rectangle ;
  2663.   Rectangle.xLeft   = Position.x ;
  2664.   Rectangle.xRight  = Position.x + Position.cx ;
  2665.   Rectangle.yBottom = Position.y ;
  2666.   Rectangle.yTop    = Position.y + Position.cy ;
  2667.  
  2668.   WinCalcFrameRect ( hwndFrame, &Rectangle, TRUE ) ;
  2669.  
  2670.  /***************************************************************************
  2671.   * Hide or reveal the controls windows by changing their parentage.        *
  2672.   ***************************************************************************/
  2673.  
  2674.   if ( fHide )
  2675.   {
  2676.     WinSetParent ( hwndSysMenu,  HWND_OBJECT, FALSE ) ;
  2677.     WinSetParent ( hwndTitleBar, HWND_OBJECT, FALSE ) ;
  2678.     WinSetParent ( hwndMinMax,     HWND_OBJECT, FALSE ) ;
  2679.   }
  2680.   else
  2681.   {
  2682.     WinSetParent ( hwndSysMenu,  hwndFrame, TRUE ) ;
  2683.     WinSetParent ( hwndTitleBar, hwndFrame, TRUE ) ;
  2684.     WinSetParent ( hwndMinMax,     hwndFrame, TRUE ) ;
  2685.   }
  2686.  
  2687.  /***************************************************************************
  2688.   * Tell the frame that things have changed.  Let it update the window.     *
  2689.   ***************************************************************************/
  2690.  
  2691.   WinSendMsg ( hwndFrame, WM_UPDATEFRAME,
  2692.     MPFROMSHORT ( FCF_TITLEBAR | FCF_SYSMENU | FCF_MINBUTTON ), 0L ) ;
  2693.  
  2694.  /***************************************************************************
  2695.   * Reposition the frame around the client window, which is left be.        *
  2696.   ***************************************************************************/
  2697.  
  2698.   WinCalcFrameRect ( hwndFrame, &Rectangle, FALSE ) ;
  2699.  
  2700.   WinSetWindowPos ( hwndFrame, NULL,
  2701.     (SHORT) Rectangle.xLeft,  (SHORT) Rectangle.yBottom,
  2702.     (SHORT) (Rectangle.xRight-Rectangle.xLeft),
  2703.     (SHORT) (Rectangle.yTop-Rectangle.yBottom),
  2704.     SWP_SIZE | SWP_MOVE ) ;
  2705.  
  2706.  /***************************************************************************
  2707.   * If window was maximized, put it back that way.                *
  2708.   ***************************************************************************/
  2709.  
  2710.   if ( OldPosition.fl & SWP_MAXIMIZE )
  2711.   {
  2712.     WinSetWindowPos ( hwndFrame, NULL,
  2713.       (SHORT) Rectangle.xLeft,    (SHORT) Rectangle.yBottom,
  2714.       (SHORT) (Rectangle.xRight-Rectangle.xLeft),
  2715.       (SHORT) (Rectangle.yTop-Rectangle.yBottom),
  2716.       SWP_SIZE | SWP_MOVE |
  2717.       ( OldPosition.fl & SWP_MAXIMIZE ) ) ;
  2718.   }
  2719.  
  2720.  /***************************************************************************
  2721.   * If the window was visible in the first place, show it.            *
  2722.   ***************************************************************************/
  2723.  
  2724.   if ( WasVisible )
  2725.   {
  2726.     WinShowWindow ( hwndFrame, TRUE ) ;
  2727.   }
  2728. }
  2729.  
  2730. /****************************************************************************
  2731.  *                                        *
  2732.  *    Update Window                                *
  2733.  *                                        *
  2734.  ****************************************************************************/
  2735.  
  2736. STATIC void UpdateWindow ( HWND hwnd, PDATA Data, BOOL All )
  2737. {
  2738.  /***************************************************************************
  2739.   * Lock the item list.                             *
  2740.   ***************************************************************************/
  2741.  
  2742.   if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
  2743.   {
  2744.     Log ( "ERROR: Unable to lock item list to update window contents.\r\n" ) ;
  2745.     DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
  2746.   }
  2747.  
  2748.  /***************************************************************************
  2749.   * Determine how many items are to be displayed.                *
  2750.   ***************************************************************************/
  2751.  
  2752.   int Count = 0 ;
  2753.   for ( int i=0; i<Data->Profile.ItemCount; i++ )
  2754.   {
  2755.     if ( Data->Profile.Items[i].Flag )
  2756.     {
  2757.       Count ++ ;
  2758.     }
  2759.   }
  2760.  
  2761.  /***************************************************************************
  2762.   * Get presentation space and make it use RGB colors.                *
  2763.   ***************************************************************************/
  2764.  
  2765.   HPS hPS = WinGetPS ( hwnd ) ;
  2766.   GpiCreateLogColorTable ( hPS, LCOL_RESET, LCOLF_RGB, 0L, 0L, NULL ) ;
  2767.  
  2768.  /***************************************************************************
  2769.   * Get the window's size and determine the initial position.               *
  2770.   ***************************************************************************/
  2771.  
  2772.   RECTL Rectangle ;
  2773.   WinQueryWindowRect ( hwnd, &Rectangle ) ;
  2774.  
  2775.   Rectangle.xLeft += Data->Width / 2 ;
  2776.   Rectangle.xRight -= Data->Width / 2 ;
  2777.  
  2778.   Rectangle.yBottom = Data->Height * ( Count - 1 ) ;
  2779.   Rectangle.yTop = Rectangle.yBottom + Data->Height ;
  2780.  
  2781.  /***************************************************************************
  2782.   * Review all items.  Display those changed, or all.                *
  2783.   ***************************************************************************/
  2784.  
  2785.   for ( i=0; i<Data->Profile.ItemCount; i++ )
  2786.   {
  2787.     ULONG NewValue ;
  2788.  
  2789.     PITEM Item = Data->Profile.Items + i ;
  2790.  
  2791.     if ( Item->Flag )
  2792.     {
  2793.       if ( NOT Item->Error )
  2794.       {
  2795.     NewValue = Item->NewValue ( Data, Item->Parm ) ;
  2796.       }
  2797.       else
  2798.       {
  2799.     NewValue = DRIVE_ERROR ;
  2800.       }
  2801.  
  2802.       if ( All OR ( NewValue != Item->Value ) )
  2803.       {
  2804.         BYTE Text [100] ;
  2805.     switch ( i )
  2806.     {
  2807.       case ITEM_CLOCK:
  2808.       {
  2809.         ULONG Month  = ( NewValue % 100000000L ) / 1000000L ;
  2810.         ULONG Day     = ( NewValue % 1000000L ) / 10000L ;
  2811.         ULONG Hour     = ( NewValue % 10000L ) / 100L ;
  2812.         ULONG Minute = ( NewValue % 100L ) ;
  2813.  
  2814.         switch ( Data->CountryInfo.fsDateFmt )
  2815.         {
  2816.           case DATEFMT_DD_MM_YY:
  2817.         sprintf ( (PCHAR)Text, "%02lu%s%02lu ",
  2818.           Day, Data->CountryInfo.szDateSeparator, Month ) ;
  2819.         break ;
  2820.  
  2821.           case DATEFMT_YY_MM_DD:
  2822.           case DATEFMT_MM_DD_YY:
  2823.           default:
  2824.         sprintf ( (PCHAR)Text, "%02lu%s%02lu ",
  2825.           Month, Data->CountryInfo.szDateSeparator, Day ) ;
  2826.         break ;
  2827.         }
  2828.  
  2829.         if ( Data->CountryInfo.fsTimeFmt )
  2830.         {
  2831.           sprintf ( (PCHAR)(Text+strlen((PCHAR)Text)), "%02lu%s%02lu",
  2832.         Hour,
  2833.         Data->CountryInfo.szTimeSeparator,
  2834.         Minute ) ;
  2835.         }
  2836.         else
  2837.         {
  2838.           PCHAR AmPm ;
  2839.  
  2840.           if ( Hour )
  2841.           {
  2842.         if ( Hour < 12 )
  2843.         {
  2844.           AmPm = "a" ;
  2845.         }
  2846.         else if ( Hour == 12 )
  2847.         {
  2848.           if ( Minute )
  2849.             AmPm = "p" ;
  2850.           else
  2851.             AmPm = "a" ;
  2852.         }
  2853.         else if ( Hour > 12 )
  2854.         {
  2855.           Hour -= 12 ;
  2856.           AmPm = "p" ;
  2857.         }
  2858.           }
  2859.           else
  2860.           {
  2861.         Hour = 12 ;
  2862.         if ( Minute )
  2863.           AmPm = "a" ;
  2864.         else
  2865.           AmPm = "p" ;
  2866.           }
  2867.           sprintf ( (PCHAR)(Text+strlen((PCHAR)Text)), "%02lu%s%02lu%s",
  2868.         Hour, Data->CountryInfo.szTimeSeparator, Minute, AmPm ) ;
  2869.         }
  2870.         break ;
  2871.       }
  2872.  
  2873.       case ITEM_ELAPSEDTIME:
  2874.       {
  2875.         memset ( Text, 0, sizeof(Text) ) ;
  2876.  
  2877.         ULONG Days = NewValue / ( 60L * 24L ) ;
  2878.  
  2879.         if ( Days )
  2880.         {
  2881.           sprintf ( PCHAR(Text), "%lu %s, ",
  2882.         Days, Days > 1 ? Data->Days : Data->Day ) ;
  2883.         }
  2884.  
  2885.         ULONG Minutes = NewValue % ( 60L * 24L ) ;
  2886.  
  2887.         sprintf ( PCHAR(Text+strlen(PCHAR(Text))), "%lu%s%02lu",
  2888.           Minutes/60, Data->CountryInfo.szTimeSeparator, Minutes%60 ) ;
  2889.  
  2890.         break ;
  2891.       }
  2892.  
  2893.       default:
  2894.       {
  2895.         memset ( Text, 0, sizeof(Text) ) ;
  2896.  
  2897.         if ( NewValue == DRIVE_ERROR )
  2898.         {
  2899.           Item->Error = TRUE ;
  2900.           strcpy ( PCHAR(Text), PCHAR(Data->DriveError) ) ;
  2901.         }
  2902.         else
  2903.         {
  2904.           if ( Item->Divisor )
  2905.           {
  2906.         if ( NewValue < ( Item->Divisor * 1024 ) / 2 )
  2907.           sprintf ( (PCHAR)Text, "%lu", NewValue ) ;
  2908.         else
  2909.           sprintf ( (PCHAR)Text, "%lu", (NewValue+Item->Divisor/2)/Item->Divisor ) ;
  2910.           }
  2911.           else
  2912.           {
  2913.         sprintf ( (PCHAR)Text, "%lu", NewValue ) ;
  2914.           }
  2915.  
  2916.           {
  2917.         PBYTE p1, p2 ;
  2918.         BYTE Work[100] ;
  2919.  
  2920.         p1 = Text ;
  2921.         p2 = Work ;
  2922.         while ( *p1 )
  2923.         {
  2924.           *p2 = *p1 ;
  2925.           p1 ++ ;
  2926.           p2 ++ ;
  2927.           if ( *p1 )
  2928.           {
  2929.             if ( strlen((PCHAR)p1) % 3 == 0 )
  2930.             {
  2931.               *p2 = Data->CountryInfo.szThousandsSeparator [0] ;
  2932.               p2 ++ ;
  2933.             }
  2934.           }
  2935.         }
  2936.         *p2 = 0 ;
  2937.         strcpy ( (PCHAR)Text, (PCHAR)Work ) ;
  2938.           }
  2939.  
  2940.           if ( Item->Divisor )
  2941.           {
  2942.         if ( NewValue < ( Item->Divisor * 1024 ) / 2 )
  2943.           Text[strlen((PCHAR)Text)] = ' ' ;
  2944.         else
  2945.           Text[strlen((PCHAR)Text)] = Item->Suffix ;
  2946.           }
  2947.           else
  2948.           {
  2949.         Text[strlen((PCHAR)Text)] = Item->Suffix ;
  2950.           }
  2951.         }
  2952.       }
  2953.     }
  2954.  
  2955.     WinDrawText ( hPS, strlen((PCHAR)Text), Text, &Rectangle,
  2956.       Data->Profile.TextColor, Data->Profile.BackColor,
  2957.       DT_RIGHT | DT_BOTTOM | DT_ERASERECT ) ;
  2958.  
  2959.     WinDrawText ( hPS, strlen((PCHAR)Item->Label), Item->Label, &Rectangle,
  2960.       Data->Profile.TextColor, Data->Profile.BackColor,
  2961.       DT_LEFT | DT_BOTTOM ) ;
  2962.  
  2963.     Item->Value = NewValue ;
  2964.       }
  2965.       Rectangle.yBottom -= Data->Height ;
  2966.       Rectangle.yTop    -= Data->Height ;
  2967.     }
  2968.   }
  2969.  
  2970.  /***************************************************************************
  2971.   * Release the presentation space and return.                    *
  2972.   ***************************************************************************/
  2973.  
  2974.   WinReleasePS ( hPS ) ;
  2975.  
  2976.  /***************************************************************************
  2977.   * Release the item list.                            *
  2978.   ***************************************************************************/
  2979.  
  2980.   DosReleaseMutexSem ( ItemSemaphore ) ;
  2981. }
  2982.  
  2983.  
  2984. /****************************************************************************
  2985.  *                                        *
  2986.  *    Compute Time                                *
  2987.  *                                        *
  2988.  ****************************************************************************/
  2989.  
  2990. STATIC ULONG ComputeTime ( PVOID Data, USHORT Dummy )
  2991. {
  2992.   DATETIME DateTime ;
  2993.   DosGetDateTime ( &DateTime ) ;
  2994.  
  2995.   ULONG Time ;
  2996.   Time = DateTime.month ;
  2997.   Time *= 100 ;
  2998.   Time += DateTime.day ;
  2999.   Time *= 100 ;
  3000.   Time += DateTime.hours ;
  3001.   Time *= 100 ;
  3002.   Time += DateTime.minutes ;
  3003.  
  3004.   return ( Time ) ;
  3005. }
  3006.  
  3007. /****************************************************************************
  3008.  *                                        *
  3009.  *    Compute Elapsed Time                            *
  3010.  *                                        *
  3011.  ****************************************************************************/
  3012.  
  3013. STATIC ULONG ComputeElapsed ( PVOID Data, USHORT Dummy )
  3014. {
  3015.   ULONG Milliseconds ;
  3016.   DosQuerySysInfo ( QSV_MS_COUNT, QSV_MS_COUNT, &Milliseconds, sizeof(Milliseconds) ) ;
  3017.   return ( Milliseconds / 60000L ) ;
  3018. }
  3019.  
  3020. /****************************************************************************
  3021.  *                                        *
  3022.  *    Compute Available Memory                            *
  3023.  *                                        *
  3024.  ****************************************************************************/
  3025.  
  3026. STATIC ULONG ComputeFreeMemory ( PVOID Data, USHORT Dummy )
  3027. {
  3028.   ULONG VirtualMemory ;
  3029.   DosQuerySysInfo ( QSV_TOTAVAILMEM, QSV_TOTAVAILMEM, &VirtualMemory, sizeof(VirtualMemory) ) ;
  3030.  
  3031.   ULONG SwapFree = ComputeSwapFree ( Data, Dummy ) ;
  3032.  
  3033.   LONG Space = LONG(VirtualMemory) - LONG(SwapFree) ;
  3034.   while ( Space < 0 )
  3035.   {
  3036.     Space += 0x100000 ;
  3037.   }
  3038.  
  3039.   return ( ULONG(Space) ) ;
  3040. }
  3041.  
  3042. /****************************************************************************
  3043.  *                                        *
  3044.  *    Compute Swap-File Size                            *
  3045.  *                                        *
  3046.  ****************************************************************************/
  3047.  
  3048. STATIC ULONG ComputeSwapSize ( PVOID Data, USHORT Dummy )
  3049. {
  3050.  /***************************************************************************
  3051.   * Find the swap file.                                                 *
  3052.   ***************************************************************************/
  3053.  
  3054.   char Path [_MAX_PATH+1] ;
  3055.   strcpy ( Path, (PCHAR)((PDATA)Data)->SwapPath ) ;
  3056.  
  3057.   if ( Path[strlen(Path)-1] != '\\' )
  3058.   {
  3059.     strcat ( Path, "\\" ) ;
  3060.   }
  3061.  
  3062.   strcat ( Path, "SWAPPER.DAT" ) ;
  3063.  
  3064.  /***************************************************************************
  3065.   * Determine its size.                                           *
  3066.   ***************************************************************************/
  3067.  
  3068.   ULONG SwapSize = 0 ;
  3069.   FILESTATUS3 Status ;
  3070.   if ( DosQueryPathInfo ( (PSZ)Path, FIL_STANDARD, &Status, sizeof(Status) ) == 0 )
  3071.   {
  3072.     SwapSize = Status.cbFileAlloc ;
  3073.   }
  3074.  
  3075.   return ( SwapSize ) ;
  3076. }
  3077.  
  3078. /****************************************************************************
  3079.  *                                        *
  3080.  *    Compute Available Swap Space                        *
  3081.  *                                        *
  3082.  ****************************************************************************/
  3083.  
  3084. STATIC ULONG ComputeSwapFree ( PVOID Data, USHORT Dummy )
  3085. {
  3086.  /***************************************************************************
  3087.   * Find the swap file and find its size.                    *
  3088.   ***************************************************************************/
  3089.  
  3090.   char Path [_MAX_PATH+1] ;
  3091.   strcpy ( Path, (PCHAR)((PDATA)Data)->SwapPath ) ;
  3092.   strcat ( Path, "\\SWAPPER.DAT" ) ;
  3093.  
  3094.  /***************************************************************************
  3095.   * Compute swap device free space.                        *
  3096.   ***************************************************************************/
  3097.  
  3098.   ULONG SwapFree = 0 ;
  3099.   if ( Path[0] )
  3100.   {
  3101.     DosError ( FERR_DISABLEHARDERR ) ;
  3102.     FSALLOCATE Allocation ;
  3103.     DosQueryFSInfo ( Path[0]-'A'+1, FSIL_ALLOC,
  3104.       (PBYTE)&Allocation, sizeof(Allocation) ) ;
  3105.     DosError ( FERR_ENABLEHARDERR ) ;
  3106.  
  3107.     SwapFree = Allocation.cUnitAvail*Allocation.cSectorUnit*Allocation.cbSector ;
  3108.   }
  3109.  
  3110.  /***************************************************************************
  3111.   * Return swap device's free space, less the minimum free space.           *
  3112.   ***************************************************************************/
  3113.  
  3114.   if ( SwapFree < ULONG(PDATA(Data)->MinFree*1024L) )
  3115.     return ( 0L ) ;
  3116.   else
  3117.     return ( SwapFree - ULONG(PDATA(Data)->MinFree*1024L) ) ;
  3118. }
  3119.  
  3120. /****************************************************************************
  3121.  *                                        *
  3122.  *    Compute Spool-file Size                            *
  3123.  *                                        *
  3124.  ****************************************************************************/
  3125.  
  3126. STATIC ULONG ComputeSpoolSize ( PVOID Data, USHORT Dummy )
  3127. {
  3128.  /***************************************************************************
  3129.   * Build file specifier for the spool directory.                *
  3130.   ***************************************************************************/
  3131.  
  3132.   ULONG PathSize ;
  3133.   DosQuerySysInfo ( QSV_MAX_PATH_LENGTH, QSV_MAX_PATH_LENGTH, &PathSize, sizeof(PathSize) ) ;
  3134.  
  3135.   PBYTE Path = malloc ( PathSize ) ;
  3136.   if ( Path == NULL )
  3137.   {
  3138.     Log ( "ERROR: Unable to allocate memory for spool-file search path.\r\n" ) ;
  3139.     return ( 0 ) ;
  3140.   }
  3141.  
  3142.   PFILEFINDBUF3 Found = malloc ( PathSize + sizeof(FILEFINDBUF3) ) ;
  3143.   if ( Found == NULL )
  3144.   {
  3145.     Log ( "ERROR: Unable to allocate memory for spool-file search result structure.\r\n" ) ;
  3146.     free ( Path ) ;
  3147.     return ( 0 ) ;
  3148.   }
  3149.  
  3150.   strcpy ( (PCHAR)Path, (PCHAR)((PDATA)Data)->SpoolPath ) ;
  3151.   strcat ( (PCHAR)Path, "\\*.*" ) ;
  3152.  
  3153.  /***************************************************************************
  3154.   * If there are any files/directories in the spool directory . . .        *
  3155.   ***************************************************************************/
  3156.  
  3157.   HDIR hDir = (HDIR) HDIR_CREATE ;
  3158.   ULONG Count = 1 ;
  3159.   ULONG TotalSize = 0 ;
  3160.  
  3161.   if ( !DosFindFirst2 ( Path, &hDir,
  3162.     FILE_NORMAL | FILE_READONLY | FILE_DIRECTORY | FILE_ARCHIVED,
  3163.     Found, PathSize+sizeof(FILEFINDBUF3), &Count, FIL_STANDARD ) )
  3164.   {
  3165.  
  3166.    /*************************************************************************
  3167.     * Loop through every entry in the spool directory.                *
  3168.     *************************************************************************/
  3169.  
  3170.     do
  3171.     {
  3172.  
  3173.      /***********************************************************************
  3174.       * Ignore the parent and current directory entries.            *
  3175.       ***********************************************************************/
  3176.  
  3177.       if ( !strcmp ( (PCHAR)Found->achName, "." )
  3178.     OR !strcmp ( (PCHAR)Found->achName, ".." ) )
  3179.       {
  3180.     continue ;
  3181.       }
  3182.  
  3183.      /***********************************************************************
  3184.       * If the entry is a subdirectory . . .                    *
  3185.       ***********************************************************************/
  3186.  
  3187.       if ( Found->attrFile & FILE_DIRECTORY )
  3188.       {
  3189.  
  3190.        /*********************************************************************
  3191.     * Scan the subdirectory and add every file's size to the total.     *
  3192.     *********************************************************************/
  3193.  
  3194.     HDIR hDir = (HDIR) HDIR_CREATE ;
  3195.  
  3196.     strcpy ( (PCHAR)Path, (PCHAR)((PDATA)Data)->SpoolPath ) ;
  3197.     strcat ( (PCHAR)Path, "\\" ) ;
  3198.     strcat ( (PCHAR)Path, (PCHAR)Found->achName ) ;
  3199.     strcat ( (PCHAR)Path, "\\*.*" ) ;
  3200.  
  3201.     Count = 1 ;
  3202.     if ( !DosFindFirst2 ( Path, &hDir,
  3203.       FILE_NORMAL | FILE_READONLY | FILE_ARCHIVED,
  3204.       Found, PathSize+sizeof(FILEFINDBUF3), &Count, FIL_STANDARD ) )
  3205.     {
  3206.       do
  3207.       {
  3208.           TotalSize += Found->cbFileAlloc ;
  3209.       }
  3210.       while ( !DosFindNext ( hDir, Found, PathSize+sizeof(FILEFINDBUF3), &Count ) ) ;
  3211.       DosFindClose ( hDir ) ;
  3212.     }
  3213.  
  3214.     Count = 1 ;
  3215.       }
  3216.  
  3217.      /***********************************************************************
  3218.       * Else if it was a file, add its size to the total.            *
  3219.       ***********************************************************************/
  3220.  
  3221.       else
  3222.       {
  3223.     TotalSize += Found->cbFileAlloc ;
  3224.       }
  3225.     }
  3226.     while ( !DosFindNext ( hDir, Found, PathSize+sizeof(FILEFINDBUF3), &Count ) ) ;
  3227.  
  3228.    /*************************************************************************
  3229.     * Close the directory scan.                         *
  3230.     *************************************************************************/
  3231.  
  3232.     DosFindClose ( hDir ) ;
  3233.   }
  3234.  
  3235.   free ( Path ) ;
  3236.   free ( Found ) ;
  3237.  
  3238.   return ( TotalSize ) ;
  3239. }
  3240.  
  3241. /****************************************************************************
  3242.  *                                        *
  3243.  *    Compute CPU Load                                *
  3244.  *                                        *
  3245.  ****************************************************************************/
  3246.  
  3247. STATIC ULONG ComputeCpuLoad ( PVOID Data, USHORT Dummy )
  3248. {
  3249.   ((PDATA)Data)->MaxCount = (ULONG) max ( ((PDATA)Data)->MaxCount, ((PDATA)Data)->IdleCount ) ;
  3250.  
  3251.   ULONG Load = ( ( ((PDATA)Data)->MaxCount - ((PDATA)Data)->IdleCount ) * 100 ) / ((PDATA)Data)->MaxCount ;
  3252.  
  3253.   return ( Load ) ;
  3254. }
  3255.  
  3256. /****************************************************************************
  3257.  *                                        *
  3258.  *    Compute Active Task Count                         *
  3259.  *                                        *
  3260.  ****************************************************************************/
  3261.  
  3262. STATIC ULONG ComputeTaskCount ( PVOID Data, USHORT Dummy )
  3263. {
  3264.   return ( WinQuerySwitchList ( ((PDATA)Data)->Anchor, NULL, 0 ) ) ;
  3265. }
  3266.  
  3267. /****************************************************************************
  3268.  *                                        *
  3269.  *    Compute Drive Free Space                            *
  3270.  *                                        *
  3271.  ****************************************************************************/
  3272.  
  3273. STATIC ULONG ComputeDriveFree ( PVOID Data, USHORT Drive )
  3274. {
  3275.   DosError ( FERR_DISABLEHARDERR ) ;
  3276.   FSALLOCATE Allocation ;
  3277.   USHORT Status = DosQueryFSInfo ( Drive, FSIL_ALLOC, (PBYTE)&Allocation, sizeof(Allocation) ) ;
  3278.   DosError ( FERR_ENABLEHARDERR ) ;
  3279.  
  3280.   if ( Status )
  3281.   {
  3282.     return ( DRIVE_ERROR ) ;
  3283.   }
  3284.  
  3285.   DosError ( FERR_ENABLEHARDERR ) ;
  3286.   return ( Allocation.cUnitAvail*Allocation.cSectorUnit*Allocation.cbSector ) ;
  3287. }
  3288.  
  3289. /****************************************************************************
  3290.  *                                        *
  3291.  *    Monitor Loop Thread                            *
  3292.  *                                        *
  3293.  ****************************************************************************/
  3294.  
  3295. STATIC VOID MonitorLoopThread ( PMONITOR_PARMS Parms )
  3296. {
  3297.  /***************************************************************************
  3298.   * Set this thread's priority as high as it can go.                        *
  3299.   ***************************************************************************/
  3300.  
  3301.   DosSetPrty ( PRTYS_THREAD, PRTYC_TIMECRITICAL, PRTYD_MAXIMUM, 0 ) ;
  3302.  
  3303.  /***************************************************************************
  3304.   * Start up the high resolution timer, if it is available.            *
  3305.   ***************************************************************************/
  3306.  
  3307.   BOOL HiResTimer = OpenTimer ( ) ;
  3308.  
  3309.  /***************************************************************************
  3310.   * Loop forever . . .                                *
  3311.   ***************************************************************************/
  3312.  
  3313.   while ( 1 )
  3314.   {
  3315.  
  3316.    /*************************************************************************
  3317.     * Reset the last time and count seen.                    *
  3318.     *************************************************************************/
  3319.  
  3320.     ULONG LastMilliseconds ;
  3321.     TIMESTAMP Time [2] ;
  3322.  
  3323.     if ( HiResTimer )
  3324.       GetTime ( &Time[0] ) ;
  3325.     else
  3326.       DosQuerySysInfo ( QSV_MS_COUNT, QSV_MS_COUNT, &LastMilliseconds, sizeof(LastMilliseconds) ) ;
  3327.  
  3328.     ULONG LastCounter = *Parms->Counter ;
  3329.  
  3330.    /*************************************************************************
  3331.     * Sleep for a bit.                                *
  3332.     *************************************************************************/
  3333.  
  3334.     DosSleep ( *Parms->Interval ) ;
  3335.  
  3336.    /*************************************************************************
  3337.     * Find out how much time and counts went by.                *
  3338.     *************************************************************************/
  3339.  
  3340.     ULONG CurrentCounter = *Parms->Counter ;
  3341.  
  3342.     ULONG DeltaMilliseconds ;
  3343.  
  3344.     if ( HiResTimer )
  3345.     {
  3346.       GetTime ( &Time[1] ) ;
  3347.  
  3348.       ULONG Nanoseconds ;
  3349.       DeltaMilliseconds = ElapsedTime ( &Time[0], &Time[1], &Nanoseconds ) ;
  3350.  
  3351.       if ( Nanoseconds >= 500000L )
  3352.     DeltaMilliseconds ++ ;
  3353.     }
  3354.     else
  3355.     {
  3356.       ULONG Milliseconds ;
  3357.       DosQuerySysInfo ( QSV_MS_COUNT, QSV_MS_COUNT, &Milliseconds, sizeof(Milliseconds) ) ;
  3358.       DeltaMilliseconds = Milliseconds - LastMilliseconds ;
  3359.     }
  3360.  
  3361.    /*************************************************************************
  3362.     * Find out how much idle time was counted.    Adjust it to persecond.     *
  3363.     *************************************************************************/
  3364.  
  3365.     ULONG Counter = (ULONG) ( ( (double)(CurrentCounter-LastCounter) * 1000L ) / (double)DeltaMilliseconds ) ;
  3366.  
  3367.    /*************************************************************************
  3368.     * Tell the owner window to refresh its statistics.                *
  3369.     *************************************************************************/
  3370.  
  3371.     WinPostMsg ( Parms->Owner, WM_REFRESH, MPFROMLONG(Counter), 0L ) ;
  3372.   }
  3373. }
  3374.  
  3375. /****************************************************************************
  3376.  *                                        *
  3377.  *    Update the Item List to reflect changes in the available drives.    *
  3378.  *                                        *
  3379.  ****************************************************************************/
  3380.  
  3381. STATIC VOID UpdateDriveList
  3382. (
  3383.   HAB Anchor,
  3384.   HMODULE Library,
  3385.   HINI ProfileHandle,
  3386.   PPROFILE Profile,
  3387.   ULONG OldDrives,
  3388.   ULONG NewDrives
  3389. )
  3390. {
  3391.  /***************************************************************************
  3392.   * Lock the item list.                             *
  3393.   ***************************************************************************/
  3394.  
  3395.   if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
  3396.   {
  3397.     Log ( "ERROR: Unable to lock item list to update the drive list.\r\n" ) ;
  3398.     DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
  3399.   }
  3400.  
  3401.  /***************************************************************************
  3402.   * Get format strings.                             *
  3403.   ***************************************************************************/
  3404.  
  3405.   BYTE LabelFormat [80] ;
  3406.   WinLoadString ( Anchor, Library, IDS_SHOW_DRIVE_FREE_LABEL, sizeof(LabelFormat), LabelFormat ) ;
  3407.  
  3408.   BYTE OptionFormat [80] ;
  3409.   WinLoadString ( Anchor, Library, IDS_SHOW_DRIVE_FREE_OPTION, sizeof(OptionFormat), OptionFormat ) ;
  3410.  
  3411.  /***************************************************************************
  3412.   * Save the old item list for comparison.                    *
  3413.   ***************************************************************************/
  3414.  
  3415.   ITEM OldItems [ ITEM_BASE_COUNT + MAX_DRIVES ] ;
  3416.  
  3417.   memset ( OldItems, 0, sizeof(OldItems) ) ;
  3418.  
  3419.   USHORT OldCount = 0 ;
  3420.   if ( OldDrives )
  3421.   {
  3422.     OldCount = Profile->ItemCount ;
  3423.     memcpy ( OldItems, Items, sizeof(OldItems) ) ;
  3424.   }
  3425.  
  3426.  /***************************************************************************
  3427.   * Add items for each drive on the system.                    *
  3428.   ***************************************************************************/
  3429.  
  3430.   USHORT Count = ITEM_BASE_COUNT ;
  3431.   USHORT OldIndex = ITEM_BASE_COUNT ;
  3432.  
  3433.   NewDrives >>= 2 ;
  3434.   OldDrives >>= 2 ;
  3435.  
  3436.   for ( int Drive=3; Drive<=MAX_DRIVES; Drive++ )
  3437.   {
  3438.     while ( ( OldIndex < OldCount )
  3439.       AND ( (SHORT)OldItems[OldIndex].MenuId < IDM_SHOW_DRIVE_FREE + Drive ) )
  3440.     {
  3441.       OldIndex ++ ;
  3442.     }
  3443.  
  3444.     if ( NewDrives & 1 )
  3445.     {
  3446.       if ( OldDrives & 1 )
  3447.       {
  3448.     if ( ( OldIndex < OldCount )
  3449.       AND ( (SHORT)OldItems[OldIndex].MenuId == IDM_SHOW_DRIVE_FREE + Drive ) )
  3450.     {
  3451.       Items[Count++] = OldItems[OldIndex++] ;
  3452.     }
  3453.       }
  3454.       else
  3455.       {
  3456.         BYTE FileSystem [80] ;
  3457.     if ( CheckDrive ( Drive, FileSystem ) )
  3458.     {
  3459.       sprintf ( (PCHAR)Items[Count].Name,        "ShowDrive%c:",      Drive+'A'-1 ) ;
  3460.       sprintf ( (PCHAR)Items[Count].Label,      (PCHAR)LabelFormat,  Drive+'A'-1, FileSystem ) ;
  3461.       sprintf ( (PCHAR)Items[Count].MenuOption, (PCHAR)OptionFormat, Drive+'A'-1 ) ;
  3462.  
  3463.       Items[Count].MenuId = IDM_SHOW_DRIVE_FREE + Drive ;
  3464.       Items[Count].NewValue = ComputeDriveFree ;
  3465.       Items[Count].Parm = Drive ;
  3466.       Items[Count].Divisor = 1024 ;
  3467.       Items[Count].Suffix = 'K' ;
  3468.       Items[Count].Error = FALSE ;
  3469.       Count ++ ;
  3470.     }
  3471.       }
  3472.     }
  3473.  
  3474.     NewDrives >>= 1 ;
  3475.     OldDrives >>= 1 ;
  3476.   }
  3477.  
  3478.  /***************************************************************************
  3479.   * Save pointer to fixed configuration information.                *
  3480.   ***************************************************************************/
  3481.  
  3482.   Profile->Items = Items ;
  3483.   Profile->ItemCount = Count ;
  3484.  
  3485.  /***************************************************************************
  3486.   * Fetch the display flags for the drives.                    *
  3487.   ***************************************************************************/
  3488.  
  3489.   ULONG Size ;
  3490.   for ( int i=ITEM_BASE_COUNT; i<Profile->ItemCount; i++ )
  3491.   {
  3492.     PITEM Item = Profile->Items + i ;
  3493.     Item->Flag = TRUE ;
  3494.     if
  3495.     (
  3496.       PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, Item->Name, &Size )
  3497.       AND
  3498.       ( Size == sizeof(Item->Flag) )
  3499.       AND
  3500.       PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, Item->Name, &Item->Flag, &Size )
  3501.     )
  3502.     {
  3503.       ;
  3504.     }
  3505.   }
  3506.  
  3507.  /***************************************************************************
  3508.   * Release the item list.                            *
  3509.   ***************************************************************************/
  3510.  
  3511.   DosReleaseMutexSem ( ItemSemaphore ) ;
  3512. }
  3513.  
  3514. /****************************************************************************
  3515.  *                                        *
  3516.  *    Check to see if drive should be added to display list.            *
  3517.  *                                        *
  3518.  ****************************************************************************/
  3519.  
  3520. STATIC BOOL CheckDrive ( USHORT Drive, PBYTE FileSystem )
  3521. {
  3522.  /***************************************************************************
  3523.   * First, check to see if drive is local or remote.  Remote drives are     *
  3524.   *   always monitored.                             *
  3525.   ***************************************************************************/
  3526.  
  3527.   BYTE Path [3] ;
  3528.   Path[0] = (BYTE) ( Drive + 'A' - 1 ) ;
  3529.   Path[1] = ':' ;
  3530.   Path[2] = 0 ;
  3531.  
  3532.   DosError ( FERR_DISABLEHARDERR ) ;
  3533.  
  3534.   BYTE Buffer [1024] ;
  3535.   ULONG Size = sizeof(Buffer) ;
  3536.   ULONG Status = DosQueryFSAttach ( Path, 0, FSAIL_QUERYNAME, (PFSQBUFFER2)Buffer, &Size ) ;
  3537.   DosError ( FERR_ENABLEHARDERR ) ;
  3538.  
  3539.   if ( Status )
  3540.   {
  3541.     Log ( "ERROR: Unable to query drive %s for file system.  Status %04X.\r\n",
  3542.       Path, Status ) ;
  3543.     return ( FALSE ) ;
  3544.   }
  3545.  
  3546.   USHORT cbName = ((PFSQBUFFER2)Buffer)->cbName ;
  3547.   strcpy ( (PCHAR)FileSystem, (PCHAR)((PFSQBUFFER2)(Buffer+cbName))->szFSDName ) ;
  3548.  
  3549.   if ( ((PFSQBUFFER2)Buffer)->iType == FSAT_REMOTEDRV )
  3550.   {
  3551.     return ( TRUE ) ;
  3552.   }
  3553.  
  3554.  /***************************************************************************
  3555.   * Attempt to open the local drive as an entire device.  If unable to do   *
  3556.   *   so, we cannot monitor this drive.                     *
  3557.   ***************************************************************************/
  3558.  
  3559.   ULONG Action ;
  3560.   HFILE Handle ;
  3561.   Status = DosOpen ( Path, &Handle, &Action, 0, 0, FILE_OPEN,
  3562.     OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE |
  3563.     OPEN_FLAGS_DASD | OPEN_FLAGS_FAIL_ON_ERROR, 0 ) ;
  3564.  
  3565.   if ( Status )
  3566.   {
  3567.     Log ( "ERROR: Unable to open local drive %s.  Status %04X.\r\n",
  3568.       Path, Status ) ;
  3569.     return ( FALSE ) ;
  3570.   }
  3571.  
  3572.  /***************************************************************************
  3573.   * Check to see if the drive has removable media.  We cannot monitor such. *
  3574.   ***************************************************************************/
  3575.  
  3576.   BOOL Addit = FALSE ;
  3577.   BYTE Command = 0 ;
  3578.   BYTE NonRemovable ;
  3579.  
  3580.   ULONG LengthIn = sizeof(Command) ;
  3581.   ULONG LengthOut = sizeof(NonRemovable);
  3582.  
  3583.   if 
  3584.   ( 
  3585.     NOT DosDevIOCtl 
  3586.     ( 
  3587.       Handle, 8, 0x20, 
  3588.       &Command, sizeof(Command), &LengthIn,
  3589.       &NonRemovable, sizeof(NonRemovable), &LengthOut 
  3590.     ) 
  3591.   )
  3592.   {
  3593.     Addit = NonRemovable ;
  3594.   }
  3595.  
  3596.  /***************************************************************************
  3597.   * Close the drive.                                *
  3598.   ***************************************************************************/
  3599.  
  3600.   DosClose ( Handle ) ;
  3601.  
  3602.  /***************************************************************************
  3603.   * Return the final verdict.                            *
  3604.   ***************************************************************************/
  3605.  
  3606.   return ( Addit ) ;
  3607. }
  3608.  
  3609. /****************************************************************************
  3610.  *                                        *
  3611.  *    Rebuild the Display Items submenu.                    *
  3612.  *                                        *
  3613.  ****************************************************************************/
  3614.  
  3615. STATIC VOID RebuildDisplayItems ( HWND hwnd, PDATA Data )
  3616. {
  3617.  /***************************************************************************
  3618.   * Lock the item list.                             *
  3619.   ***************************************************************************/
  3620.  
  3621.   if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
  3622.   {
  3623.     Log ( "ERROR: Unable to lock item list to rebuild the display item menu.\r\n" ) ;
  3624.     DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
  3625.   }
  3626.  
  3627.  /***************************************************************************
  3628.   * Find the item menu's handle.                                            *
  3629.   ***************************************************************************/
  3630.  
  3631.   HWND Frame = WinQueryWindow ( hwnd, QW_PARENT ) ;
  3632.  
  3633.   HWND SysMenu = WinWindowFromID ( Frame, FID_SYSMENU ) ;
  3634.  
  3635.   MENUITEM MenuItem ;
  3636.   WinSendMsg ( SysMenu, MM_QUERYITEM,
  3637.     MPFROM2SHORT ( IDM_DISPLAY_ITEMS, TRUE ),
  3638.     (MPARAM) &MenuItem ) ;
  3639.  
  3640.   HWND ItemMenu = MenuItem.hwndSubMenu ;
  3641.  
  3642.  /***************************************************************************
  3643.   * Remove all items from the menu.                        *
  3644.   ***************************************************************************/
  3645.  
  3646.   if ( WinSendMsg ( ItemMenu, MM_QUERYITEMCOUNT, 0, 0 ) )
  3647.   {
  3648.     USHORT Id ;
  3649.     SHORT ItemsLeft ;
  3650.  
  3651.     do
  3652.     {
  3653.       Id = SHORT1FROMMR ( WinSendMsg ( ItemMenu, MM_ITEMIDFROMPOSITION,
  3654.     0, 0 ) ) ;
  3655.  
  3656.       ItemsLeft = SHORT1FROMMR ( WinSendMsg ( ItemMenu, MM_DELETEITEM,
  3657.     MPFROM2SHORT(Id,FALSE), 0 ) ) ;
  3658.     }
  3659.     while ( ItemsLeft ) ;
  3660.   }
  3661.  
  3662.  /***************************************************************************
  3663.   * Prepare menu item structure for use.                    *
  3664.   ***************************************************************************/
  3665.  
  3666.   MenuItem.iPosition = MIT_END ;
  3667.   MenuItem.afStyle = MIS_TEXT ;
  3668.   MenuItem.afAttribute = 0 ;
  3669.   MenuItem.hwndSubMenu = NULL ;
  3670.   MenuItem.hItem = 0L ;
  3671.  
  3672.  /***************************************************************************
  3673.   * Add all menu items called for.                        *
  3674.   ***************************************************************************/
  3675.  
  3676.   for ( int i=0; i<Data->Profile.ItemCount; i++ )
  3677.   {
  3678.     PITEM Item = Data->Profile.Items + i ;
  3679.  
  3680.     MenuItem.id = Item->MenuId ;
  3681.  
  3682.     AddSysSubMenuItem ( Frame, IDM_DISPLAY_ITEMS, &MenuItem, Item->MenuOption ) ;
  3683.  
  3684.     CheckMenuItem ( Frame, FID_SYSMENU, Item->MenuId, Item->Flag ) ;
  3685.   }
  3686.  
  3687.  /***************************************************************************
  3688.   * Split the menu if it's too tall.                                        *
  3689.   ***************************************************************************/
  3690.  
  3691.   if ( Data->Profile.ItemCount > 15 )
  3692.   {
  3693.     USHORT Midpoint = ( Data->Profile.ItemCount + 1 ) / 2 ;
  3694.     USHORT Id = Data->Profile.Items[Midpoint].MenuId ;
  3695.     MENUITEM MenuItem ;
  3696.  
  3697.     if ( WinSendMsg ( ItemMenu, MM_QUERYITEM, MPFROM2SHORT(Id,FALSE), &MenuItem ) )
  3698.     {
  3699.       MenuItem.afStyle |= MIS_BREAKSEPARATOR ;
  3700.       WinSendMsg ( ItemMenu, MM_SETITEM, MPFROM2SHORT(0,FALSE), &MenuItem ) ;
  3701.     }
  3702.   }
  3703.  
  3704.  /***************************************************************************
  3705.   * Release the item list.                            *
  3706.   ***************************************************************************/
  3707.  
  3708.   DosReleaseMutexSem ( ItemSemaphore ) ;
  3709. }
  3710.  
  3711. /****************************************************************************
  3712.  *                                        *
  3713.  *             Calibrate the Load Meter                *
  3714.  *                                        *
  3715.  ****************************************************************************/
  3716.  
  3717. STATIC ULONG CalibrateLoadMeter ( void )
  3718. {
  3719.  /***************************************************************************
  3720.   * Set result to zero as a default.                        *
  3721.   ***************************************************************************/
  3722.  
  3723.   double AdjustedMaxLoad = 0.0 ;
  3724.  
  3725.  /***************************************************************************
  3726.   * If HRTIMER.SYS has been installed . . .                    *
  3727.   ***************************************************************************/
  3728.  
  3729.   if ( OpenTimer ( ) )
  3730.   {
  3731.    /*************************************************************************
  3732.     * Increase this thread's priority to the maximum.                       *
  3733.     *************************************************************************/
  3734.  
  3735.     DosSetPrty ( PRTYS_THREAD, PRTYC_TIMECRITICAL, PRTYD_MAXIMUM, 0 ) ;
  3736.  
  3737.    /*************************************************************************
  3738.     * Create the calibration thread and set its priority next highest.        *
  3739.     *************************************************************************/
  3740.  
  3741.     TID tidCalibrate ;
  3742.     ULONG MaxLoad ;
  3743.     DosCreateThread ( &tidCalibrate, CounterThread, (ULONG)&MaxLoad, 0, 4096 ) ;
  3744.     DosSetPrty ( PRTYS_THREAD, PRTYC_TIMECRITICAL, PRTYD_MAXIMUM-1, tidCalibrate ) ;
  3745.     DosSuspendThread ( tidCalibrate ) ;
  3746.  
  3747.    /*************************************************************************
  3748.     * Reset the calibration count, get the time, and let the counter go.    *
  3749.     *************************************************************************/
  3750.  
  3751.     MaxLoad = 0 ;
  3752.     TIMESTAMP Time[2] ;
  3753.     GetTime ( &Time[0] ) ;
  3754.     DosResumeThread ( tidCalibrate ) ;
  3755.  
  3756.    /*************************************************************************
  3757.     * Sleep for one second.                            *
  3758.     *************************************************************************/
  3759.  
  3760.     DosSleep ( 1000 ) ;
  3761.  
  3762.    /*************************************************************************
  3763.     * Suspend the calibration counter and get the time.             *
  3764.     *************************************************************************/
  3765.  
  3766.     DosSuspendThread ( tidCalibrate ) ;
  3767.     GetTime ( &Time[1] ) ;
  3768.  
  3769.    /*************************************************************************
  3770.     * Return priorities to normal.                        *
  3771.     *************************************************************************/
  3772.  
  3773.     DosSetPrty ( PRTYS_THREAD, PRTYC_REGULAR, 0, 0 ) ;
  3774.  
  3775.    /*************************************************************************
  3776.     * Get the elapsed time and adjust the calibration count.            *
  3777.     *************************************************************************/
  3778.  
  3779.     ULONG Milliseconds ;
  3780.     ULONG Nanoseconds ;
  3781.     Milliseconds = ElapsedTime ( &Time[0], &Time[1], &Nanoseconds ) ;
  3782.  
  3783.     AdjustedMaxLoad = (double)MaxLoad * 1.0E9 ;
  3784.     AdjustedMaxLoad /= (double)Milliseconds*1.0E6L + (double)Nanoseconds ;
  3785.  
  3786.    /*************************************************************************
  3787.     * Close down the connection to HRTIMER.SYS.                 *
  3788.     *************************************************************************/
  3789.  
  3790.     CloseTimer ( ) ;
  3791.   }
  3792.  
  3793.  /***************************************************************************
  3794.   * Return the adjusted calibration count.  If HRTIMER was not there, it    *
  3795.   *   will be zero.                                *
  3796.   ***************************************************************************/
  3797.  
  3798.   return ( (ULONG)AdjustedMaxLoad ) ;
  3799. }
  3800.  
  3801. /****************************************************************************
  3802.  *                                        *
  3803.  *              General Purpose Counter Thread                *
  3804.  *                                        *
  3805.  ****************************************************************************/
  3806.  
  3807. STATIC VOID CounterThread ( PULONG Counter )
  3808. {
  3809.   while ( 1 )
  3810.   {
  3811.     (*Counter) ++ ;
  3812.   }
  3813. }
  3814.  
  3815. /****************************************************************************
  3816.  *                                        *
  3817.  *    Open the Profile                            *
  3818.  *                                        *
  3819.  ****************************************************************************/
  3820.  
  3821. STATIC HINI OpenProfile ( PSZ Name, HAB Anchor, HMODULE Library, HWND HelpInstance )
  3822. {
  3823.  /***************************************************************************
  3824.   * Query the system INI for the profile file's path.                       *
  3825.   ***************************************************************************/
  3826.  
  3827.   PSZ ProfilePath = NULL ;
  3828.   ULONG Size ;
  3829.  
  3830.   if ( PrfQueryProfileSize ( HINI_USERPROFILE, PSZ(PROGRAM_NAME), PSZ("INIPATH"), &Size ) )
  3831.   {
  3832.     // The info exists.  Fetch it.
  3833.     ProfilePath = PSZ ( AllocateMemory ( Size ) ) ;
  3834.     PrfQueryProfileData ( HINI_USERPROFILE, PSZ(PROGRAM_NAME), PSZ("INIPATH"),
  3835.       ProfilePath, &Size ) ;
  3836.  
  3837.     // Build the profile file name.
  3838.     BYTE FullPath [_MAX_PATH] ;
  3839.     strcpy ( PCHAR(FullPath), PCHAR(ProfilePath) ) ;
  3840.     strcat ( PCHAR(FullPath), "\\" ) ;
  3841.     strcat ( PCHAR(FullPath), PCHAR(Name) ) ;
  3842.     strcat ( PCHAR(FullPath), ".INI" ) ;
  3843.  
  3844.     // Clean the name up and expand it to a full path.
  3845.     BYTE Path [256] ;
  3846.     DosQueryPathInfo ( FullPath, FIL_QUERYFULLNAME, Path, sizeof(Path) ) ;
  3847.  
  3848.     // Does the file exist?  If not, discard the name.
  3849.     FILESTATUS3 Status ;
  3850.     if ( DosQueryPathInfo ( Path, FIL_STANDARD, &Status, sizeof(Status) ) )
  3851.     {
  3852.       FreeMemory ( ProfilePath ) ;
  3853.       ProfilePath = NULL ;
  3854.     }
  3855.   }
  3856.  
  3857.  /***************************************************************************
  3858.   * If the profile file couldn't be found, ask the user for a path.         *
  3859.   ***************************************************************************/
  3860.  
  3861.   if ( ProfilePath == NULL )
  3862.   {
  3863.     // Set the default path.
  3864.     BYTE Path [256] ;
  3865.     DosQueryPathInfo ( PSZ("."), FIL_QUERYFULLNAME, Path, sizeof(Path) ) ;
  3866.  
  3867.     // Call up the entry dialog.
  3868.     PROFILE_PARMS Parms ;
  3869.     Parms.id = IDD_PROFILE_PATH ;
  3870.     Parms.hwndHelp = HelpInstance ;
  3871.     Parms.Path = Path ;
  3872.     Parms.PathSize = sizeof(Path) ;
  3873.     if ( WinDlgBox ( HWND_DESKTOP, HWND_DESKTOP, ProfileProcessor,
  3874.       Library, IDD_PROFILE_PATH, &Parms ) )
  3875.     {
  3876.       // If OK, save the approved path in the system profile.
  3877.       ProfilePath = PSZ ( AllocateMemory ( strlen(PCHAR(Path)) + 1 ) ) ;
  3878.       strcpy ( PCHAR(ProfilePath), PCHAR(Path) ) ;
  3879.  
  3880.       PrfWriteProfileData ( HINI_USERPROFILE, PSZ(PROGRAM_NAME), PSZ("INIPATH"),
  3881.     ProfilePath, strlen(PCHAR(ProfilePath))+1 ) ;
  3882.     }
  3883.     else
  3884.     {
  3885.       // If not, return an error.
  3886.       return ( NULL ) ;
  3887.     }
  3888.   }
  3889.  
  3890.  /***************************************************************************
  3891.   * Build the full profile file name.                        *
  3892.   ***************************************************************************/
  3893.  
  3894.   BYTE ProfileName [_MAX_PATH] ;
  3895.   strcpy ( PCHAR(ProfileName), PCHAR(ProfilePath) ) ;
  3896.   strcat ( PCHAR(ProfileName), "\\" PROGRAM_NAME ".INI" ) ;
  3897.  
  3898.  /***************************************************************************
  3899.   * Release the memory previously allocated to store the path.            *
  3900.   ***************************************************************************/
  3901.  
  3902.   if ( ProfilePath )
  3903.   {
  3904.     FreeMemory ( ProfilePath ) ;
  3905.   }
  3906.  
  3907.  /***************************************************************************
  3908.   * Open/Create the profile file and return the resultant handle.        *
  3909.   ***************************************************************************/
  3910.  
  3911.   return ( PrfOpenProfile ( Anchor, ProfileName ) ) ;
  3912. }
  3913. 
  3914.