Category : OS/2 Files
Archive   : MEMSZ200.ZIP
Filename : MEMSIZE.CC

 
Output of file : MEMSIZE.CC contained in archive : MEMSZ200.ZIP

/***************************************************************** MEMSIZE.CC
* *
* System Resources Monitor *
* *
* (C) Copyright 1991-1993 by Richard W. Papo. *
* *
* This is 'FreeWare'. As such, it may be copied and distributed *
* freely. If you want to use part of it in your own program, please *
* give credit where credit is due. If you want to change the *
* program, please refer the change request to me or send me the *
* modified source code. I can be reached at CompuServe 72607,3111. *
* *
****************************************************************************/

#define INCL_BASE
#define INCL_PM
#include

#include
#include
#include

#include "debug.h"
#include "support.h"
#include "about.h"
#include "profile.h"
#include "settimer.h"

#include "memsize.h"

#define STATIC extern

/****************************************************************************
* *
* Definitions & Declarations *
* *
****************************************************************************/

// Constants

#define PROGRAM_NAME "MEMSIZE"
#define CLASS_NAME PROGRAM_NAME

#define DATEFMT_MM_DD_YY (0x0000)
#define DATEFMT_DD_MM_YY (0x0001)
#define DATEFMT_YY_MM_DD (0x0002)

#define WM_REFRESH (WM_USER)

#define MAX_DRIVES (26)
#define DRIVE_ERROR (0xFFFFFFFFL)

enum
{
ITEM_CLOCK,
ITEM_ELAPSEDTIME,
ITEM_MEMORYFREE,
ITEM_SWAPFILESIZE,
ITEM_SWAPDISKFREE,
ITEM_SPOOLFILESIZE,
ITEM_CPULOAD,
ITEM_TASKCOUNT,
ITEM_BASE_COUNT
} ;


// Macros

#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))


// Data Types

typedef struct // Data structure for item to be monitored.
{
BYTE Name [80] ; // Text for items profile name.
BOOL Flag ; // Flag: Show this item at this time?
BYTE Label [80] ; // Text to display on left part of line.
ULONG Value ; // Value to display on right part of line.
BOOL Error ; // Flag: Drive has had an error.
BYTE MenuOption [80] ; // Text to display in system menu.
USHORT MenuId ; // ID for use in system menu.
ULONG (*NewValue) // Function to determine new value.
(PVOID,USHORT) ;
USHORT Parm ; // Parameter to pass to NewValue function.
USHORT Divisor ; // Amount to divide value by before display.
BYTE Suffix ; // Character to place after value.
}
ITEM, *PITEM ;

typedef struct // Parameters saved to system.
{
PITEM Items ; // Items to display.
int ItemCount ;

SWP Position ; // Window size & location.
BOOL fPosition ;

BOOL HideControls ; // User options.
BOOL fHideControls ;

USHORT TimerInterval ;
BOOL fTimerInterval ;

BYTE FontNameSize [80] ; // Presentation Parameters
BOOL fFontNameSize ;

COLOR BackColor ;
BOOL fBackColor ;

COLOR TextColor ;
BOOL fTextColor ;
}
PROFILE, *PPROFILE ;

typedef struct // Data structure for window.
{
HAB Anchor ;
HMODULE Library ;
HINI ProfileHandle ;

ULONG MaxCount ;
ULONG IdleCounter ;
ULONG IdleCount ;
TID IdleLoopTID ;
TID MonitorLoopTID ;

PROFILE Profile ;

HWND hwndTitleBar ;
HWND hwndSysMenu ;
HWND hwndMinMax ;

ULONG Drives ;

BYTE SwapPath [_MAX_PATH] ;
ULONG MinFree ;

PCHAR SpoolPath ;

long Width ;
long Height ;

COUNTRYINFO CountryInfo ;

BYTE Day [40] ;
BYTE Days [40] ;
BYTE DriveError [40] ;
}
DATA, *PDATA ;

typedef struct
{
HAB Anchor ;
HMODULE Library ;
HINI ProfileHandle ;
}
PARMS, *PPARMS ;

typedef struct
{
volatile PULONG Counter ;
PUSHORT Interval ;
HWND Owner ;
}
MONITOR_PARMS, *PMONITOR_PARMS ;


// Function Prototypes

extern INT main ( INT argc, PCHAR argv[] ) ;

STATIC MRESULT EXPENTRY MessageProcessor
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
) ;

STATIC METHODFUNCTION Create ;
STATIC METHODFUNCTION Destroy ;
STATIC METHODFUNCTION Size ;
STATIC METHODFUNCTION SaveApplication ;
STATIC METHODFUNCTION Paint ;
STATIC METHODFUNCTION Command ;
STATIC METHODFUNCTION ResetDefaults ;
STATIC METHODFUNCTION HideControlsCmd ;
STATIC METHODFUNCTION SetTimer ;
STATIC METHODFUNCTION About ;
STATIC METHODFUNCTION ButtonDown ;
STATIC METHODFUNCTION ButtonDblClick ;
STATIC METHODFUNCTION PresParamChanged ;
STATIC METHODFUNCTION SysColorChange ;
STATIC METHODFUNCTION QueryKeysHelp ;
STATIC METHODFUNCTION HelpError ;
STATIC METHODFUNCTION ExtHelpUndefined ;
STATIC METHODFUNCTION HelpSubitemNotFound ;
STATIC METHODFUNCTION Refresh ;

STATIC int GetProfile ( HAB Anchor, HMODULE Library, HINI ProfileHandle, PPROFILE Profile ) ;
STATIC VOID PutProfile ( HINI ProfileHandle, PPROFILE Profile ) ;

STATIC PSZ ScanSystemConfig ( HAB Anchor, PSZ Keyword ) ;

STATIC void ResizeWindow ( HWND hwnd, PPROFILE Profile ) ;

STATIC void HideControls
(
BOOL fHide,
HWND hwndFrame,
HWND hwndSysMenu,
HWND hwndTitleBar,
HWND hwndMinMax
) ;

STATIC void UpdateWindow ( HWND hwnd, PDATA Data, BOOL All ) ;

STATIC ULONG ComputeTime ( PVOID Data, USHORT Dummy ) ;
STATIC ULONG ComputeElapsed ( PVOID Data, USHORT Dummy ) ;
STATIC ULONG ComputeFreeMemory ( PVOID Data, USHORT Dummy ) ;
STATIC ULONG ComputeSwapSize ( PVOID Data, USHORT Dummy ) ;
STATIC ULONG ComputeSwapFree ( PVOID Data, USHORT Dummy ) ;
STATIC ULONG ComputeSpoolSize ( PVOID Data, USHORT Dummy ) ;
STATIC ULONG ComputeCpuLoad ( PVOID Data, USHORT Dummy ) ;
STATIC ULONG ComputeTaskCount ( PVOID Data, USHORT Dummy ) ;
STATIC ULONG ComputeDriveFree ( PVOID Data, USHORT Drive ) ;

STATIC VOID MonitorLoopThread ( PMONITOR_PARMS Parms ) ;

STATIC VOID UpdateDriveList
(
HAB Anchor,
HMODULE Library,
HINI ProfileHandle,
PPROFILE Profile,
ULONG OldDrives,
ULONG NewDrives
) ;

STATIC BOOL CheckDrive ( USHORT Drive, PBYTE FileSystem ) ;

STATIC VOID RebuildDisplayItems ( HWND hwnd, PDATA Data ) ;

STATIC ULONG CalibrateLoadMeter ( VOID ) ;

STATIC VOID CounterThread ( PULONG Counter ) ;

STATIC HINI OpenProfile ( PSZ Name, HAB Anchor, HMODULE Library, HWND HelpInstance ) ;


// Global Data (private)

static HMTX ItemSemaphore ;

static ITEM Items [ ITEM_BASE_COUNT + MAX_DRIVES ] =
{
{
"ShowTime", TRUE, "", 0, FALSE, "", IDM_SHOW_CLOCK,
ComputeTime, 0, 0, ' '
},

{
"ShowElapsed", TRUE, "", 0, FALSE, "", IDM_SHOW_ELAPSED,
ComputeElapsed, 0, 0, ' '
},

{
"ShowMemory", TRUE, "", 0, FALSE, "", IDM_SHOW_MEMORY,
ComputeFreeMemory, 0, 1024, 'K'
},

{
"ShowSwapsize", TRUE, "", 0, FALSE, "", IDM_SHOW_SWAPSIZE,
ComputeSwapSize, 0, 1024, 'K'
},

{
"ShowSwapfree", TRUE, "", 0, FALSE, "", IDM_SHOW_SWAPFREE,

ComputeSwapFree, 0, 1024, 'K'
},

{
"ShowSpoolSize", TRUE, "", 0, FALSE, "", IDM_SHOW_SPOOLSIZE,
ComputeSpoolSize, 0, 1024, 'K'
},

{
"ShowCpuLoad", TRUE, "", 0, FALSE, "", IDM_SHOW_CPULOAD,
ComputeCpuLoad, 0, 0, '%'
},

{
"ShowTaskCount", TRUE, "", 0, FALSE, "", IDM_SHOW_TASKCOUNT,
ComputeTaskCount, 0, 0, ' '
}
} ;


/****************************************************************************
* *
* Program Mainline *
* *
****************************************************************************/

extern INT main ( INT argc, PCHAR argv[] )
{
/***************************************************************************
* Initialize for PM. Abort if unable to do so. *
***************************************************************************/

HAB Anchor = WinInitialize ( 0 ) ;
if ( Anchor == NULL )
{
return ( 1 ) ;
}

/***************************************************************************
* Create the application message queue. Abort if unable to do so. *
***************************************************************************/

HMQ MessageQueue = WinCreateMsgQueue ( Anchor, 0 ) ;
if ( MessageQueue == NULL )
{
WinTerminate ( Anchor ) ;
return ( 1 ) ;
}

/***************************************************************************
* Now WIN and GPI calls will work. Open the language DLL. *
***************************************************************************/

HMODULE Library ;
if ( DosLoadModule ( NULL, 0, (PSZ)PROGRAM_NAME, &Library ) )
{
Debug ( HWND_DESKTOP, "ERROR: Unable to load " PROGRAM_NAME ".DLL." ) ;
WinDestroyMsgQueue ( MessageQueue ) ;
WinTerminate ( Anchor ) ;
return ( 1 ) ;
}

/***************************************************************************
* Get the program title. *
***************************************************************************/

BYTE Title [80] ;
WinLoadString ( Anchor, Library, IDS_TITLE, sizeof(Title), Title ) ;

/***************************************************************************
* Decipher command-line parameters. *
***************************************************************************/

BOOL Reset = FALSE ;
BYTE ResetCommand [40] ;

WinLoadString ( Anchor, Library, IDS_PARMS_RESET, sizeof(ResetCommand), ResetCommand ) ;
WinUpper ( Anchor, NULL, NULL, ResetCommand ) ;

while ( --argc )
{
argv ++ ;

WinUpper ( Anchor, NULL, NULL, (PSZ)*argv ) ;

if ( *argv[0] == '?' )
{
BYTE Message [200] ;

WinLoadString ( Anchor, Library, IDS_PARAMETERLIST,
sizeof(Message), Message ) ;
WinMessageBox ( HWND_DESKTOP, HWND_DESKTOP, Message,
Title, 0, MB_ENTER | MB_NOICON ) ;

WinDestroyMsgQueue ( MessageQueue ) ;
WinTerminate ( Anchor ) ;
return ( 1 ) ;
}

if ( !strcmp ( *argv, (PCHAR)ResetCommand ) )
{
Reset = TRUE ;
continue ;
}

{
BYTE Format [200] ;
BYTE Message [200] ;

WinLoadString ( Anchor, Library, IDS_ERROR_INVALIDPARM,
sizeof(Format), Format ) ;
sprintf ( (PCHAR)Message, (PCHAR)Format, *argv ) ;
WinMessageBox ( HWND_DESKTOP, HWND_DESKTOP, Message,
Title, 0, MB_ENTER | MB_ICONEXCLAMATION ) ;

WinDestroyMsgQueue ( MessageQueue ) ;
WinTerminate ( Anchor ) ;
return ( 1 ) ;
}
}

/***************************************************************************
* Create the help instance. *
***************************************************************************/

HELPINIT HelpInit =
{
sizeof ( HELPINIT ),
0L,
NULL,
MAKEP ( 0xFFFF, ID_MAIN ),
0,
0,
0,
0,
NULL,
CMIC_HIDE_PANEL_ID,
(PSZ) ( PROGRAM_NAME ".HLP" )
} ;

BYTE HelpTitle [80] ;
WinLoadString ( Anchor, Library, IDS_HELPTITLE, sizeof(HelpTitle), HelpTitle ) ;
HelpInit.pszHelpWindowTitle = HelpTitle ;

HWND hwndHelp = WinCreateHelpInstance ( Anchor, &HelpInit ) ;

if ( hwndHelp == NULL )
{
BYTE Message [200] ;

WinLoadString ( Anchor, Library, IDS_ERROR_WINCREATEHELPINSTANCE,
sizeof(Message), Message ) ;
WinMessageBox ( HWND_DESKTOP, HWND_DESKTOP, Message,
Title, 0, MB_ENTER | MB_ICONEXCLAMATION ) ;
}

/***************************************************************************
* Open/create the profile file. *
***************************************************************************/

HINI ProfileHandle = OpenProfile ( PSZ(PROGRAM_NAME), Anchor, Library, hwndHelp ) ;

if ( ProfileHandle == NULL )
{
BYTE Message [200] ;

WinLoadString ( Anchor, Library, IDS_ERROR_PRFOPENPROFILE,
sizeof(Message), Message ) ;
Log ( "%s\r\n", Message ) ;
WinMessageBox ( HWND_DESKTOP, HWND_DESKTOP, Message,
Title, 0, MB_ENTER | MB_ICONEXCLAMATION ) ;
DosFreeModule ( Library ) ;
WinDestroyMsgQueue ( MessageQueue ) ;
WinTerminate ( Anchor ) ;
return ( 1 ) ;
}

/***************************************************************************
* If we're going to reset the program's profile, do it now. *
***************************************************************************/

if ( Reset )
{
PrfWriteProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, NULL, NULL, 0 ) ;
}

