/* joytoy.c - WinMain() and WndProc() for JOYTOY, along with
 *      initialization and support code.
 *
 * JOYTOY is a Windows with Multimedia application that illustrates
 *  how to use the joystick services. When run, it presents the user
 *  with a crosshair cursor. When the joystick or the mouse is moved,
 *  the cursor follows. When a joystick button is pressed, JOYTOY
 *  produces a sound and draws a bullet hole icon onto the screen.
 *
 *
 *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 *  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
 *  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR
 *  A PARTICULAR PURPOSE.
 *
 *  Copyright (C) 1993 - 1996 Microsoft Corporation. All Rights Reserved.
 */

#include <windows.h>
#include <mmsystem.h>
#include "joytoy.h"


char szAppName[] = "JoyToy";     
HANDLE hSound1, hSound2;
HANDLE hHole;
LPSTR lpSound1, lpSound2;


/* WinMain - Entry point for JOYTOY.
 */
int PASCAL WinMain(hInstance,hPrevInstance,lpszCmdLine,cmdShow)
HANDLE hInstance,hPrevInstance;
LPSTR lpszCmdLine;
int cmdShow;
{
    MSG msg;

    /* Initialize the application.
     */
    if(! InitJoyToy(hInstance,hPrevInstance))
        return FALSE;

    /* Standard Windows message processing loop.  We don't drop out of
     * this loop until the user quits the application.
     */
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    /* Unlock and free resources allocated by InitJoyToy().
     */
    UnlockResource(hSound1);
    UnlockResource(hSound2);
    FreeResource(hSound1);
    FreeResource(hSound2);
    FreeResource(hHole);

    return (msg.wParam);
}

/* DrawSight - Takes the new joystick position and moves the 
 *  mouse cursor accordingly.
 *
 * Params:  lParam - Specifies the new joystick position. The
 *  high-order word is the y position and the low-order word is
 *  the x position.
 *
 * Returns: void
 */
void DrawSight(DWORD lParam)
{
    WORD x, y;
    POINT pt;

    /* Get the current cursor position in screen coordinates.
     */
    GetCursorPos(&pt);

    /* Joystick positions are expressed in a coordinate system with the
     * origin in the upper left corner and with coordinate ranges from
     * 0 to 65535. Take the 5 most significant bits, so the position is 
     * expressed in the range of 0 to 31.
     */
    x = LOWORD(lParam) >> 11;
    y = HIWORD(lParam) >> 11;

    /* If the joystick is to the left of the center position, then move
     * the cursor position to the left. Otherwise, if the joystick is to 
     * the right of the center position, then move the cursor position 
     * to the right.
     */
    if(x <= 12)
        pt.x = pt.x + x - 17;
    else if(x >= 20)
        pt.x = pt.x + x - 15;

    /* If the joystick is below the center position, then move the cursor 
     * position down. Otherwise, if the joystick is above the center 
     * position, then move the cursor position up.
     */
    if(y <= 12)
        pt.y = pt.y + y - 17;
    else if(y >= 20)
        pt.y = pt.y + y - 15;

    /* Set the new cursor position.
     */
    SetCursorPos(pt.x, pt.y);
}

/* DrawHole - Draws an icon representing a hole in the given window at the 
 * current cursor position.
 *
 * Params:  hWnd - Specifies the handle to the window to draw in.
 *
 * Returns: void
 */
void DrawHole(HWND hWnd)
{
    HDC hDC;
    POINT pt;

    /* Get the current cursor position.
     */
    GetCursorPos(&pt);
    
    /* Get a DC, draw the icon, release DC.
     */
    hDC = GetDC(hWnd);
    DrawIcon(hDC, pt.x - 16, pt.y - 16, hHole);
    ReleaseDC(hWnd, hDC);
}


/* WndProc - Main window procedure function.
 */
