// ============================================================================
// resgauge.c -- a gauge of free system resources.
//
// Richard Franklin Albury
// PO Box 19652
// Raleigh, NC 27619-9652
//
// CIS: 76477,534
// ============================================================================


#define MAIN // make globals local to this module


#include <windows.h>
#include <toolhelp.h>
#include "resgauge.h"


// ---------------------
// default configuration
// ---------------------
CONFIG GcfgDef =
       {
           FALSE,              // TRUE if the alarm is enabled
           FALSE,              // TRUE if "Keep on top" is selected
           ALARM_BEEP,         // beep or flash
           MONITOR_BOTH,       // GDI, User, or both
           10,                 // percent at which alarm sounds
           RGB(255,   0,   0), // gauge color for GDI
           RGB(  0, 255,   0), // gauge color for User
           RGB(  0,   0, 255)  // gauge color for both
       };

// -------------------------------------
// lookup table for plotting the "gauge"
// -------------------------------------
OFFSET GaOffset[] =
       {
           {  0,  91}, {  0,  94}, {  0,  97}, {  0, 100}, {  1,  84},
           {  1,  87}, {  2,  78}, {  2,  81}, {  3,  75}, {  4,  72},
           {  5,  69}, {  6,  66}, {  7,  63}, {  8,  60}, { 10,  57},
           { 11,  55}, { 12,  52}, { 14,  49}, { 16,  46}, { 17,  44},
           { 19,  41}, { 21,  39}, { 23,  36}, { 25,  34}, { 27,  32},
           { 29,  29}, { 32,  27}, { 34,  25}, { 36,  23}, { 39,  21},
           { 41,  19}, { 44,  17}, { 46,  16}, { 49,  14}, { 52,  12},
           { 55,  11}, { 57,  10}, { 60,   8}, { 63,   7}, { 66,   6},
           { 69,   5}, { 72,   4}, { 75,   3}, { 78,   2}, { 81,   2},
           { 84,   1}, { 87,   1}, { 91,   0}, { 94,   0}, { 97,   0},
           {100,   0}, {103,   0}, {106,   0}, {109,   0}, {113,   1},
           {116,   1}, {119,   2}, {122,   2}, {125,   3}, {128,   4},
           {131,   5}, {134,   6}, {137,   7}, {140,   8}, {143,  10},
           {145,  11}, {148,  12}, {151,  14}, {154,  16}, {156,  17},
           {159,  19}, {161,  21}, {164,  23}, {166,  25}, {168,  27},
           {171,  29}, {173,  32}, {175,  34}, {177,  36}, {179,  39},
           {181,  41}, {183,  44}, {184,  46}, {186,  49}, {188,  52},
           {189,  55}, {190,  57}, {192,  60}, {193,  63}, {194,  66},
           {195,  69}, {196,  72}, {197,  75}, {198,  78}, {198,  81},
           {199,  84}, {199,  87}, {200,  91}, {200,  94}, {200,  97},
           {200, 100}
       };


// ============================================================================
// > > > > > > > > > > > > > > >  code  begins  < < < < < < < < < < < < < < < <
// ============================================================================
// The "main()" for Windows: calls the initialization functions and enters the
// message processing loop.  Returns the wParam element of the MSG when a
// WM_QUIT message has been processed by the window function.
// ----------------------------------------------------------------------------
int
PASCAL
WinMain(
HINSTANCE hInstance,     // current instance
HINSTANCE hPrevInstance, // previous instance
LPSTR     lpstrCmdLine,  // command line
int       nCmdShow)      // window state (usually open or iconic)
{
    int   nReturn;
    MSG   msg;

    // assume failure and save the instance handle
    nReturn = 1;
    GhInst = hInstance;

    // get the configuration info and process the command line
    GetConfig();
    ProcessCmdLine(lpstrCmdLine);

    // call the initialization functions and enter the message loop
    if ( InitApplication(hPrevInstance) && InitInstance() )
    {
        while ( GetMessage(&msg, NULL, 0, 0) )
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        nReturn = msg.wParam;
    }

    return(nReturn);
} // WinMain