/***************************************************************************
* Create the frame window. *
***************************************************************************/

#pragma pack(2)
struct
{
USHORT Filler ;
USHORT cb ;
ULONG flCreateFlags ;
USHORT hmodResources ;
USHORT idResources ;
}
fcdata ;
#pragma pack()

fcdata.cb = sizeof(fcdata) - sizeof(fcdata.Filler) ;
fcdata.flCreateFlags =
FCF_TITLEBAR | FCF_SYSMENU | FCF_BORDER |
FCF_ICON | FCF_MINBUTTON | FCF_NOBYTEALIGN | FCF_ACCELTABLE ;
fcdata.hmodResources = 0 ;
fcdata.idResources = ID_MAIN ;

HWND hwndFrame = WinCreateWindow
(
HWND_DESKTOP,
WC_FRAME,
Title,
0,
0, 0, 0, 0,
HWND_DESKTOP,
HWND_TOP,
ID_MAIN,
&fcdata.cb,
NULL
) ;

if ( hwndFrame == NULL )
{
BYTE Message [200] ;

WinLoadString ( Anchor, Library, IDS_ERROR_WINCREATEFRAME,
sizeof(Message), Message ) ;
WinMessageBox ( HWND_DESKTOP, HWND_DESKTOP, Message,
Title, 0, MB_ENTER | MB_ICONEXCLAMATION ) ;

PrfCloseProfile ( ProfileHandle ) ;
DosFreeModule ( Library ) ;
WinDestroyMsgQueue ( MessageQueue ) ;
WinTerminate ( Anchor ) ;
return ( 1 ) ;
}

/***************************************************************************
* Associate the help instance with the frame window. *
***************************************************************************/

if ( hwndHelp )
{
WinAssociateHelpInstance ( hwndHelp, hwndFrame ) ;
}

/***************************************************************************
* Register the window class. *
***************************************************************************/

if ( NOT WinRegisterClass ( Anchor, (PSZ)CLASS_NAME, MessageProcessor,
CS_MOVENOTIFY, sizeof(PVOID) ) )
{
BYTE Format [200] ;
BYTE Message [200] ;

WinLoadString ( Anchor, Library, IDS_ERROR_WINREGISTERCLASS,
sizeof(Format), Format ) ;
sprintf ( PCHAR(Message), PCHAR(Format), CLASS_NAME ) ;
WinMessageBox ( HWND_DESKTOP, HWND_DESKTOP, Message,
Title, 0, MB_ENTER | MB_ICONEXCLAMATION ) ;

PrfCloseProfile ( ProfileHandle ) ;
DosFreeModule ( Library ) ;
WinDestroyMsgQueue ( MessageQueue ) ;
WinTerminate ( Anchor ) ;
return ( 1 ) ;
}

/***************************************************************************
* Create client window. If this fails, destroy frame and return. *
***************************************************************************/

PARMS Parms ;
Parms.Anchor = Anchor ;
Parms.Library = Library ;
Parms.ProfileHandle = ProfileHandle ;

HWND hwndClient = WinCreateWindow
(
hwndFrame,
(PSZ)CLASS_NAME,
(PSZ)"",
0,
0, 0, 0, 0,
hwndFrame,
HWND_BOTTOM,
FID_CLIENT,
&Parms,
NULL
) ;

if ( hwndClient == NULL )
{
BYTE Message [200] ;

WinLoadString ( Anchor, Library, IDS_ERROR_WINCREATEWINDOW,
sizeof(Message), Message ) ;
WinMessageBox ( HWND_DESKTOP, HWND_DESKTOP, Message,
Title, 0, MB_ENTER | MB_ICONEXCLAMATION ) ;

WinDestroyWindow ( hwndFrame ) ;
if ( hwndHelp )
{
WinDestroyHelpInstance ( hwndHelp ) ;
}
PrfCloseProfile ( ProfileHandle ) ;
DosFreeModule ( Library ) ;
WinDestroyMsgQueue ( MessageQueue ) ;
WinTerminate ( Anchor ) ;
return ( 1 ) ;
}

/***************************************************************************
* Wait for and process messages to the window's queue. Terminate *
* when the WM_QUIT message is received. *
***************************************************************************/

QMSG QueueMessage ;
while ( WinGetMsg ( Anchor, &QueueMessage, NULL, 0, 0 ) )
{
WinDispatchMsg ( Anchor, &QueueMessage ) ;
}

/***************************************************************************
* Discard all that was requested of the system and terminate. *
***************************************************************************/

WinDestroyWindow ( hwndFrame ) ;

if ( hwndHelp )
{
WinDestroyHelpInstance ( hwndHelp ) ;
}

PrfCloseProfile ( ProfileHandle ) ;

DosFreeModule ( Library ) ;

WinDestroyMsgQueue ( MessageQueue ) ;

WinTerminate ( Anchor ) ;

return ( 0 ) ;
}

/****************************************************************************
* *
* Window Message Processor *
* *
****************************************************************************/

STATIC MRESULT EXPENTRY MessageProcessor
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Dispatch the message according to the method table and return the *
* result. Any messages not defined above get handled by the system *
* default window processor. *
***************************************************************************/

static METHOD Methods [] =
{
{ WM_CREATE, Create },
{ WM_DESTROY, Destroy },
{ WM_SIZE, Size },
{ WM_MOVE, Size },
{ WM_SAVEAPPLICATION, SaveApplication },
{ WM_PAINT, Paint },
{ WM_BUTTON1DOWN, ButtonDown },
{ WM_BUTTON2DOWN, ButtonDown },
{ WM_BUTTON1DBLCLK, ButtonDblClick },
{ WM_BUTTON2DBLCLK, ButtonDblClick },
{ WM_PRESPARAMCHANGED, PresParamChanged },
{ WM_SYSCOLORCHANGE, SysColorChange },
{ WM_COMMAND, Command },
{ HM_QUERY_KEYS_HELP, QueryKeysHelp },
{ HM_ERROR, HelpError },
{ HM_EXT_HELP_UNDEFINED, ExtHelpUndefined },
{ HM_HELPSUBITEM_NOT_FOUND, HelpSubitemNotFound },
{ WM_REFRESH, Refresh }
} ;

return ( DispatchMessage ( hwnd, msg, mp1, mp2, Methods, sizeof(Methods)/sizeof(Methods[0]), WinDefWindowProc ) ) ;
}

/****************************************************************************
* *
* Create the main window. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY Create
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Allocate instance data. *
***************************************************************************/

PDATA Data = malloc ( sizeof(DATA) ) ;

memset ( Data, 0, sizeof(DATA) ) ;

WinSetWindowPtr ( hwnd, QWL_USER, Data ) ;

/***************************************************************************
* Grab any parameters from the WM_CREATE message. *
***************************************************************************/

PPARMS Parms = (PPARMS) PVOIDFROMMP ( mp1 ) ;

Data->Anchor = Parms->Anchor ;
Data->Library = Parms->Library ;
Data->ProfileHandle = Parms->ProfileHandle ;

/***************************************************************************
* Create the item list MUTEX semaphore. *
***************************************************************************/

DosCreateMutexSem ( NULL, &ItemSemaphore, DC_SEM_SHARED, 0 ) ;

/***************************************************************************
* Get the current drive mask. *
***************************************************************************/

ULONG Drive ;
DosQueryCurrentDisk ( &Drive, &Data->Drives ) ;

/***************************************************************************
* Get profile data. Try the OS2.INI first, then try for private INI. *
* If obtained from OS2.INI, erase it afterwards. *
***************************************************************************/

if ( GetProfile ( Data->Anchor, Data->Library, HINI_USERPROFILE, &Data->Profile ) )
{
GetProfile ( Data->Anchor, Data->Library, Data->ProfileHandle, &Data->Profile ) ;
}
else
{
PrfWriteProfileData ( HINI_USERPROFILE, (PSZ)PROGRAM_NAME, NULL, NULL, 0 ) ;
}

/***************************************************************************
* Get country information. *
***************************************************************************/

{
COUNTRYCODE CountryCode ;
ULONG Count ;
ULONG Status ;

CountryCode.country = 0 ;
CountryCode.codepage = 0 ;

Status = DosGetCtryInfo ( sizeof(Data->CountryInfo), &CountryCode,
&Data->CountryInfo, &Count ) ;
if ( Status )
{
BYTE Message [80] ;
WinLoadMessage ( Data->Anchor, Data->Library, IDS_ERROR_DOSGETCTRYINFO,
sizeof(Message), Message ) ;
Debug ( hwnd, (PCHAR)Message, Status ) ;
Data->CountryInfo.fsDateFmt = DATEFMT_MM_DD_YY ;
Data->CountryInfo.fsTimeFmt = 0 ;
Data->CountryInfo.szDateSeparator[0] = '/' ;
Data->CountryInfo.szDateSeparator[1] = 0 ;
Data->CountryInfo.szTimeSeparator[0] = ':' ;
Data->CountryInfo.szTimeSeparator[1] = 0 ;
Data->CountryInfo.szThousandsSeparator[0] = ',' ;
Data->CountryInfo.szThousandsSeparator[1] = 0 ;
}
}

/***************************************************************************
* Get the texts that may be needed every second. *
***************************************************************************/

WinLoadString ( Data->Anchor, Data->Library, IDS_DAY, sizeof(Data->Day), Data->Day ) ;
WinLoadString ( Data->Anchor, Data->Library, IDS_DAYS, sizeof(Data->Days), Data->Days ) ;
WinLoadString ( Data->Anchor, Data->Library, IDS_DRIVEERROR, sizeof(Data->DriveError), Data->DriveError ) ;

/***************************************************************************
* Get the frame handle. *
***************************************************************************/

HWND hwndFrame = WinQueryWindow ( hwnd, QW_PARENT ) ;

/***************************************************************************
* Get the control window handles. *
***************************************************************************/

Data->hwndSysMenu = WinWindowFromID ( hwndFrame, FID_SYSMENU ) ;
Data->hwndTitleBar = WinWindowFromID ( hwndFrame, FID_TITLEBAR ) ;
Data->hwndMinMax = WinWindowFromID ( hwndFrame, FID_MINMAX ) ;

/***************************************************************************
* Create the submenu window for Display Items. *
***************************************************************************/

static MENUITEM MenuItems [] =
{
{ MIT_END, MIS_TEXT, 0, IDM_SAVE_APPLICATION, NULL, 0 },
{ MIT_END, MIS_TEXT, 0, IDM_RESET_DEFAULTS, NULL, 0 },
{ MIT_END, MIS_TEXT, 0, IDM_HIDE_CONTROLS, NULL, 0 },
{ MIT_END, MIS_TEXT, 0, IDM_SET_TIMER, NULL, 0 },
{ MIT_END, MIS_SUBMENU, 0, IDM_DISPLAY_ITEMS, NULL, 0 },
} ;

SHORT idSysMenu = SHORT1FROMMR ( WinSendMsg ( Data->hwndSysMenu, MM_ITEMIDFROMPOSITION, NULL, NULL ) ) ;

MENUITEM MenuItem ;
WinSendMsg ( Data->hwndSysMenu, MM_QUERYITEM, MPFROM2SHORT(idSysMenu,FALSE), MPFROMP(&MenuItem) ) ;

HWND hwndSysSubMenu = MenuItem.hwndSubMenu ;

HWND hwndSubMenu = WinCreateWindow ( hwndSysSubMenu, WC_MENU, (PSZ)"",
WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, 0, 0, hwndSysSubMenu, HWND_TOP, IDM_DISPLAY_ITEMS, NULL, NULL ) ;

MenuItems[IDM_DISPLAY_ITEMS-MenuItems[0].id].hwndSubMenu = hwndSubMenu ;

/***************************************************************************
* Add basic extensions to the system menu. *
***************************************************************************/

static MENUITEM MenuSeparator =
{ MIT_END, MIS_SEPARATOR, 0, 0, NULL, 0 } ;

AddSysMenuItem ( hwndFrame, &MenuSeparator, NULL ) ;