LRESULT FAR PASCAL WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_CREATE:
            /* Capture the joystick. If this fails, beep and display
             * error, then quit.
             */
            if(joySetCapture(hWnd, JOYSTICKID1, 0, FALSE))
            {
                MessageBeep(MB_ICONEXCLAMATION);
                MessageBox(hWnd, "Couldn't capture the joystick", NULL, 
                           MB_OK | MB_ICONEXCLAMATION);
                return -1;
            }            
            break;

        case WM_ACTIVATE:
        case WM_ACTIVATEAPP:
            /* If app becomes inactive (wParam == 0), then we want to quit.
             */
            if(wParam)
                break;

        case WM_LBUTTONDOWN:
        case WM_MBUTTONDOWN:
        case WM_RBUTTONDOWN:
        case WM_KEYDOWN:
        case WM_CHAR:
            /* Also, any keystrokes or mouse buttons cause us to quit.
             */
            PostMessage(hWnd,WM_CLOSE,0,0L);
            break;

        case WM_ERASEBKGND:
            /* Process this message to keep Windows from erasing background.
             */
            return 0l;
 
        case MM_JOY1BUTTONDOWN :
            /* Joystick button pressed. Detect which button, play appropriate
             * sound, then draw bullet hole. To play sound, pass 
             * sndPlaySound() a pointer to an in-memory WAVE file and 
             * specify the SND_MEMORY flag. Specify SND_LOOP and SND_ASYNC
             * so that sound keeps playing until it's turned off after 
             * joystick button is released.
             */
            if (wParam & JOY_BUTTON1CHG)
            {
                sndPlaySound(lpSound1, SND_LOOP | SND_ASYNC | SND_MEMORY);
                DrawHole(hWnd);
            }
            else if (wParam & JOY_BUTTON2CHG)
            {
                sndPlaySound(lpSound2, SND_LOOP | SND_ASYNC | SND_MEMORY);
                DrawHole(hWnd);
            }
            break;

        case MM_JOY1BUTTONUP :
            /* Stop playing looped sound.
             */
            if (wParam & JOY_BUTTON1)
                sndPlaySound(lpSound1, SND_LOOP | SND_ASYNC | SND_MEMORY);
            else if (wParam & JOY_BUTTON2)
                sndPlaySound(lpSound2, SND_LOOP | SND_ASYNC | SND_MEMORY);
            else
                sndPlaySound(NULL, 0);
            break;

        case MM_JOY1MOVE :
            /* If joystick buttons are pressed, draw bullet holes
             * while joystick is moved.
             */
            if(wParam & (JOY_BUTTON1 | JOY_BUTTON2))
                DrawHole(hWnd);

            /* Redraw the crosshair in its new position.
             */
            DrawSight(lParam);
            break;


        case WM_DESTROY:
            /* We're shutting down. Release capture on the joystick, 
             * make sure any sounds that are playing are stopped.
             */
            joyReleaseCapture(JOYSTICKID1);
            sndPlaySound(NULL, 0);
            PostQuitMessage(0);
            break;
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}


/* InitJoyToy - Application initialization routine.
 *
 * Params:  hInstance - App's instance handle.
 *
 * Returns: TRUE if initialization is successful, FALSE otherwise.
 */
BOOL InitJoyToy(HANDLE hInstance, HANDLE hPrevInstance)
{
    WNDCLASS wc;
    HWND     hWnd;

    /* Make sure there is a joystick device installed.
     */
    if (!joyGetNumDevs())
    {
        MessageBox(NULL, "There are no joystick devices installed. Exiting.",
                   NULL, MB_OK | MB_ICONEXCLAMATION);
        return FALSE;
    }
   
    /* Load bullet hole icon.
     */
    hHole = LoadIcon(hInstance, "HoleIcon");

    if (hPrevInstance == NULL) {
        /* Setup and register a window class for our main window.
         */
        wc.hCursor          = LoadCursor(NULL, IDC_CROSS);
        wc.hIcon            = NULL;
        wc.lpszMenuName     = NULL;
        wc.lpszClassName    = szAppName;
        wc.hbrBackground    = GetStockObject(BLACK_BRUSH);
        wc.hInstance        = hInstance;
        wc.style            = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc      = WndProc;
        wc.cbClsExtra       = 0;
        wc.cbWndExtra       = 0;
    
        if(! RegisterClass(&wc))
            return FALSE;   
    }

    /* Create a full-screen window with no title bar or scroll bars.
     */
    hWnd = CreateWindow(szAppName, szAppName, WS_POPUP | WS_VISIBLE, 0, 0,
        GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
        NULL, NULL, hInstance, NULL);

    if (hWnd != NULL) {
        /* Load and lock sound resources.
         */
        hSound1 = LoadResource(hInstance, FindResource(hInstance, "SOUND1", "WAVE"));
        hSound2 = LoadResource(hInstance, FindResource(hInstance, "SOUND2", "WAVE"));
        lpSound1 = LockResource(hSound1);
        lpSound2 = LockResource(hSound2);
    }
    return hWnd != NULL;
}