// ----------------------------------------------------------------------------
// Processes messages for the main window.
// ----------------------------------------------------------------------------
LRESULT
WINAPI
MainWndProc(
HWND        hWnd,     // window handle
UINT        wMessage, // type of message
WPARAM      wParam,   // 16 bits of information
LPARAM      lParam)   // 32 additional bits of information
{
    HMENU   hMenu;
    LRESULT lrReturn;
    WORD    wTmp;

    lrReturn = 0L;
    switch ( wMessage )
    {
        case WM_CREATE:
            MainCreate(hWnd);
            break;

        // disable Restore, Size, Minimize, and Maximize on the system menu
        case WM_INITMENU:
            hMenu = (HMENU) wParam;
            EnableMenuItem(hMenu, SC_RESTORE,  MF_BYCOMMAND | MF_GRAYED);
            EnableMenuItem(hMenu, SC_SIZE,     MF_BYCOMMAND | MF_GRAYED);
            EnableMenuItem(hMenu, SC_MINIMIZE, MF_BYCOMMAND | MF_GRAYED);
            EnableMenuItem(hMenu, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
            DrawMenuBar(hWnd);
            break;

        case WM_TIMER:
            MainTimer(hWnd);
            break;

        case WM_SYSCOMMAND:
            switch ( wParam )
            {
                case SC_KEEP_ON_TOP:
                    hMenu = GetSystemMenu(hWnd, FALSE);
                    wTmp = GetMenuState(hMenu, SC_KEEP_ON_TOP, MF_BYCOMMAND);
                    HandleKeepOnTop(hWnd,
                                    hMenu,
                                    ((wTmp & MF_CHECKED) != MF_CHECKED));
                    SaveKeepOnTop();
                    break;

                case SC_CONFIGURE:
                    DialogBox(GhInst,
                              MAKEINTRESOURCE(IDDB_CONFIGURE),
                              hWnd,
                              ConfigDlgProc);
                    break;

                case SC_ABOUT:
                    DialogBox(GhInst,
                              MAKEINTRESOURCE(IDDB_ABOUT),
                              hWnd,
                              AboutDlgProc);
                    break;

                default:
                    lrReturn = DefWindowProc(hWnd, wMessage, wParam, lParam);
                    break;
            }
            break;

        case WM_PAINT:
            MainPaint(hWnd);
            break;

        case WM_QUERYOPEN:
            break;

        case WM_DESTROY:
            KillTimer(hWnd, TIMER_ID);
            PostQuitMessage(0);
            break;

        default:
            lrReturn = DefWindowProc(hWnd, wMessage, wParam, lParam);
            break;
    }

    return(lrReturn);
} // MainWndProc


// ----------------------------------------------------------------------------
// Handles the WM_CREATE message for the main window.
// ----------------------------------------------------------------------------
void
FAR
PASCAL
MainCreate(
HWND      hWnd) // window handle
{
    char  szMenuStr[32];
    HMENU hMenu;

    GwFree = GetSystemResources();
    FormatLabel();
    hMenu = GetSystemMenu(hWnd, FALSE);
    AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
    LoadString(GhInst, IDSM_KEEP_ON_TOP, szMenuStr, sizeof(szMenuStr) - 1);
    AppendMenu(hMenu, MF_STRING, SC_KEEP_ON_TOP, szMenuStr);
    LoadString(GhInst, IDSM_CONFIGURE, szMenuStr, sizeof(szMenuStr) - 1);
    AppendMenu(hMenu, MF_STRING, SC_CONFIGURE, szMenuStr);
    LoadString(GhInst, IDSM_ABOUT, szMenuStr, sizeof(szMenuStr) - 1);
    AppendMenu(hMenu, MF_STRING, SC_ABOUT, szMenuStr);
    HandleKeepOnTop(hWnd, hMenu, GcfgCur.fKeepOnTop);
    DrawMenuBar(hWnd);
} // MainCreate


// ----------------------------------------------------------------------------
// Handles the WM_PAINT message for the main window.
// ----------------------------------------------------------------------------
void
FAR
PASCAL
MainPaint(
HWND            hWnd) // window handle
{
    int         xText;
    int         yText;
    int         wX;
    int         wY;
    DWORD       dwExtent;
    HBRUSH      hBrush;
    HBRUSH      hOldBrush;
    HDC         hDC;
    PAINTSTRUCT ps;
    RECT        rect;

    // begin painting
    hDC = BeginPaint(hWnd, &ps);

    // get the dimensions of the label string
    SelectObject(hDC, GetStockObject(ANSI_VAR_FONT));
    dwExtent = GetTextExtent(hDC, GszBuf, lstrlen(GszBuf));

    // get the dimensions of the the window and frame the window
    GetClientRect(hWnd, &rect);
    FrameRect(hDC, &rect, GetStockObject(BLACK_BRUSH));

    // calculate where the label goes
    xText = rect.left +
            ((rect.right - rect.left) - LOWORD(dwExtent)) / 2;
    yText = rect.top + rect.bottom - HIWORD(dwExtent) - 1;
    yText = rect.bottom - HIWORD(dwExtent) - 1;

    // modify the rectangle to hold the gauge pie
    rect.bottom = yText;
    InflateRect(&rect, -2, -2);

    // calculate the origin of the pie
    wX = rect.right - rect.left;
    wY = rect.bottom - rect.top;

    // create a brush for the gauge pie and save the old brush
    if ( GcfgCur.wMonitor == MONITOR_GDI )
        hBrush = CreateSolidBrush(GcfgCur.dwColorGDI);
    else if ( GcfgCur.wMonitor == MONITOR_USER )
        hBrush = CreateSolidBrush(GcfgCur.dwColorUser);
    else
        hBrush = CreateSolidBrush(GcfgCur.dwColorBoth);
    hOldBrush = SelectObject(hDC, hBrush);

    // draw the gauge pie
    Pie(hDC,
        rect.left,
        rect.top,
        rect.right,
        rect.bottom + (rect.bottom - rect.top) + 1,
        rect.left + (GaOffset[GwFree].wX * wX) / 200,
        rect.top  + (GaOffset[GwFree].wY * wY) / 100,
        rect.left,
        rect.bottom);

    // restore the old brush and delete the new brush
    SelectObject(hDC, hOldBrush);
    DeleteObject(hBrush);

    // draw the label
    TextOut(hDC, xText, yText, GszBuf, lstrlen(GszBuf));

    // stop painting
    EndPaint(hWnd, &ps);
} // MainPaint


// ----------------------------------------------------------------------------
// Handles the WM_TIMER message for the main window.
// ----------------------------------------------------------------------------
void
FAR
PASCAL
MainTimer(
HWND     hWnd) // window handle
{
    WORD wFree;

    wFree = GetSystemResources();
    if ( (GcfgCur.fAlarmEnabled) && (wFree <= GcfgCur.wThreshold) )
    {
        if ( GcfgCur.wAlarmType == ALARM_FLASH )
        {
            SetWindowPos(hWnd,
                         HWND_TOP,
                         0, 0, 0, 0,
                         SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
            FlashWindow(hWnd, TRUE);
        }
        else
        {
            MessageBeep(0);
        }
    }
    if ( wFree != GwFree )
    {
        // save the new value
        GwFree = wFree;

        // format the gauge window label
        FormatLabel();

        // force a repaint
        InvalidateRect(hWnd, NULL, TRUE);
    }
} // MainTimer


// ----------------------------------------------------------------------------
// Handles the toggling of the "Keep on top" system menu option.
// ----------------------------------------------------------------------------
void
FAR
PASCAL
HandleKeepOnTop(
HWND     hWnd,       // window handle
HMENU    hMenu,      // system menu handle
BOOL     fKeepOnTop) // TRUE if "Keep on top" was selected
{
    HWND hWndInsertAfter;
    WORD wCheck;

    if ( fKeepOnTop )
    {
        wCheck = MF_BYCOMMAND | MF_CHECKED;
        hWndInsertAfter = HWND_TOPMOST;
        GcfgCur.fKeepOnTop = TRUE;
    }
    else
    {
        wCheck = MF_BYCOMMAND | MF_UNCHECKED;
        hWndInsertAfter = HWND_NOTOPMOST;
        GcfgCur.fKeepOnTop = FALSE;
    }
    CheckMenuItem(hMenu, SC_KEEP_ON_TOP, wCheck);
    SetWindowPos(hWnd,
                 hWndInsertAfter,
                 0, 0, 0, 0,
                 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
} // HandleKeepOnTop


// ----------------------------------------------------------------------------
// Centers a dialog.
// ----------------------------------------------------------------------------
void
FAR
PASCAL
CenterDialog(
HWND      hDlg)
{
    short x;
    short y;
    short xSize;
    short ySize;
    RECT  rect;

    GetWindowRect(hDlg, &rect);
    xSize = rect.right - rect.left;
    ySize = rect.bottom - rect.top;
    x = GetSystemMetrics(SM_CXSCREEN);
    y = GetSystemMetrics(SM_CYSCREEN);
    MoveWindow(hDlg, (x - xSize) / 2, (y - ySize) / 2, xSize, ySize, TRUE);
} // CenterDialog


// ----------------------------------------------------------------------------
// Uses the SystemHeapInfo() call and the algorithm given in TIPS.TXT to
// calculate the percentage of free system resources.
// ----------------------------------------------------------------------------
WORD
FAR
PASCAL
GetSystemResources(void)
{
    SYSHEAPINFO shi;
    WORD        wReturn;

    wReturn = 0;
    shi.dwSize = sizeof(SYSHEAPINFO);
    if ( SystemHeapInfo(&shi) )
    {
        switch ( GcfgCur.wMonitor )
        {
            case MONITOR_GDI:
                wReturn = shi.wGDIFreePercent;
                break;

            case MONITOR_USER:
                wReturn = shi.wUserFreePercent;
                break;

            case MONITOR_BOTH:
                wReturn = min(shi.wGDIFreePercent, shi.wUserFreePercent);
                break;
        }
    }

    return(wReturn);
} // GetSystemResources


// ----------------------------------------------------------------------------
// Builds the label string for the gauge window.
// ----------------------------------------------------------------------------
void
FAR
PASCAL
FormatLabel(void)
{
    char szFormat[32];
    WORD wTmp;

    // determine the monitor mode
    if ( GcfgCur.wMonitor == MONITOR_GDI )
        wTmp = IDS_FORMAT_GDI;
    else if ( GcfgCur.wMonitor == MONITOR_USER )
        wTmp = IDS_FORMAT_USER;
    else
        wTmp = IDS_FORMAT_BOTH;

    // load the formatting string and build the label string
    LoadString(GhInst, wTmp, szFormat, sizeof(szFormat) - 1);
    wsprintf(GszBuf, szFormat, GwFree);
} // FormatLabel


// =================
// end of resgauge.c
// =================