for ( int i=0; i {
BYTE MenuText [80] ;
WinLoadString ( Data->Anchor, Data->Library, i+IDS_SAVE_APPLICATION, sizeof(MenuText), MenuText ) ;
AddSysMenuItem ( hwndFrame, MenuItems+i, MenuText ) ;
}

/***************************************************************************
* Add 'About' to the system menu. *
***************************************************************************/

static MENUITEM MenuAbout =
{ MIT_END, MIS_TEXT, 0, IDM_ABOUT, NULL, 0 } ;

AddSysMenuItem ( hwndFrame, &MenuSeparator, NULL ) ;

BYTE AboutText [80] ;
WinLoadString ( Data->Anchor, Data->Library, IDS_ABOUT, sizeof(AboutText), AboutText ) ;

AddSysMenuItem ( hwndFrame, &MenuAbout, AboutText ) ;

/***************************************************************************
* Add 'Help' to the system menu. *
***************************************************************************/

static MENUITEM MenuHelp =
{ MIT_END, MIS_HELP, 0, 0, NULL, 0 } ;

BYTE HelpText [80] ;
WinLoadString ( Data->Anchor, Data->Library, IDS_HELP, sizeof(HelpText), HelpText ) ;

AddSysMenuItem ( hwndFrame, &MenuHelp, HelpText ) ;

/***************************************************************************
* Build the display items menu. *
***************************************************************************/

RebuildDisplayItems ( hwnd, Data ) ;

/***************************************************************************
* Get the SWAPPATH statement from CONFIG.SYS. *
***************************************************************************/

PSZ Swappath = ScanSystemConfig ( Data->Anchor, (PSZ)"SWAPPATH" ) ;

if ( Swappath == NULL )
{
Swappath = (PSZ) "C:\\OS2\\SYSTEM 0" ;
}

sscanf ( (PCHAR)Swappath, "%s %li", Data->SwapPath, &Data->MinFree ) ;

/***************************************************************************
* Find out where the spool work directory is. *
***************************************************************************/

Data->SpoolPath = NULL ;

ULONG Size ;
if ( PrfQueryProfileSize ( HINI_PROFILE, (PSZ)"PM_SPOOLER", (PSZ)"DIR", &Size ) )
{
Data->SpoolPath = malloc ( (int)Size ) ;
if ( Data->SpoolPath )
{
if ( PrfQueryProfileData ( HINI_PROFILE, (PSZ)"PM_SPOOLER", (PSZ)"DIR", Data->SpoolPath, &Size ) )
{
PBYTE p = (PBYTE) strchr ( (PCHAR)Data->SpoolPath, ';' ) ;
if ( p )
{
*p = 0 ;
}
}
else
{
free ( Data->SpoolPath ) ;
Data->SpoolPath = NULL ;
}
}
}

/***************************************************************************
* Calibrate the old-style load meter, if the high resolution timer's *
* available. *
***************************************************************************/

Data->MaxCount = CalibrateLoadMeter ( ) ;
Data->MaxCount = (ULONG) max ( 1L, Data->MaxCount ) ;

/***************************************************************************
* Start the new load meter. *
***************************************************************************/

DosCreateThread ( &Data->IdleLoopTID, CounterThread, (ULONG)&Data->IdleCounter, 0, 4096 ) ;
DosSetPrty ( PRTYS_THREAD, PRTYC_IDLETIME, PRTYD_MINIMUM, Data->IdleLoopTID ) ;
DosSuspendThread ( Data->IdleLoopTID ) ;

Data->IdleCount = 0 ;
Data->IdleCounter = 0 ;

if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
{
Log ( "ERROR: Unable to lock item list to check CPU load item status.\r\n" ) ;
DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
}
if ( Data->Profile.Items[ITEM_CPULOAD].Flag )
{
DosResumeThread ( Data->IdleLoopTID ) ;
}
DosReleaseMutexSem ( ItemSemaphore ) ;

PMONITOR_PARMS MonitorParms ;
MonitorParms = malloc ( sizeof(*MonitorParms) ) ;
MonitorParms->Counter = & Data->IdleCounter ;
MonitorParms->Interval = & Data->Profile.TimerInterval ;
MonitorParms->Owner = hwnd ;
DosCreateThread ( &Data->MonitorLoopTID, MonitorLoopThread, (ULONG)MonitorParms, 2, 8192 ) ;

/***************************************************************************
* Add the program to the system task list. *
***************************************************************************/

BYTE Title [80] ;
WinLoadString ( Data->Anchor, Data->Library, IDS_TITLE, sizeof(Title), Title ) ;
Add2TaskList ( hwndFrame, Title ) ;

/***************************************************************************
* Position & size the window. For some reason, we must move and size *
* the window to the saved position before applying the resizing *
* function as fine-tuning. Maybe the positioning request fails if *
* the window has no size? *
***************************************************************************/

WinSetWindowPos ( hwndFrame, HWND_BOTTOM,
Data->Profile.Position.x, Data->Profile.Position.y,
Data->Profile.Position.cx, Data->Profile.Position.cy,
SWP_SIZE | SWP_MOVE | SWP_ZORDER |
( Data->Profile.Position.fl & SWP_MINIMIZE ) |
( Data->Profile.Position.fl & SWP_RESTORE ) ) ;

ResizeWindow ( hwnd, &Data->Profile ) ;

/***************************************************************************
* Hide the controls if so configured. *
***************************************************************************/

if ( Data->Profile.HideControls
AND NOT ( Data->Profile.Position.fl & SWP_MINIMIZE ) )
{
CheckMenuItem ( hwndFrame, FID_SYSMENU, IDM_HIDE_CONTROLS, Data->Profile.HideControls ) ;

HideControls
(
TRUE,
hwndFrame,
Data->hwndSysMenu,
Data->hwndTitleBar,
Data->hwndMinMax
) ;
}

/***************************************************************************
* Get the saved presentation parameters and reinstate them. *
***************************************************************************/

if ( Data->Profile.fFontNameSize )
{
WinSetPresParam ( hwnd, PP_FONTNAMESIZE,
strlen((PCHAR)Data->Profile.FontNameSize)+1, Data->Profile.FontNameSize ) ;
}

if ( Data->Profile.fBackColor )
{
WinSetPresParam ( hwnd, PP_BACKGROUNDCOLOR,
sizeof(Data->Profile.BackColor), &Data->Profile.BackColor ) ;
}

if ( Data->Profile.fTextColor )
{
WinSetPresParam ( hwnd, PP_FOREGROUNDCOLOR,
sizeof(Data->Profile.TextColor), &Data->Profile.TextColor ) ;
}

/***************************************************************************
* Determine our font size. *
***************************************************************************/

HPS hPS = WinGetPS ( hwnd ) ;
RECTL Rectangle ;
WinQueryWindowRect ( HWND_DESKTOP, &Rectangle ) ;
WinDrawText ( hPS, 1, (PSZ)" ", &Rectangle, 0L, 0L, DT_LEFT | DT_BOTTOM | DT_QUERYEXTENT ) ;
Data->Width = Rectangle.xRight - Rectangle.xLeft ;
Data->Height = Rectangle.yTop - Rectangle.yBottom ;
WinReleasePS ( hPS ) ;

/***************************************************************************
* Now that the window's in order, make it visible. *
***************************************************************************/

WinShowWindow ( hwndFrame, TRUE ) ;

/***************************************************************************
* Success? Return no error. *
***************************************************************************/

return ( 0 ) ;
}

/****************************************************************************
* *
* Destroy main window. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY Destroy
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Release the instance memory. *
***************************************************************************/

free ( Data ) ;

/***************************************************************************
* We're done. *
***************************************************************************/

return ( MRFROMSHORT ( 0 ) ) ;
}

/****************************************************************************
* *
* Process window resize message. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY Size
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Find out the window's new position and size. *
***************************************************************************/

HWND hwndFrame = WinQueryWindow ( hwnd, QW_PARENT ) ;

SWP Position ;
WinQueryWindowPos ( hwndFrame, &Position ) ;

if ( NOT ( Position.fl & SWP_MINIMIZE )
AND NOT ( Position.fl & SWP_MAXIMIZE ) )
{
Data->Profile.Position.x = Position.x ;
Data->Profile.Position.y = Position.y ;

Data->Profile.Position.cx = Position.cx ;
Data->Profile.Position.cy = Position.cy ;
}

/***************************************************************************
* If hiding the controls . . . *
***************************************************************************/

if ( Data->Profile.HideControls )
{

/*************************************************************************
* If changing to or from minimized state . . . *
*************************************************************************/

if ( ( Position.fl & SWP_MINIMIZE ) != ( Data->Profile.Position.fl & SWP_MINIMIZE ) )
{

/***********************************************************************
* Hide the controls if no longer minimized. *
***********************************************************************/

HideControls
(
NOT ( Position.fl & SWP_MINIMIZE ),
hwndFrame,
Data->hwndSysMenu,
Data->hwndTitleBar,
Data->hwndMinMax
) ;
}
}

Data->Profile.Position.fl = Position.fl ;

/***************************************************************************
* We're done. *
***************************************************************************/

return ( 0 ) ;
}

/****************************************************************************
* *
* Process SAVE APPLICATION message. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY SaveApplication
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Call function to put all profile data out to the system. *
***************************************************************************/

PutProfile ( Data->ProfileHandle, &Data->Profile ) ;

/***************************************************************************
* We're done. Let the system complete default processing. *
***************************************************************************/

return ( WinDefWindowProc ( hwnd, WM_SAVEAPPLICATION, 0, 0 ) ) ;
}

/****************************************************************************
* *
* Repaint entire window. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY Paint
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Get presentation space and make it use RGB colors. *
***************************************************************************/

HPS hPS = WinBeginPaint ( hwnd, NULL, NULL ) ;
GpiCreateLogColorTable ( hPS, LCOL_RESET, LCOLF_RGB, 0L, 0L, NULL ) ;

/***************************************************************************
* Clear the window. *
***************************************************************************/

RECTL Rectangle ;
WinQueryWindowRect ( hwnd, &Rectangle ) ;

GpiMove ( hPS, (PPOINTL) &Rectangle.xLeft ) ;
GpiSetColor ( hPS, Data->Profile.BackColor ) ;
GpiBox ( hPS, DRO_FILL, (PPOINTL) &Rectangle.xRight, 0L, 0L ) ;

/***************************************************************************
* Release presentation space. *
***************************************************************************/

WinEndPaint ( hPS ) ;

/***************************************************************************
* Update the window and return. *
***************************************************************************/

UpdateWindow ( hwnd, Data, TRUE ) ;

return ( 0 ) ;
}

/****************************************************************************
* *
* Process commands received by Main Window *
* *
****************************************************************************/

STATIC MRESULT APIENTRY Command
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Process indicated command . . . *
***************************************************************************/

USHORT Command = SHORT1FROMMP ( mp1 ) ;

/***************************************************************************
* Process display item commands. *
***************************************************************************/

if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
{
Log ( "ERROR: Unable to lock item list to process display item commands.\r\n" ) ;
DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
}

for ( int i=0; iProfile.ItemCount; i++ )
{
PITEM Item = Data->Profile.Items + i ;

if ( Command == Item->MenuId )
{
HWND hwndFrame = WinQueryWindow ( hwnd, QW_PARENT ) ;

Item->Flag = Item->Flag ? FALSE : TRUE ;

if ( i == ITEM_CPULOAD )
{
if ( Item->Flag )
DosResumeThread ( Data->IdleLoopTID ) ;
else
DosSuspendThread ( Data->IdleLoopTID ) ;
}

CheckMenuItem ( hwndFrame, FID_SYSMENU, Item->MenuId, Item->Flag ) ;

DosReleaseMutexSem ( ItemSemaphore ) ;

ResizeWindow ( hwnd, &Data->Profile ) ;

return ( MRFROMSHORT ( 0 ) ) ;
}
}
DosReleaseMutexSem ( ItemSemaphore ) ;

/***************************************************************************
* Dispatch all other commands through the method table. *
***************************************************************************/

static METHOD Methods [] =
{
{ IDM_SAVE_APPLICATION, SaveApplication },
{ IDM_RESET_DEFAULTS, ResetDefaults },
{ IDM_HIDE_CONTROLS, HideControlsCmd },
{ IDM_SET_TIMER, SetTimer },
{ IDM_EXIT, Exit },
{ IDM_ABOUT, About },
} ;

return ( DispatchMessage ( hwnd, SHORT1FROMMP(mp1), mp1, mp2, Methods, sizeof(Methods)/sizeof(Methods[0]), NULL ) ) ;
}

/****************************************************************************
* *
* Process Reset Defaults menu command. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY ResetDefaults
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Reset all profile data for this program. *
***************************************************************************/

PrfWriteProfileData ( Data->ProfileHandle, (PSZ)PROGRAM_NAME, NULL, NULL, 0 ) ;

/***************************************************************************
* Reset the program's presentation parameters. *
***************************************************************************/

WinRemovePresParam ( hwnd, PP_FONTNAMESIZE ) ;
WinRemovePresParam ( hwnd, PP_FOREGROUNDCOLOR ) ;
WinRemovePresParam ( hwnd, PP_BACKGROUNDCOLOR ) ;

/***************************************************************************
* Done. *
***************************************************************************/

return ( MRFROMSHORT ( 0 ) ) ;
}

/****************************************************************************
* *
* Process Hide Controls menu command. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY HideControlsCmd
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Toggle the Hide Controls setting. *
***************************************************************************/

Data->Profile.HideControls = Data->Profile.HideControls ? FALSE : TRUE ;
Data->Profile.fHideControls = TRUE ;

/***************************************************************************
* Get the frame handle. *
***************************************************************************/

HWND hwndFrame = WinQueryWindow ( hwnd, QW_PARENT ) ;

/***************************************************************************
* If controls aren't hidden yet, update the menu check-mark. *
***************************************************************************/

if ( Data->Profile.HideControls )
CheckMenuItem ( hwndFrame, FID_SYSMENU, IDM_HIDE_CONTROLS, Data->Profile.HideControls ) ;

/***************************************************************************
* If not minimized right now, hide or reveal the controls. *
***************************************************************************/

if ( NOT ( Data->Profile.Position.fl & SWP_MINIMIZE ) )
{
HideControls
(
Data->Profile.HideControls,
hwndFrame,
Data->hwndSysMenu,
Data->hwndTitleBar,
Data->hwndMinMax
) ;
}

/***************************************************************************
* If controls are no longer hidden, update the menu check-mark. *
***************************************************************************/

if ( NOT Data->Profile.HideControls )
CheckMenuItem ( hwndFrame, FID_SYSMENU, IDM_HIDE_CONTROLS, Data->Profile.HideControls ) ;

/***************************************************************************
* Done. *
***************************************************************************/

return ( MRFROMSHORT ( 0 ) ) ;
}

/****************************************************************************
* *
* Process Set Timer menu command. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY SetTimer
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Invoke the Set Timer dialog. *
***************************************************************************/

SETTIMER_PARMS Parms ;
Parms.id = IDD_SET_TIMER ;
Parms.hwndHelp = WinQueryHelpInstance ( hwnd ) ;
Parms.TimerInterval = & Data->Profile.TimerInterval ;

WinDlgBox ( HWND_DESKTOP, hwnd, SetTimerProcessor,
Data->Library, IDD_SET_TIMER, &Parms ) ;

/***************************************************************************
* Done. *
***************************************************************************/

return ( MRFROMSHORT ( 0 ) ) ;
}

/****************************************************************************
* *
* Process About menu command. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY About
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Invoke the About dialog. *
***************************************************************************/

ABOUT_PARMS Parms ;
Parms.id = IDD_ABOUT ;
Parms.hwndHelp = WinQueryHelpInstance ( hwnd ) ;

WinDlgBox ( HWND_DESKTOP, hwnd, AboutProcessor,
Data->Library, IDD_ABOUT, &Parms ) ;

/***************************************************************************
* Done. *
***************************************************************************/

return ( MRFROMSHORT ( 0 ) ) ;
}

/****************************************************************************
* *
* Process Mouse Button being pressed. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY ButtonDown
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Determine the new window position. *
***************************************************************************/

TRACKINFO TrackInfo ;
memset ( &TrackInfo, 0, sizeof(TrackInfo) ) ;

TrackInfo.cxBorder = 1 ;
TrackInfo.cyBorder = 1 ;
TrackInfo.cxGrid = 1 ;
TrackInfo.cyGrid = 1 ;
TrackInfo.cxKeyboard = 8 ;
TrackInfo.cyKeyboard = 8 ;

HWND hwndFrame = WinQueryWindow ( hwnd, QW_PARENT ) ;

SWP Position ;
WinQueryWindowPos ( hwndFrame, &Position ) ;
TrackInfo.rclTrack.xLeft = Position.x ;
TrackInfo.rclTrack.xRight = Position.x + Position.cx ;
TrackInfo.rclTrack.yBottom = Position.y ;
TrackInfo.rclTrack.yTop = Position.y + Position.cy ;

WinQueryWindowPos ( HWND_DESKTOP, &Position ) ;
TrackInfo.rclBoundary.xLeft = Position.x ;
TrackInfo.rclBoundary.xRight = Position.x + Position.cx ;
TrackInfo.rclBoundary.yBottom = Position.y ;
TrackInfo.rclBoundary.yTop = Position.y + Position.cy ;

TrackInfo.ptlMinTrackSize.x = 0 ;
TrackInfo.ptlMinTrackSize.y = 0 ;
TrackInfo.ptlMaxTrackSize.x = Position.cx ;
TrackInfo.ptlMaxTrackSize.y = Position.cy ;

TrackInfo.fs = TF_MOVE | TF_STANDARD | TF_ALLINBOUNDARY ;

if ( WinTrackRect ( HWND_DESKTOP, NULL, &TrackInfo ) )
{
WinSetWindowPos ( hwndFrame, NULL,
(SHORT) TrackInfo.rclTrack.xLeft,
(SHORT) TrackInfo.rclTrack.yBottom,
0, 0, SWP_MOVE ) ;
}

/***************************************************************************
* Return through the default processor, letting window activation *
* and other system functions occur. *
***************************************************************************/

return ( WinDefWindowProc ( hwnd, msg, mp1, mp2 ) ) ;
}

/****************************************************************************
* *
* Process Mouse Button having been double-clicked. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY ButtonDblClick
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Send message to self to stop hiding the controls. *
***************************************************************************/

WinPostMsg ( hwnd, WM_COMMAND,
MPFROM2SHORT ( IDM_HIDE_CONTROLS, 0 ),
MPFROM2SHORT ( CMDSRC_OTHER, TRUE ) ) ;

/***************************************************************************
* Return through the default processor, letting window activation *
* and other system functions occur. *
***************************************************************************/

return ( WinDefWindowProc ( hwnd, msg, mp1, mp2 ) ) ;
}

/****************************************************************************
* *
* Process Presentation Parameter Changed notification. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY PresParamChanged
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Get the presentation parameter that changed. *
***************************************************************************/

switch ( LONGFROMMP(mp1) )
{

/*************************************************************************
* If font, note the fact that we now have a font to be saved as *
* part of the configuration. Get the font metrics and resize *
* the window appropriately. *
*************************************************************************/

case PP_FONTNAMESIZE:
{
ULONG ppid ;
if ( WinQueryPresParam ( hwnd, PP_FONTNAMESIZE, 0, &ppid,
sizeof(Data->Profile.FontNameSize), &Data->Profile.FontNameSize,
0 ) )
{
Data->Profile.fFontNameSize = TRUE ;
}
else
{
strcpy ( (PCHAR)Data->Profile.FontNameSize, "" ) ;
Data->Profile.fFontNameSize = FALSE ;
PrfWriteProfileData ( Data->ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"FontNameSize", NULL, 0 ) ;
}

HPS hPS = WinGetPS ( hwnd ) ;
RECTL Rectangle ;
WinQueryWindowRect ( HWND_DESKTOP, &Rectangle ) ;
WinDrawText ( hPS, 1, (PSZ)" ", &Rectangle, 0L, 0L, DT_LEFT | DT_BOTTOM | DT_QUERYEXTENT ) ;
Data->Width = Rectangle.xRight - Rectangle.xLeft ;
Data->Height = Rectangle.yTop - Rectangle.yBottom ;
WinReleasePS ( hPS ) ;
ResizeWindow ( hwnd, &Data->Profile ) ;
break ;
}

/*************************************************************************
* If background color, note the fact and repaint the window. *
*************************************************************************/

case PP_BACKGROUNDCOLOR:
{
ULONG ppid ;
if ( WinQueryPresParam ( hwnd, PP_BACKGROUNDCOLOR, 0, &ppid,
sizeof(Data->Profile.BackColor), &Data->Profile.BackColor, 0 ) )
{
Data->Profile.fBackColor = TRUE ;
}
else
{
Data->Profile.BackColor = WinQuerySysColor ( HWND_DESKTOP, SYSCLR_WINDOW, 0L ) ;
Data->Profile.fBackColor = FALSE ;
PrfWriteProfileData ( Data->ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"BackgroundColor", NULL, 0 ) ;
}
WinInvalidateRect ( hwnd, NULL, TRUE ) ;
break ;
}

/*************************************************************************
* If foreground color, note the fact and repaint the window. *
*************************************************************************/

case PP_FOREGROUNDCOLOR:
{
ULONG ppid ;
if ( WinQueryPresParam ( hwnd, PP_FOREGROUNDCOLOR, 0, &ppid,
sizeof(Data->Profile.TextColor), &Data->Profile.TextColor, 0 ) )
{
Data->Profile.fTextColor = TRUE ;
}
else
{
Data->Profile.TextColor = WinQuerySysColor ( HWND_DESKTOP, SYSCLR_OUTPUTTEXT, 0L ) ;
Data->Profile.fTextColor = FALSE ;
PrfWriteProfileData ( Data->ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"ForegroundColor", NULL, 0 ) ;
}
WinInvalidateRect ( hwnd, NULL, TRUE ) ;
break ;
}
}

/***************************************************************************
* Return through the default processor, letting window activation *
* and other system functions occur. *
***************************************************************************/

return ( WinDefWindowProc ( hwnd, msg, mp1, mp2 ) ) ;
}

/****************************************************************************
* *
* Process System Color Change notification. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY SysColorChange
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* If we aren't using custom colors, then query for the new defaults. *
***************************************************************************/

if ( NOT Data->Profile.fBackColor )
{
Data->Profile.BackColor = WinQuerySysColor ( HWND_DESKTOP, SYSCLR_WINDOW, 0L ) ;
}

if ( NOT Data->Profile.fTextColor )
{
Data->Profile.TextColor = WinQuerySysColor ( HWND_DESKTOP, SYSCLR_OUTPUTTEXT, 0L ) ;
}

/***************************************************************************
* Return value must be NULL, according to the documentation. *
***************************************************************************/

return ( MRFROMP ( NULL ) ) ;
}

/****************************************************************************
* *
* Process Query for Keys Help resource id. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY QueryKeysHelp
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Simply return the ID of the Keys Help panel. *
***************************************************************************/

return ( (MRESULT) IDM_KEYS_HELP ) ;
}

/****************************************************************************
* *
* Process Help Manager Error *
* *
****************************************************************************/

STATIC MRESULT APIENTRY HelpError
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Local Declarations *
***************************************************************************/

static struct
{
ULONG Error ;
USHORT StringId ;
}
HelpErrors [] =
{
{ HMERR_NO_FRAME_WND_IN_CHAIN, IDS_HMERR_NO_FRAME_WND_IN_CHAIN },
{ HMERR_INVALID_ASSOC_APP_WND, IDS_HMERR_INVALID_ASSOC_APP_WND },
{ HMERR_INVALID_ASSOC_HELP_INST, IDS_HMERR_INVALID_ASSOC_HELP_IN },
{ HMERR_INVALID_DESTROY_HELP_INST, IDS_HMERR_INVALID_DESTROY_HELP_ },
{ HMERR_NO_HELP_INST_IN_CHAIN, IDS_HMERR_NO_HELP_INST_IN_CHAIN },
{ HMERR_INVALID_HELP_INSTANCE_HDL, IDS_HMERR_INVALID_HELP_INSTANCE },
{ HMERR_INVALID_QUERY_APP_WND, IDS_HMERR_INVALID_QUERY_APP_WND },
{ HMERR_HELP_INST_CALLED_INVALID, IDS_HMERR_HELP_INST_CALLED_INVA },
{ HMERR_HELPTABLE_UNDEFINE, IDS_HMERR_HELPTABLE_UNDEFINE },
{ HMERR_HELP_INSTANCE_UNDEFINE, IDS_HMERR_HELP_INSTANCE_UNDEFIN },
{ HMERR_HELPITEM_NOT_FOUND, IDS_HMERR_HELPITEM_NOT_FOUND },
{ HMERR_INVALID_HELPSUBITEM_SIZE, IDS_HMERR_INVALID_HELPSUBITEM_S },
{ HMERR_HELPSUBITEM_NOT_FOUND, IDS_HMERR_HELPSUBITEM_NOT_FOUND },
{ HMERR_INDEX_NOT_FOUND, IDS_HMERR_INDEX_NOT_FOUND },
{ HMERR_CONTENT_NOT_FOUND, IDS_HMERR_CONTENT_NOT_FOUND },
{ HMERR_OPEN_LIB_FILE, IDS_HMERR_OPEN_LIB_FILE },
{ HMERR_READ_LIB_FILE, IDS_HMERR_READ_LIB_FILE },
{ HMERR_CLOSE_LIB_FILE, IDS_HMERR_CLOSE_LIB_FILE },
{ HMERR_INVALID_LIB_FILE, IDS_HMERR_INVALID_LIB_FILE },
{ HMERR_NO_MEMORY, IDS_HMERR_NO_MEMORY },
{ HMERR_ALLOCATE_SEGMENT, IDS_HMERR_ALLOCATE_SEGMENT },
{ HMERR_FREE_MEMORY, IDS_HMERR_FREE_MEMORY },
{ HMERR_PANEL_NOT_FOUND, IDS_HMERR_PANEL_NOT_FOUND },
{ HMERR_DATABASE_NOT_OPEN, IDS_HMERR_DATABASE_NOT_OPEN },
{ 0, IDS_HMERR_UNKNOWN }
} ;

ULONG ErrorCode = (ULONG) LONGFROMMP ( mp1 ) ;

/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Find the error code in the message table. *
***************************************************************************/

int Index = 0 ;
while ( HelpErrors[Index].Error
AND ( HelpErrors[Index].Error != ErrorCode ) )
{
Index ++ ;
}

/***************************************************************************
* Get the message texts. *
***************************************************************************/

BYTE Title [80] ;
WinLoadString ( Data->Anchor, Data->Library, IDS_HMERR, sizeof(Title), Title ) ;

BYTE Message [200] ;
WinLoadString ( Data->Anchor, Data->Library, HelpErrors[Index].StringId, sizeof(Message), Message ) ;

/***************************************************************************
* Display the error message. *
***************************************************************************/

WinMessageBox
(
HWND_DESKTOP,
hwnd,
Message,
Title,
0,
MB_OK | MB_WARNING
) ;

/***************************************************************************
* Return zero, indicating that the message was processed. *
***************************************************************************/

return ( MRFROMSHORT ( 0 ) ) ;
}

/****************************************************************************
* *
* Process "Extended Help Undefined" notification *
* *
****************************************************************************/

STATIC MRESULT APIENTRY ExtHelpUndefined
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Get the message texts. *
***************************************************************************/

BYTE Title [80] ;
WinLoadString ( Data->Anchor, Data->Library, IDS_HMERR, sizeof(Title), Title ) ;

BYTE Message [200] ;
WinLoadString ( Data->Anchor, Data->Library, IDS_HMERR_EXTHELPUNDEFINED, sizeof(Message), Message ) ;

/***************************************************************************
* Display the error message. *
***************************************************************************/

WinMessageBox
(
HWND_DESKTOP,
hwnd,
Message,
Title,
0,
MB_OK | MB_WARNING
) ;

/***************************************************************************
* Return zero, indicating that the message was processed. *
***************************************************************************/

return ( MRFROMSHORT ( 0 ) ) ;
}

/****************************************************************************
* *
* Process "Help Subitem Not Found" notification *
* *
****************************************************************************/

STATIC MRESULT APIENTRY HelpSubitemNotFound
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Get the title text. *
***************************************************************************/

BYTE Title [80] ;
WinLoadString ( Data->Anchor, Data->Library, IDS_HMERR, sizeof(Title), Title ) ;

/***************************************************************************
* Format the error message. *
***************************************************************************/

USHORT Topic = (USHORT) SHORT1FROMMP ( mp2 ) ;
USHORT Subtopic = (USHORT) SHORT2FROMMP ( mp2 ) ;

BYTE Mode [40] ;
switch ( SHORT1FROMMP ( mp1 ) )
{
case HLPM_FRAME:
WinLoadString ( Data->Anchor, Data->Library, IDS_HELPMODE_FRAME, sizeof(Mode), Mode ) ;
break ;

case HLPM_MENU:
WinLoadString ( Data->Anchor, Data->Library, IDS_HELPMODE_MENU, sizeof(Mode), Mode ) ;
break ;

case HLPM_WINDOW:
WinLoadString ( Data->Anchor, Data->Library, IDS_HELPMODE_WINDOW, sizeof(Mode), Mode ) ;
break ;

default:
WinLoadString ( Data->Anchor, Data->Library, IDS_HELPMODE_UNKNOWN, sizeof(Mode), Mode ) ;
}

BYTE Format [200] ;
WinLoadString ( Data->Anchor, Data->Library, IDS_HELPSUBITEMNOTFOUND, sizeof(Format), Format ) ;

BYTE Message [200] ;
sprintf ( (PCHAR)Message, (PCHAR)Format, Mode, Topic, Subtopic ) ;

/***************************************************************************
* Display the error message. *
***************************************************************************/

WinMessageBox
(
HWND_DESKTOP,
hwnd,
Message,
Title,
0,
MB_OK | MB_WARNING
) ;

/***************************************************************************
* Return zero, indicating that the message was processed. *
***************************************************************************/

return ( MRFROMSHORT ( 0 ) ) ;
}

/****************************************************************************
* *
* Process Refresh message. *
* *
****************************************************************************/

STATIC MRESULT APIENTRY Refresh
(
HWND hwnd,
USHORT msg,
MPARAM mp1,
MPARAM mp2
)
{
/***************************************************************************
* Find the instance data. *
***************************************************************************/

PDATA Data = (PDATA) WinQueryWindowPtr ( hwnd, QWL_USER ) ;

/***************************************************************************
* Save the idle counter. *
***************************************************************************/

Data->IdleCount = LONGFROMMP ( mp1 ) ;

/***************************************************************************
* Determine if drive mask has changed. *
***************************************************************************/

ULONG Drive ;
ULONG Drives ;
DosQueryCurrentDisk ( &Drive, &Drives ) ;

if ( Drives != Data->Drives )
{
/*************************************************************************
* It has. First save the display options. *
*************************************************************************/

SaveApplication ( hwnd, WM_SAVEAPPLICATION, 0, 0 ) ;

/*************************************************************************
* Next, update the drive item list. *
*************************************************************************/

UpdateDriveList ( Data->Anchor, Data->Library, Data->ProfileHandle,
&Data->Profile, Data->Drives, Drives ) ;

/*************************************************************************
* If the controls are hidden, hide the whole window and reveal the *
* controls. Otherwise the menu wouldn't get updated correctly. *
*************************************************************************/

if ( Data->Profile.HideControls )
{
WinShowWindow ( WinQueryWindow(hwnd,QW_PARENT), FALSE ) ;
HideControls
(
FALSE,
WinQueryWindow ( hwnd, QW_PARENT ),
Data->hwndSysMenu,
Data->hwndTitleBar,
Data->hwndMinMax
) ;
}

/*************************************************************************
* Update the menu. *
*************************************************************************/

RebuildDisplayItems ( hwnd, Data ) ;

/*************************************************************************
* If the controls were supposed to be hidden, hide them once more and *
* show the window to the world again. *
*************************************************************************/

if ( Data->Profile.HideControls )
{
HideControls
(
TRUE,
WinQueryWindow ( hwnd, QW_PARENT ),
Data->hwndSysMenu,
Data->hwndTitleBar,
Data->hwndMinMax
) ;
WinShowWindow ( WinQueryWindow(hwnd,QW_PARENT), TRUE ) ;
}

/*************************************************************************
* Save the updated drive mask. *
*************************************************************************/

Data->Drives = Drives ;

/*************************************************************************
* Resize the window to accommodate the new option list. *
*************************************************************************/

ResizeWindow ( hwnd, &Data->Profile ) ;
}

/***************************************************************************
* Update the statistics. *
***************************************************************************/

UpdateWindow ( hwnd, Data, FALSE ) ;

/***************************************************************************
* Return zero, indicating that the message was processed. *
***************************************************************************/

return ( MRFROMSHORT ( 0 ) ) ;
}


/****************************************************************************
* *
* Get Profile Data *
* *
****************************************************************************/

STATIC int GetProfile ( HAB Anchor, HMODULE Library, HINI ProfileHandle, PPROFILE Profile )
{
/***************************************************************************
* Get the window's current size and position. *
***************************************************************************/

#pragma pack(2)
typedef struct {
USHORT Filler ;
USHORT fs ;
USHORT cy, cx, y, x ;
HWND hwndInsertBehind ;
HWND hwnd ;
} OLDSWP ;
#pragma pack()

ULONG Size ;
memset ( &Profile->Position, 0, sizeof(Profile->Position) ) ;
Profile->fPosition = FALSE ;
if ( PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"Position", &Size ) )
{
if ( Size == sizeof(OLDSWP)-sizeof(USHORT) )
{
OLDSWP OldPosition ;
if ( PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"Position", &OldPosition.fs, &Size ) )
{
Profile->Position.fl = OldPosition.fs ;
Profile->Position.cy = OldPosition.cy ;
Profile->Position.cx = OldPosition.cx ;
Profile->Position.y = OldPosition.y ;
Profile->Position.x = OldPosition.x ;
Profile->Position.hwndInsertBehind = OldPosition.hwndInsertBehind ;
Profile->Position.hwnd = OldPosition.hwnd ;
Profile->fPosition = TRUE ;
}
}
else if ( Size == sizeof(Profile->Position) )
{
if ( PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"Position", &Profile->Position, &Size ) )
{
Profile->fPosition = TRUE ;
}
}
}

if ( NOT Profile->fPosition )
{
if ( ProfileHandle == HINI_USERPROFILE )
{
return ( 1 ) ;
}
}

/***************************************************************************
* Get the program options. *
***************************************************************************/

Profile->HideControls = FALSE ;
Profile->fHideControls = FALSE ;
if
(
PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"HideControls", &Size )
AND
( ( Size == sizeof(Profile->HideControls) ) OR ( Size == sizeof(short) ) )
AND
PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"HideControls", &Profile->HideControls, &Size )
)
{
Profile->fHideControls = TRUE ;
}

Profile->TimerInterval = 1000 ;
Profile->fTimerInterval = FALSE ;
if
(
PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"TimerInterval", &Size )
AND
( ( Size == sizeof(Profile->TimerInterval) ) OR ( Size == sizeof(short) ) )
AND
PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"TimerInterval", &Profile->TimerInterval, &Size )
)
{
Profile->fTimerInterval = TRUE ;
}

/***************************************************************************
* Get the presentation parameters. *
***************************************************************************/

strcpy ( (PCHAR)Profile->FontNameSize, "" ) ;
Profile->fFontNameSize = FALSE ;
if
(
PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"FontNameSize", &Size )
AND
( Size == sizeof(Profile->FontNameSize) )
AND
PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"FontNameSize", &Profile->FontNameSize, &Size )
)
{
Profile->fFontNameSize = TRUE ;
}

Profile->BackColor = WinQuerySysColor ( HWND_DESKTOP, SYSCLR_WINDOW, 0L ) ;
Profile->fBackColor = FALSE ;
if
(
PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"BackgroundColor", &Size )
AND
( Size == sizeof(Profile->BackColor) )
AND
PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"BackgroundColor", &Profile->BackColor, &Size )
)
{
Profile->fBackColor = TRUE ;
}

Profile->TextColor = WinQuerySysColor ( HWND_DESKTOP, SYSCLR_OUTPUTTEXT, 0L ) ;
Profile->fTextColor = FALSE ;
if
(
PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"ForegroundColor", &Size )
AND
( Size == sizeof(Profile->TextColor) )
AND
PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, (PSZ)"ForegroundColor", &Profile->TextColor, &Size )
)
{
Profile->fTextColor = TRUE ;
}

/***************************************************************************
* Lock the item list. *
***************************************************************************/

if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
{
Log ( "ERROR: Unable to lock item list to build it.\r\n" ) ;
DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
}

/***************************************************************************
* Build the fixed portion of the item list. *
***************************************************************************/

for ( int i=0; i {
WinLoadString ( Anchor, Library, i*2+IDS_SHOW_CLOCK_LABEL,
sizeof(Items[i].Label), Items[i].Label ) ;

WinLoadString ( Anchor, Library, i*2+IDS_SHOW_CLOCK_OPTION,
sizeof(Items[i].MenuOption), Items[i].MenuOption ) ;

Items[i].Flag = TRUE ;
if
(
PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, Items[i].Name, &Size )
AND
( ( Size == sizeof(Profile->HideControls) ) OR ( Size == sizeof(short) ) )
AND
PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, Items[i].Name, &Items[i].Flag, &Size )
)
{
;
}
}

/***************************************************************************
* Release the item list. *
***************************************************************************/

DosReleaseMutexSem ( ItemSemaphore ) ;

/***************************************************************************
* Add items for each drive on the system. *
***************************************************************************/

ULONG Drive, Drives ;
DosQueryCurrentDisk ( &Drive, &Drives ) ;
UpdateDriveList ( Anchor, Library, ProfileHandle, Profile, 0, Drives ) ;

return ( 0 ) ;
}

/****************************************************************************
* *
* Put Profile Data *
* *
****************************************************************************/

STATIC void PutProfile ( HINI ProfileHandle, PPROFILE Profile )
{
/***************************************************************************
* Save the window's current size and position. *
***************************************************************************/

PrfWriteProfileData
(
ProfileHandle,
(PSZ)PROGRAM_NAME,
(PSZ)"Position",
&Profile->Position,
sizeof(Profile->Position)
) ;

/***************************************************************************
* Save the program options. *
***************************************************************************/

if ( Profile->fHideControls )
{
PrfWriteProfileData
(
ProfileHandle,
(PSZ)PROGRAM_NAME,
(PSZ)"HideControls",
&Profile->HideControls,
sizeof(Profile->HideControls)

) ;
}

if ( Profile->fTimerInterval )
{
PrfWriteProfileData
(
ProfileHandle,
(PSZ)PROGRAM_NAME,
(PSZ)"TimerInterval",
&Profile->TimerInterval,
sizeof(Profile->TimerInterval)
) ;
}

/***************************************************************************
* Save the item options. *
***************************************************************************/

if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
{
Log ( "ERROR: Unable to lock item list to save display options.\r\n" ) ;
DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
}

for ( int i=0; iItemCount; i++ )
{
PITEM Item = Profile->Items + i ;

PrfWriteProfileData
(
ProfileHandle,
(PSZ)PROGRAM_NAME,
Item->Name,
&Item->Flag,
sizeof(Item->Flag)
) ;
}

DosReleaseMutexSem ( ItemSemaphore ) ;

/***************************************************************************
* Save the presentation parameters. *
***************************************************************************/

if ( Profile->fFontNameSize )
{
PrfWriteProfileData
(
ProfileHandle,
(PSZ)PROGRAM_NAME,
(PSZ)"FontNameSize",
Profile->FontNameSize,
sizeof(Profile->FontNameSize)
) ;
}

if ( Profile->fBackColor )
{
PrfWriteProfileData
(
ProfileHandle,
(PSZ)PROGRAM_NAME,
(PSZ)"BackgroundColor",
&Profile->BackColor,
sizeof(Profile->BackColor)
) ;
}

if ( Profile->fTextColor )
{
PrfWriteProfileData
(
ProfileHandle,
(PSZ)PROGRAM_NAME,
(PSZ)"ForegroundColor",
&Profile->TextColor,
sizeof(Profile->TextColor)
) ;
}
}

/****************************************************************************
* *
* Scan CONFIG.SYS for a keyword. Return the value. *
* *
****************************************************************************/

STATIC PSZ ScanSystemConfig ( HAB Anchor, PSZ Keyword )
{
/***************************************************************************
* Get the boot drive number from the global information segment. *
***************************************************************************/

ULONG BootDrive ;
DosQuerySysInfo ( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &BootDrive, sizeof(BootDrive) ) ;

/***************************************************************************
* Convert the keyword to upper case. *
***************************************************************************/

WinUpper ( Anchor, NULL, NULL, Keyword ) ;

/***************************************************************************
* Build the CONFIG.SYS path. *
***************************************************************************/

char Path [_MAX_PATH] ;
Path[0] = (char) ( BootDrive + 'A' - 1 ) ;
Path[1] = 0 ;
strcat ( Path, ":\\CONFIG.SYS" ) ;

/***************************************************************************
* Open CONFIG.SYS for reading. *
***************************************************************************/

FILE *File = fopen ( Path, "rt" ) ;
if ( NOT File )
{
return ( NULL ) ;
}

/***************************************************************************
* While there're more lines in CONFIG.SYS, read a line and check it. *
***************************************************************************/

static char Buffer [500] ;
while ( fgets ( Buffer, sizeof(Buffer), File ) )
{

/*************************************************************************
* Clean any trailing newline character from the input string. *
*************************************************************************/

if ( Buffer[strlen(Buffer)-1] == '\n' )
{
Buffer[strlen(Buffer)-1] = 0 ;
}

/*************************************************************************
* If keyword starts the line, we've found the line we want. Close *
* the file and return a pointer to the parameter text. *
*************************************************************************/

WinUpper ( Anchor, NULL, NULL, (PSZ)Buffer ) ;

if ( NOT strncmp ( Buffer, (PCHAR)Keyword, strlen((PCHAR)Keyword) )
AND ( Buffer[strlen((PCHAR)Keyword)] == '=' ) )
{
fclose ( File ) ;
return ( (PSZ) ( Buffer + strlen((PCHAR)Keyword) + 1 ) ) ;
}
}

/***************************************************************************
* Close the file. We haven't found the line we wanted. *
***************************************************************************/

fclose ( File ) ;

return ( NULL ) ;
}

/****************************************************************************
* *
* Resize Client Window *
* *
****************************************************************************/

STATIC void ResizeWindow ( HWND hwnd, PPROFILE Profile )
{
/***************************************************************************
* If the window is visible and minimized, restore it invisibly. *
***************************************************************************/

HWND hwndFrame = WinQueryWindow ( hwnd, QW_PARENT ) ;

SHORT fHadToHide = FALSE ;
SHORT fHadToRestore = FALSE ;
if ( Profile->Position.fl & SWP_MINIMIZE )
{
if ( WinIsWindowVisible ( hwndFrame ) )
{
WinShowWindow ( hwndFrame, FALSE ) ;
fHadToHide = TRUE ;
}
WinSetWindowPos ( hwndFrame, NULL, 0, 0, 0, 0, SWP_RESTORE ) ;
fHadToRestore = TRUE ;
}

/***************************************************************************
* Determine how many items are to be displayed. *
***************************************************************************/

if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
{
Log ( "ERROR: Unable to lock item list to determine window size.\r\n" ) ;
DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
}

HPS hPS = WinGetPS ( hwnd ) ;

int Count = 0 ;
LONG Widest = 0 ;
LONG Height = 0 ;

for ( int i=0; iItemCount; i++ )
{
PITEM Item = Profile->Items + i ;

if ( Item->Flag )
{
Count ++ ;

BYTE Text [100] ;
sprintf ( (PCHAR)Text, "%s 1,234,567K", Item->Label ) ;

RECTL Rectangle ;
WinQueryWindowRect ( HWND_DESKTOP, &Rectangle ) ;

WinDrawText ( hPS, strlen((PCHAR)Text), Text,
&Rectangle, 0L, 0L, DT_LEFT | DT_BOTTOM | DT_QUERYEXTENT ) ;

Widest = max ( Widest, (Rectangle.xRight-Rectangle.xLeft+1) ) ;

Height += Rectangle.yTop - Rectangle.yBottom ;
}
}

WinReleasePS ( hPS ) ;

DosReleaseMutexSem ( ItemSemaphore ) ;

/***************************************************************************
* Get the window's current size & position. *
***************************************************************************/

RECTL Rectangle ;
WinQueryWindowRect ( hwndFrame, &Rectangle ) ;

WinCalcFrameRect ( hwndFrame, &Rectangle, TRUE ) ;

/***************************************************************************
* Adjust the window's width & height. *
***************************************************************************/

Rectangle.xRight = Rectangle.xLeft + Widest ;

Rectangle.yTop = Rectangle.yBottom + Height ;

/***************************************************************************
* Compute new frame size and apply it. *
***************************************************************************/

WinCalcFrameRect ( hwndFrame, &Rectangle, FALSE ) ;

WinSetWindowPos ( hwndFrame, NULL, 0, 0,
(SHORT) (Rectangle.xRight-Rectangle.xLeft),
(SHORT) (Rectangle.yTop-Rectangle.yBottom),
SWP_SIZE ) ;

/***************************************************************************
* Return the window to its original state. *
***************************************************************************/

if ( fHadToRestore )
{
WinSetWindowPos ( hwndFrame, NULL,
Profile->Position.x, Profile->Position.y,
Profile->Position.cx, Profile->Position.cy,
SWP_MOVE | SWP_SIZE | SWP_MINIMIZE ) ;
}

if ( fHadToHide )
{
WinShowWindow ( hwndFrame, TRUE ) ;
}

/***************************************************************************
* Invalidate the window so that it gets repainted. *
***************************************************************************/

WinInvalidateRect ( hwnd, NULL, TRUE ) ;
}

/****************************************************************************
* *
* Hide Window Controls *
* *
****************************************************************************/

STATIC void HideControls
(
BOOL fHide,
HWND hwndFrame,
HWND hwndSysMenu,
HWND hwndTitleBar,
HWND hwndMinMax
)
{
/***************************************************************************
* Get original window position and state. *
***************************************************************************/

SWP OldPosition ;
WinQueryWindowPos ( hwndFrame, &OldPosition ) ;

BOOL WasVisible = WinIsWindowVisible ( hwndFrame ) ;

/***************************************************************************
* Restore and hide the window. *
***************************************************************************/

WinSetWindowPos ( hwndFrame, NULL, 0, 0, 0, 0, SWP_RESTORE | SWP_HIDE ) ;

/***************************************************************************
* Determine client window and location. *
***************************************************************************/

SWP Position ;
WinQueryWindowPos ( hwndFrame, &Position ) ;

RECTL Rectangle ;
Rectangle.xLeft = Position.x ;
Rectangle.xRight = Position.x + Position.cx ;
Rectangle.yBottom = Position.y ;
Rectangle.yTop = Position.y + Position.cy ;

WinCalcFrameRect ( hwndFrame, &Rectangle, TRUE ) ;

/***************************************************************************
* Hide or reveal the controls windows by changing their parentage. *
***************************************************************************/

if ( fHide )
{
WinSetParent ( hwndSysMenu, HWND_OBJECT, FALSE ) ;
WinSetParent ( hwndTitleBar, HWND_OBJECT, FALSE ) ;
WinSetParent ( hwndMinMax, HWND_OBJECT, FALSE ) ;
}
else
{
WinSetParent ( hwndSysMenu, hwndFrame, TRUE ) ;
WinSetParent ( hwndTitleBar, hwndFrame, TRUE ) ;
WinSetParent ( hwndMinMax, hwndFrame, TRUE ) ;
}

/***************************************************************************
* Tell the frame that things have changed. Let it update the window. *
***************************************************************************/

WinSendMsg ( hwndFrame, WM_UPDATEFRAME,
MPFROMSHORT ( FCF_TITLEBAR | FCF_SYSMENU | FCF_MINBUTTON ), 0L ) ;

/***************************************************************************
* Reposition the frame around the client window, which is left be. *
***************************************************************************/

WinCalcFrameRect ( hwndFrame, &Rectangle, FALSE ) ;

WinSetWindowPos ( hwndFrame, NULL,
(SHORT) Rectangle.xLeft, (SHORT) Rectangle.yBottom,
(SHORT) (Rectangle.xRight-Rectangle.xLeft),
(SHORT) (Rectangle.yTop-Rectangle.yBottom),
SWP_SIZE | SWP_MOVE ) ;

/***************************************************************************
* If window was maximized, put it back that way. *
***************************************************************************/

if ( OldPosition.fl & SWP_MAXIMIZE )
{
WinSetWindowPos ( hwndFrame, NULL,
(SHORT) Rectangle.xLeft, (SHORT) Rectangle.yBottom,
(SHORT) (Rectangle.xRight-Rectangle.xLeft),
(SHORT) (Rectangle.yTop-Rectangle.yBottom),
SWP_SIZE | SWP_MOVE |
( OldPosition.fl & SWP_MAXIMIZE ) ) ;
}

/***************************************************************************
* If the window was visible in the first place, show it. *
***************************************************************************/

if ( WasVisible )
{
WinShowWindow ( hwndFrame, TRUE ) ;
}
}

/****************************************************************************
* *
* Update Window *
* *
****************************************************************************/

STATIC void UpdateWindow ( HWND hwnd, PDATA Data, BOOL All )
{
/***************************************************************************
* Lock the item list. *
***************************************************************************/

if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
{
Log ( "ERROR: Unable to lock item list to update window contents.\r\n" ) ;
DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
}

/***************************************************************************
* Determine how many items are to be displayed. *
***************************************************************************/

int Count = 0 ;
for ( int i=0; iProfile.ItemCount; i++ )
{
if ( Data->Profile.Items[i].Flag )
{
Count ++ ;
}
}

/***************************************************************************
* Get presentation space and make it use RGB colors. *
***************************************************************************/

HPS hPS = WinGetPS ( hwnd ) ;
GpiCreateLogColorTable ( hPS, LCOL_RESET, LCOLF_RGB, 0L, 0L, NULL ) ;

/***************************************************************************
* Get the window's size and determine the initial position. *
***************************************************************************/

RECTL Rectangle ;
WinQueryWindowRect ( hwnd, &Rectangle ) ;

Rectangle.xLeft += Data->Width / 2 ;
Rectangle.xRight -= Data->Width / 2 ;

Rectangle.yBottom = Data->Height * ( Count - 1 ) ;
Rectangle.yTop = Rectangle.yBottom + Data->Height ;

/***************************************************************************
* Review all items. Display those changed, or all. *
***************************************************************************/

for ( i=0; iProfile.ItemCount; i++ )
{
ULONG NewValue ;

PITEM Item = Data->Profile.Items + i ;

if ( Item->Flag )
{
if ( NOT Item->Error )
{
NewValue = Item->NewValue ( Data, Item->Parm ) ;
}
else
{
NewValue = DRIVE_ERROR ;
}

if ( All OR ( NewValue != Item->Value ) )
{
BYTE Text [100] ;
switch ( i )
{
case ITEM_CLOCK:
{
ULONG Month = ( NewValue % 100000000L ) / 1000000L ;
ULONG Day = ( NewValue % 1000000L ) / 10000L ;
ULONG Hour = ( NewValue % 10000L ) / 100L ;
ULONG Minute = ( NewValue % 100L ) ;

switch ( Data->CountryInfo.fsDateFmt )
{
case DATEFMT_DD_MM_YY:
sprintf ( (PCHAR)Text, "%02lu%s%02lu ",
Day, Data->CountryInfo.szDateSeparator, Month ) ;
break ;

case DATEFMT_YY_MM_DD:
case DATEFMT_MM_DD_YY:
default:
sprintf ( (PCHAR)Text, "%02lu%s%02lu ",
Month, Data->CountryInfo.szDateSeparator, Day ) ;
break ;
}

if ( Data->CountryInfo.fsTimeFmt )
{
sprintf ( (PCHAR)(Text+strlen((PCHAR)Text)), "%02lu%s%02lu",
Hour,
Data->CountryInfo.szTimeSeparator,
Minute ) ;
}
else
{
PCHAR AmPm ;

if ( Hour )
{
if ( Hour < 12 )
{
AmPm = "a" ;
}
else if ( Hour == 12 )
{
if ( Minute )
AmPm = "p" ;
else
AmPm = "a" ;
}
else if ( Hour > 12 )
{
Hour -= 12 ;
AmPm = "p" ;
}
}
else
{
Hour = 12 ;
if ( Minute )
AmPm = "a" ;
else
AmPm = "p" ;
}
sprintf ( (PCHAR)(Text+strlen((PCHAR)Text)), "%02lu%s%02lu%s",
Hour, Data->CountryInfo.szTimeSeparator, Minute, AmPm ) ;
}
break ;
}

case ITEM_ELAPSEDTIME:
{
memset ( Text, 0, sizeof(Text) ) ;

ULONG Days = NewValue / ( 60L * 24L ) ;

if ( Days )
{
sprintf ( PCHAR(Text), "%lu %s, ",
Days, Days > 1 ? Data->Days : Data->Day ) ;
}

ULONG Minutes = NewValue % ( 60L * 24L ) ;

sprintf ( PCHAR(Text+strlen(PCHAR(Text))), "%lu%s%02lu",
Minutes/60, Data->CountryInfo.szTimeSeparator, Minutes%60 ) ;

break ;
}

default:
{
memset ( Text, 0, sizeof(Text) ) ;

if ( NewValue == DRIVE_ERROR )
{
Item->Error = TRUE ;
strcpy ( PCHAR(Text), PCHAR(Data->DriveError) ) ;
}
else
{
if ( Item->Divisor )
{
if ( NewValue < ( Item->Divisor * 1024 ) / 2 )
sprintf ( (PCHAR)Text, "%lu", NewValue ) ;
else
sprintf ( (PCHAR)Text, "%lu", (NewValue+Item->Divisor/2)/Item->Divisor ) ;
}
else
{
sprintf ( (PCHAR)Text, "%lu", NewValue ) ;
}

{
PBYTE p1, p2 ;
BYTE Work[100] ;

p1 = Text ;
p2 = Work ;
while ( *p1 )
{
*p2 = *p1 ;
p1 ++ ;
p2 ++ ;
if ( *p1 )
{
if ( strlen((PCHAR)p1) % 3 == 0 )
{
*p2 = Data->CountryInfo.szThousandsSeparator [0] ;
p2 ++ ;
}
}
}
*p2 = 0 ;
strcpy ( (PCHAR)Text, (PCHAR)Work ) ;
}

if ( Item->Divisor )
{
if ( NewValue < ( Item->Divisor * 1024 ) / 2 )
Text[strlen((PCHAR)Text)] = ' ' ;
else
Text[strlen((PCHAR)Text)] = Item->Suffix ;
}
else
{
Text[strlen((PCHAR)Text)] = Item->Suffix ;
}
}
}
}

WinDrawText ( hPS, strlen((PCHAR)Text), Text, &Rectangle,
Data->Profile.TextColor, Data->Profile.BackColor,
DT_RIGHT | DT_BOTTOM | DT_ERASERECT ) ;

WinDrawText ( hPS, strlen((PCHAR)Item->Label), Item->Label, &Rectangle,
Data->Profile.TextColor, Data->Profile.BackColor,
DT_LEFT | DT_BOTTOM ) ;

Item->Value = NewValue ;
}
Rectangle.yBottom -= Data->Height ;
Rectangle.yTop -= Data->Height ;
}
}

/***************************************************************************
* Release the presentation space and return. *
***************************************************************************/

WinReleasePS ( hPS ) ;

/***************************************************************************
* Release the item list. *
***************************************************************************/

DosReleaseMutexSem ( ItemSemaphore ) ;
}


/****************************************************************************
* *
* Compute Time *
* *
****************************************************************************/

STATIC ULONG ComputeTime ( PVOID Data, USHORT Dummy )
{
DATETIME DateTime ;
DosGetDateTime ( &DateTime ) ;

ULONG Time ;
Time = DateTime.month ;
Time *= 100 ;
Time += DateTime.day ;
Time *= 100 ;
Time += DateTime.hours ;
Time *= 100 ;
Time += DateTime.minutes ;

return ( Time ) ;
}

/****************************************************************************
* *
* Compute Elapsed Time *
* *
****************************************************************************/

STATIC ULONG ComputeElapsed ( PVOID Data, USHORT Dummy )
{
ULONG Milliseconds ;
DosQuerySysInfo ( QSV_MS_COUNT, QSV_MS_COUNT, &Milliseconds, sizeof(Milliseconds) ) ;
return ( Milliseconds / 60000L ) ;
}

/****************************************************************************
* *
* Compute Available Memory *
* *
****************************************************************************/

STATIC ULONG ComputeFreeMemory ( PVOID Data, USHORT Dummy )
{
ULONG VirtualMemory ;
DosQuerySysInfo ( QSV_TOTAVAILMEM, QSV_TOTAVAILMEM, &VirtualMemory, sizeof(VirtualMemory) ) ;

ULONG SwapFree = ComputeSwapFree ( Data, Dummy ) ;

LONG Space = LONG(VirtualMemory) - LONG(SwapFree) ;
while ( Space < 0 )
{
Space += 0x100000 ;
}

return ( ULONG(Space) ) ;
}

/****************************************************************************
* *
* Compute Swap-File Size *
* *
****************************************************************************/

STATIC ULONG ComputeSwapSize ( PVOID Data, USHORT Dummy )
{
/***************************************************************************
* Find the swap file. *
***************************************************************************/

char Path [_MAX_PATH+1] ;
strcpy ( Path, (PCHAR)((PDATA)Data)->SwapPath ) ;

if ( Path[strlen(Path)-1] != '\\' )
{
strcat ( Path, "\\" ) ;
}

strcat ( Path, "SWAPPER.DAT" ) ;

/***************************************************************************
* Determine its size. *
***************************************************************************/

ULONG SwapSize = 0 ;
FILESTATUS3 Status ;
if ( DosQueryPathInfo ( (PSZ)Path, FIL_STANDARD, &Status, sizeof(Status) ) == 0 )
{
SwapSize = Status.cbFileAlloc ;
}

return ( SwapSize ) ;
}

/****************************************************************************
* *
* Compute Available Swap Space *
* *
****************************************************************************/

STATIC ULONG ComputeSwapFree ( PVOID Data, USHORT Dummy )
{
/***************************************************************************
* Find the swap file and find its size. *
***************************************************************************/

char Path [_MAX_PATH+1] ;
strcpy ( Path, (PCHAR)((PDATA)Data)->SwapPath ) ;
strcat ( Path, "\\SWAPPER.DAT" ) ;

/***************************************************************************
* Compute swap device free space. *
***************************************************************************/

ULONG SwapFree = 0 ;
if ( Path[0] )
{
DosError ( FERR_DISABLEHARDERR ) ;
FSALLOCATE Allocation ;
DosQueryFSInfo ( Path[0]-'A'+1, FSIL_ALLOC,
(PBYTE)&Allocation, sizeof(Allocation) ) ;
DosError ( FERR_ENABLEHARDERR ) ;

SwapFree = Allocation.cUnitAvail*Allocation.cSectorUnit*Allocation.cbSector ;
}

/***************************************************************************
* Return swap device's free space, less the minimum free space. *
***************************************************************************/

if ( SwapFree < ULONG(PDATA(Data)->MinFree*1024L) )
return ( 0L ) ;
else
return ( SwapFree - ULONG(PDATA(Data)->MinFree*1024L) ) ;
}

/****************************************************************************
* *
* Compute Spool-file Size *
* *
****************************************************************************/

STATIC ULONG ComputeSpoolSize ( PVOID Data, USHORT Dummy )
{
/***************************************************************************
* Build file specifier for the spool directory. *
***************************************************************************/

ULONG PathSize ;
DosQuerySysInfo ( QSV_MAX_PATH_LENGTH, QSV_MAX_PATH_LENGTH, &PathSize, sizeof(PathSize) ) ;

PBYTE Path = malloc ( PathSize ) ;
if ( Path == NULL )
{

Log ( "ERROR: Unable to allocate memory for spool-file search path.\r\n" ) ;
return ( 0 ) ;
}

PFILEFINDBUF3 Found = malloc ( PathSize + sizeof(FILEFINDBUF3) ) ;
if ( Found == NULL )
{
Log ( "ERROR: Unable to allocate memory for spool-file search result structure.\r\n" ) ;
free ( Path ) ;
return ( 0 ) ;
}

strcpy ( (PCHAR)Path, (PCHAR)((PDATA)Data)->SpoolPath ) ;
strcat ( (PCHAR)Path, "\\*.*" ) ;

/***************************************************************************
* If there are any files/directories in the spool directory . . . *
***************************************************************************/

HDIR hDir = (HDIR) HDIR_CREATE ;
ULONG Count = 1 ;
ULONG TotalSize = 0 ;

if ( !DosFindFirst2 ( Path, &hDir,
FILE_NORMAL | FILE_READONLY | FILE_DIRECTORY | FILE_ARCHIVED,
Found, PathSize+sizeof(FILEFINDBUF3), &Count, FIL_STANDARD ) )
{

/*************************************************************************
* Loop through every entry in the spool directory. *
*************************************************************************/

do
{

/***********************************************************************
* Ignore the parent and current directory entries. *
***********************************************************************/

if ( !strcmp ( (PCHAR)Found->achName, "." )
OR !strcmp ( (PCHAR)Found->achName, ".." ) )
{
continue ;
}

/***********************************************************************
* If the entry is a subdirectory . . . *
***********************************************************************/

if ( Found->attrFile & FILE_DIRECTORY )
{

/*********************************************************************
* Scan the subdirectory and add every file's size to the total. *
*********************************************************************/

HDIR hDir = (HDIR) HDIR_CREATE ;

strcpy ( (PCHAR)Path, (PCHAR)((PDATA)Data)->SpoolPath ) ;
strcat ( (PCHAR)Path, "\\" ) ;
strcat ( (PCHAR)Path, (PCHAR)Found->achName ) ;
strcat ( (PCHAR)Path, "\\*.*" ) ;

Count = 1 ;
if ( !DosFindFirst2 ( Path, &hDir,
FILE_NORMAL | FILE_READONLY | FILE_ARCHIVED,
Found, PathSize+sizeof(FILEFINDBUF3), &Count, FIL_STANDARD ) )
{
do
{
TotalSize += Found->cbFileAlloc ;
}
while ( !DosFindNext ( hDir, Found, PathSize+sizeof(FILEFINDBUF3), &Count ) ) ;
DosFindClose ( hDir ) ;
}

Count = 1 ;
}

/***********************************************************************
* Else if it was a file, add its size to the total. *
***********************************************************************/

else
{
TotalSize += Found->cbFileAlloc ;
}
}
while ( !DosFindNext ( hDir, Found, PathSize+sizeof(FILEFINDBUF3), &Count ) ) ;

/*************************************************************************
* Close the directory scan. *
*************************************************************************/

DosFindClose ( hDir ) ;
}

free ( Path ) ;
free ( Found ) ;

return ( TotalSize ) ;
}

/****************************************************************************
* *
* Compute CPU Load *
* *
****************************************************************************/

STATIC ULONG ComputeCpuLoad ( PVOID Data, USHORT Dummy )
{
((PDATA)Data)->MaxCount = (ULONG) max ( ((PDATA)Data)->MaxCount, ((PDATA)Data)->IdleCount ) ;

ULONG Load = ( ( ((PDATA)Data)->MaxCount - ((PDATA)Data)->IdleCount ) * 100 ) / ((PDATA)Data)->MaxCount ;

return ( Load ) ;
}

/****************************************************************************
* *
* Compute Active Task Count *
* *
****************************************************************************/

STATIC ULONG ComputeTaskCount ( PVOID Data, USHORT Dummy )
{
return ( WinQuerySwitchList ( ((PDATA)Data)->Anchor, NULL, 0 ) ) ;
}

/****************************************************************************
* *
* Compute Drive Free Space *
* *
****************************************************************************/

STATIC ULONG ComputeDriveFree ( PVOID Data, USHORT Drive )
{
DosError ( FERR_DISABLEHARDERR ) ;
FSALLOCATE Allocation ;
USHORT Status = DosQueryFSInfo ( Drive, FSIL_ALLOC, (PBYTE)&Allocation, sizeof(Allocation) ) ;
DosError ( FERR_ENABLEHARDERR ) ;

if ( Status )
{
return ( DRIVE_ERROR ) ;
}

DosError ( FERR_ENABLEHARDERR ) ;
return ( Allocation.cUnitAvail*Allocation.cSectorUnit*Allocation.cbSector ) ;
}

/****************************************************************************
* *
* Monitor Loop Thread *
* *
****************************************************************************/

STATIC VOID MonitorLoopThread ( PMONITOR_PARMS Parms )
{
/***************************************************************************
* Set this thread's priority as high as it can go. *
***************************************************************************/

DosSetPrty ( PRTYS_THREAD, PRTYC_TIMECRITICAL, PRTYD_MAXIMUM, 0 ) ;

/***************************************************************************
* Start up the high resolution timer, if it is available. *
***************************************************************************/

BOOL HiResTimer = OpenTimer ( ) ;

/***************************************************************************
* Loop forever . . . *
***************************************************************************/

while ( 1 )
{

/*************************************************************************
* Reset the last time and count seen. *
*************************************************************************/

ULONG LastMilliseconds ;
TIMESTAMP Time [2] ;

if ( HiResTimer )
GetTime ( &Time[0] ) ;
else
DosQuerySysInfo ( QSV_MS_COUNT, QSV_MS_COUNT, &LastMilliseconds, sizeof(LastMilliseconds) ) ;

ULONG LastCounter = *Parms->Counter ;

/*************************************************************************
* Sleep for a bit. *
*************************************************************************/

DosSleep ( *Parms->Interval ) ;

/*************************************************************************
* Find out how much time and counts went by. *
*************************************************************************/

ULONG CurrentCounter = *Parms->Counter ;

ULONG DeltaMilliseconds ;

if ( HiResTimer )
{
GetTime ( &Time[1] ) ;

ULONG Nanoseconds ;
DeltaMilliseconds = ElapsedTime ( &Time[0], &Time[1], &Nanoseconds ) ;

if ( Nanoseconds >= 500000L )
DeltaMilliseconds ++ ;
}
else
{
ULONG Milliseconds ;
DosQuerySysInfo ( QSV_MS_COUNT, QSV_MS_COUNT, &Milliseconds, sizeof(Milliseconds) ) ;
DeltaMilliseconds = Milliseconds - LastMilliseconds ;
}

/*************************************************************************
* Find out how much idle time was counted. Adjust it to persecond. *
*************************************************************************/

ULONG Counter = (ULONG) ( ( (double)(CurrentCounter-LastCounter) * 1000L ) / (double)DeltaMilliseconds ) ;

/*************************************************************************
* Tell the owner window to refresh its statistics. *
*************************************************************************/

WinPostMsg ( Parms->Owner, WM_REFRESH, MPFROMLONG(Counter), 0L ) ;
}
}

/****************************************************************************
* *
* Update the Item List to reflect changes in the available drives. *
* *
****************************************************************************/

STATIC VOID UpdateDriveList
(
HAB Anchor,
HMODULE Library,
HINI ProfileHandle,
PPROFILE Profile,
ULONG OldDrives,
ULONG NewDrives
)
{
/***************************************************************************
* Lock the item list. *
***************************************************************************/

if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
{
Log ( "ERROR: Unable to lock item list to update the drive list.\r\n" ) ;
DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
}

/***************************************************************************
* Get format strings. *
***************************************************************************/

BYTE LabelFormat [80] ;
WinLoadString ( Anchor, Library, IDS_SHOW_DRIVE_FREE_LABEL, sizeof(LabelFormat), LabelFormat ) ;

BYTE OptionFormat [80] ;
WinLoadString ( Anchor, Library, IDS_SHOW_DRIVE_FREE_OPTION, sizeof(OptionFormat), OptionFormat ) ;

/***************************************************************************
* Save the old item list for comparison. *
***************************************************************************/

ITEM OldItems [ ITEM_BASE_COUNT + MAX_DRIVES ] ;

memset ( OldItems, 0, sizeof(OldItems) ) ;

USHORT OldCount = 0 ;
if ( OldDrives )
{
OldCount = Profile->ItemCount ;
memcpy ( OldItems, Items, sizeof(OldItems) ) ;
}

/***************************************************************************
* Add items for each drive on the system. *
***************************************************************************/

USHORT Count = ITEM_BASE_COUNT ;
USHORT OldIndex = ITEM_BASE_COUNT ;

NewDrives >>= 2 ;
OldDrives >>= 2 ;

for ( int Drive=3; Drive<=MAX_DRIVES; Drive++ )
{
while ( ( OldIndex < OldCount )
AND ( (SHORT)OldItems[OldIndex].MenuId < IDM_SHOW_DRIVE_FREE + Drive ) )
{
OldIndex ++ ;
}

if ( NewDrives & 1 )
{
if ( OldDrives & 1 )
{
if ( ( OldIndex < OldCount )
AND ( (SHORT)OldItems[OldIndex].MenuId == IDM_SHOW_DRIVE_FREE + Drive ) )
{
Items[Count++] = OldItems[OldIndex++] ;
}
}
else
{
BYTE FileSystem [80] ;
if ( CheckDrive ( Drive, FileSystem ) )
{
sprintf ( (PCHAR)Items[Count].Name, "ShowDrive%c:", Drive+'A'-1 ) ;
sprintf ( (PCHAR)Items[Count].Label, (PCHAR)LabelFormat, Drive+'A'-1, FileSystem ) ;
sprintf ( (PCHAR)Items[Count].MenuOption, (PCHAR)OptionFormat, Drive+'A'-1 ) ;

Items[Count].MenuId = IDM_SHOW_DRIVE_FREE + Drive ;
Items[Count].NewValue = ComputeDriveFree ;
Items[Count].Parm = Drive ;
Items[Count].Divisor = 1024 ;
Items[Count].Suffix = 'K' ;
Items[Count].Error = FALSE ;
Count ++ ;
}
}
}

NewDrives >>= 1 ;
OldDrives >>= 1 ;
}

/***************************************************************************
* Save pointer to fixed configuration information. *
***************************************************************************/

Profile->Items = Items ;
Profile->ItemCount = Count ;

/***************************************************************************
* Fetch the display flags for the drives. *
***************************************************************************/

ULONG Size ;
for ( int i=ITEM_BASE_COUNT; iItemCount; i++ )
{
PITEM Item = Profile->Items + i ;
Item->Flag = TRUE ;
if
(
PrfQueryProfileSize ( ProfileHandle, (PSZ)PROGRAM_NAME, Item->Name, &Size )
AND
( Size == sizeof(Item->Flag) )
AND
PrfQueryProfileData ( ProfileHandle, (PSZ)PROGRAM_NAME, Item->Name, &Item->Flag, &Size )
)
{
;
}
}

/***************************************************************************
* Release the item list. *
***************************************************************************/

DosReleaseMutexSem ( ItemSemaphore ) ;
}

/****************************************************************************
* *
* Check to see if drive should be added to display list. *
* *
****************************************************************************/

STATIC BOOL CheckDrive ( USHORT Drive, PBYTE FileSystem )
{
/***************************************************************************
* First, check to see if drive is local or remote. Remote drives are *
* always monitored. *
***************************************************************************/

BYTE Path [3] ;
Path[0] = (BYTE) ( Drive + 'A' - 1 ) ;
Path[1] = ':' ;
Path[2] = 0 ;

DosError ( FERR_DISABLEHARDERR ) ;

BYTE Buffer [1024] ;
ULONG Size = sizeof(Buffer) ;
ULONG Status = DosQueryFSAttach ( Path, 0, FSAIL_QUERYNAME, (PFSQBUFFER2)Buffer, &Size ) ;
DosError ( FERR_ENABLEHARDERR ) ;

if ( Status )
{
Log ( "ERROR: Unable to query drive %s for file system. Status %04X.\r\n",
Path, Status ) ;
return ( FALSE ) ;
}

USHORT cbName = ((PFSQBUFFER2)Buffer)->cbName ;
strcpy ( (PCHAR)FileSystem, (PCHAR)((PFSQBUFFER2)(Buffer+cbName))->szFSDName ) ;

if ( ((PFSQBUFFER2)Buffer)->iType == FSAT_REMOTEDRV )
{
return ( TRUE ) ;
}

/***************************************************************************
* Attempt to open the local drive as an entire device. If unable to do *
* so, we cannot monitor this drive. *
***************************************************************************/

ULONG Action ;
HFILE Handle ;
Status = DosOpen ( Path, &Handle, &Action, 0, 0, FILE_OPEN,
OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE |

OPEN_FLAGS_DASD | OPEN_FLAGS_FAIL_ON_ERROR, 0 ) ;

if ( Status )
{
Log ( "ERROR: Unable to open local drive %s. Status %04X.\r\n",
Path, Status ) ;
return ( FALSE ) ;
}

/***************************************************************************
* Check to see if the drive has removable media. We cannot monitor such. *
***************************************************************************/

BOOL Addit = FALSE ;
BYTE Command = 0 ;
BYTE NonRemovable ;

ULONG LengthIn = sizeof(Command) ;
ULONG LengthOut = sizeof(NonRemovable);

if
(
NOT DosDevIOCtl
(
Handle, 8, 0x20,
&Command, sizeof(Command), &LengthIn,
&NonRemovable, sizeof(NonRemovable), &LengthOut
)
)
{
Addit = NonRemovable ;
}

/***************************************************************************
* Close the drive. *
***************************************************************************/

DosClose ( Handle ) ;

/***************************************************************************
* Return the final verdict. *
***************************************************************************/

return ( Addit ) ;
}

/****************************************************************************
* *
* Rebuild the Display Items submenu. *
* *
****************************************************************************/

STATIC VOID RebuildDisplayItems ( HWND hwnd, PDATA Data )
{
/***************************************************************************
* Lock the item list. *
***************************************************************************/

if ( DosRequestMutexSem ( ItemSemaphore, 5000 ) )
{
Log ( "ERROR: Unable to lock item list to rebuild the display item menu.\r\n" ) ;
DosRequestMutexSem ( ItemSemaphore, (ULONG)SEM_INDEFINITE_WAIT ) ;
}

/***************************************************************************
* Find the item menu's handle. *
***************************************************************************/

HWND Frame = WinQueryWindow ( hwnd, QW_PARENT ) ;

HWND SysMenu = WinWindowFromID ( Frame, FID_SYSMENU ) ;

MENUITEM MenuItem ;
WinSendMsg ( SysMenu, MM_QUERYITEM,
MPFROM2SHORT ( IDM_DISPLAY_ITEMS, TRUE ),
(MPARAM) &MenuItem ) ;

HWND ItemMenu = MenuItem.hwndSubMenu ;

/***************************************************************************
* Remove all items from the menu. *
***************************************************************************/

if ( WinSendMsg ( ItemMenu, MM_QUERYITEMCOUNT, 0, 0 ) )
{
USHORT Id ;
SHORT ItemsLeft ;

do
{
Id = SHORT1FROMMR ( WinSendMsg ( ItemMenu, MM_ITEMIDFROMPOSITION,
0, 0 ) ) ;

ItemsLeft = SHORT1FROMMR ( WinSendMsg ( ItemMenu, MM_DELETEITEM,
MPFROM2SHORT(Id,FALSE), 0 ) ) ;
}
while ( ItemsLeft ) ;
}

/***************************************************************************
* Prepare menu item structure for use. *
***************************************************************************/

MenuItem.iPosition = MIT_END ;
MenuItem.afStyle = MIS_TEXT ;
MenuItem.afAttribute = 0 ;
MenuItem.hwndSubMenu = NULL ;
MenuItem.hItem = 0L ;

/***************************************************************************
* Add all menu items called for. *
***************************************************************************/

for ( int i=0; iProfile.ItemCount; i++ )
{
PITEM Item = Data->Profile.Items + i ;

MenuItem.id = Item->MenuId ;

AddSysSubMenuItem ( Frame, IDM_DISPLAY_ITEMS, &MenuItem, Item->MenuOption ) ;

CheckMenuItem ( Frame, FID_SYSMENU, Item->MenuId, Item->Flag ) ;
}

/***************************************************************************
* Split the menu if it's too tall. *
***************************************************************************/

if ( Data->Profile.ItemCount > 15 )
{
USHORT Midpoint = ( Data->Profile.ItemCount + 1 ) / 2 ;
USHORT Id = Data->Profile.Items[Midpoint].MenuId ;
MENUITEM MenuItem ;

if ( WinSendMsg ( ItemMenu, MM_QUERYITEM, MPFROM2SHORT(Id,FALSE), &MenuItem ) )
{
MenuItem.afStyle |= MIS_BREAKSEPARATOR ;
WinSendMsg ( ItemMenu, MM_SETITEM, MPFROM2SHORT(0,FALSE), &MenuItem ) ;
}
}

/***************************************************************************
* Release the item list. *
***************************************************************************/

DosReleaseMutexSem ( ItemSemaphore ) ;
}

/****************************************************************************
* *
* Calibrate the Load Meter *
* *
****************************************************************************/

STATIC ULONG CalibrateLoadMeter ( void )
{
/***************************************************************************
* Set result to zero as a default. *
***************************************************************************/

double AdjustedMaxLoad = 0.0 ;

/***************************************************************************
* If HRTIMER.SYS has been installed . . . *
***************************************************************************/

if ( OpenTimer ( ) )
{
/*************************************************************************
* Increase this thread's priority to the maximum. *
*************************************************************************/

DosSetPrty ( PRTYS_THREAD, PRTYC_TIMECRITICAL, PRTYD_MAXIMUM, 0 ) ;

/*************************************************************************
* Create the calibration thread and set its priority next highest. *
*************************************************************************/

TID tidCalibrate ;
ULONG MaxLoad ;
DosCreateThread ( &tidCalibrate, CounterThread, (ULONG)&MaxLoad, 0, 4096 ) ;
DosSetPrty ( PRTYS_THREAD, PRTYC_TIMECRITICAL, PRTYD_MAXIMUM-1, tidCalibrate ) ;
DosSuspendThread ( tidCalibrate ) ;

/*************************************************************************
* Reset the calibration count, get the time, and let the counter go. *
*************************************************************************/

MaxLoad = 0 ;
TIMESTAMP Time[2] ;
GetTime ( &Time[0] ) ;
DosResumeThread ( tidCalibrate ) ;

/*************************************************************************
* Sleep for one second. *
*************************************************************************/

DosSleep ( 1000 ) ;

/*************************************************************************
* Suspend the calibration counter and get the time. *
*************************************************************************/

DosSuspendThread ( tidCalibrate ) ;
GetTime ( &Time[1] ) ;

/*************************************************************************
* Return priorities to normal. *
*************************************************************************/

DosSetPrty ( PRTYS_THREAD, PRTYC_REGULAR, 0, 0 ) ;

/*************************************************************************
* Get the elapsed time and adjust the calibration count. *
*************************************************************************/

ULONG Milliseconds ;
ULONG Nanoseconds ;
Milliseconds = ElapsedTime ( &Time[0], &Time[1], &Nanoseconds ) ;

AdjustedMaxLoad = (double)MaxLoad * 1.0E9 ;
AdjustedMaxLoad /= (double)Milliseconds*1.0E6L + (double)Nanoseconds ;

/*************************************************************************
* Close down the connection to HRTIMER.SYS. *
*************************************************************************/

CloseTimer ( ) ;
}

/***************************************************************************
* Return the adjusted calibration count. If HRTIMER was not there, it *
* will be zero. *
***************************************************************************/

return ( (ULONG)AdjustedMaxLoad ) ;
}

/****************************************************************************
* *
* General Purpose Counter Thread *
* *
****************************************************************************/

STATIC VOID CounterThread ( PULONG Counter )
{
while ( 1 )
{
(*Counter) ++ ;
}
}

/****************************************************************************
* *
* Open the Profile *
* *
****************************************************************************/

STATIC HINI OpenProfile ( PSZ Name, HAB Anchor, HMODULE Library, HWND HelpInstance )
{
/***************************************************************************
* Query the system INI for the profile file's path. *
***************************************************************************/

PSZ ProfilePath = NULL ;
ULONG Size ;

if ( PrfQueryProfileSize ( HINI_USERPROFILE, PSZ(PROGRAM_NAME), PSZ("INIPATH"), &Size ) )
{
// The info exists. Fetch it.
ProfilePath = PSZ ( AllocateMemory ( Size ) ) ;
PrfQueryProfileData ( HINI_USERPROFILE, PSZ(PROGRAM_NAME), PSZ("INIPATH"),
ProfilePath, &Size ) ;

// Build the profile file name.
BYTE FullPath [_MAX_PATH] ;
strcpy ( PCHAR(FullPath), PCHAR(ProfilePath) ) ;
strcat ( PCHAR(FullPath), "\\" ) ;
strcat ( PCHAR(FullPath), PCHAR(Name) ) ;
strcat ( PCHAR(FullPath), ".INI" ) ;

// Clean the name up and expand it to a full path.
BYTE Path [256] ;
DosQueryPathInfo ( FullPath, FIL_QUERYFULLNAME, Path, sizeof(Path) ) ;

// Does the file exist? If not, discard the name.
FILESTATUS3 Status ;
if ( DosQueryPathInfo ( Path, FIL_STANDARD, &Status, sizeof(Status) ) )
{
FreeMemory ( ProfilePath ) ;
ProfilePath = NULL ;
}
}

/***************************************************************************
* If the profile file couldn't be found, ask the user for a path. *
***************************************************************************/

if ( ProfilePath == NULL )
{
// Set the default path.
BYTE Path [256] ;
DosQueryPathInfo ( PSZ("."), FIL_QUERYFULLNAME, Path, sizeof(Path) ) ;

// Call up the entry dialog.
PROFILE_PARMS Parms ;
Parms.id = IDD_PROFILE_PATH ;
Parms.hwndHelp = HelpInstance ;
Parms.Path = Path ;
Parms.PathSize = sizeof(Path) ;
if ( WinDlgBox ( HWND_DESKTOP, HWND_DESKTOP, ProfileProcessor,
Library, IDD_PROFILE_PATH, &Parms ) )
{
// If OK, save the approved path in the system profile.
ProfilePath = PSZ ( AllocateMemory ( strlen(PCHAR(Path)) + 1 ) ) ;
strcpy ( PCHAR(ProfilePath), PCHAR(Path) ) ;

PrfWriteProfileData ( HINI_USERPROFILE, PSZ(PROGRAM_NAME), PSZ("INIPATH"),
ProfilePath, strlen(PCHAR(ProfilePath))+1 ) ;
}
else
{
// If not, return an error.
return ( NULL ) ;
}
}

/***************************************************************************
* Build the full profile file name. *
***************************************************************************/

BYTE ProfileName [_MAX_PATH] ;
strcpy ( PCHAR(ProfileName), PCHAR(ProfilePath) ) ;
strcat ( PCHAR(ProfileName), "\\" PROGRAM_NAME ".INI" ) ;

/***************************************************************************
* Release the memory previously allocated to store the path. *
***************************************************************************/

if ( ProfilePath )
{
FreeMemory ( ProfilePath ) ;
}

/***************************************************************************
* Open/Create the profile file and return the resultant handle. *
***************************************************************************/

return ( PrfOpenProfile ( Anchor, ProfileName ) ) ;
}