
/*****************************************************/
/*  textmode.c                                       */
/* -- Sample program demonstrates 80x25 mode from    */
/*    within Windows.                                */
/*****************************************************/

#include <windows.h>
#include <memory.h>
#include "texttsr.h"

/*****************************************************/
/* Prototypes.                                       */
/*****************************************************/
VOID             ExitTextMode(HWND);
VOID             EnterTextMode(HWND);
VOID             HandleKey(WPARAM);
VOID             SetCursorXY(char, char);
VOID             SetLpvWCw(VOID FAR *, WORD, UINT);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

/*****************************************************/
/* Constants.                                        */
/*****************************************************/
#define szApp    "Video"
#define dxText   80
#define dyText   25
#define cbText   (dxText * dyText * sizeof(WORD))

/*****************************************************/
/* Globals.                                          */
/*****************************************************/
/* Imports. */
extern WORD _B000H;

/* Local. */
LPWORD      lrgwText, lrgwSav;
UINT        selVGA;
char        x, y;
BOOL        fInTextMode;
BOOL        fWantTextMode;
RECT        rectWindow;

/*****************************************************/
/* Routines.                                         */
/*****************************************************/

int FAR PASCAL
WinMain(HINSTANCE hins, HINSTANCE hinsPrev, LPSTR lsz,
  int wShow)
/*****************************************************/
/* -- hins        : This program's instance.         */
/* -- hinsPrev    : Previous program's instance.     */
/* -- lsz         : Command line I was invoked with. */
/* -- wShow       : ShowWindow command.              */
/*****************************************************/
    {
    MSG     msg;
    HGLOBAL hmemText = NULL;
    BYTE    bLoaded;
    HWND    hwnd;

    /* Make sure TSR is loaded. */
    _asm
        {
        mov     ax, cmdIsLoaded
        int     0x2f
        mov     bLoaded, al
        }
    if (bLoaded != 0xff)
        {
        MessageBox(NULL,
          "Texttsr has not been loaded.  Please run"
            " texttsr.exe before entering Windows.",
          szApp, MB_ICONHAND | MB_OK);
        goto WinMainExit;
        }

    if (hinsPrev == NULL)
        {
        WNDCLASS    wcs;

        /* First instance is responsible for */
        /* registering a class. */
        wcs.style = CS_HREDRAW | CS_VREDRAW;
        wcs.lpfnWndProc = WndProc;
        wcs.cbClsExtra = 0;
        wcs.cbWndExtra = 0;
        wcs.hInstance = hins;
        wcs.hIcon = NULL;
        wcs.hCursor = LoadCursor(NULL, IDC_ARROW);
        wcs.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wcs.lpszMenuName = NULL;
        wcs.lpszClassName = szApp;

        if (!RegisterClass(&wcs))
            goto WinMainExit;
        }

    /* Initialize the video memory. */
    hmemText = GlobalAlloc(GMEM_MOVEABLE, cbText);
    if (hmemText == NULL)
        goto WinMainExit;

    lrgwSav = (LPWORD)GlobalLock(hmemText);
    lrgwText  =
      (LPWORD)MAKELONG(0x0000, (WORD)&_B000H);
    SetLpvWCw(lrgwSav, 0x0720, dxText * dyText);

    /* Create the main window. */
    hwnd = CreateWindow(
      szApp,
      szApp,
      WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT,
      CW_USEDEFAULT,
      CW_USEDEFAULT,
      CW_USEDEFAULT,
      NULL,
      NULL,
      hins,
      NULL);

    if (hwnd == NULL)
        goto WinMainExit;

    ShowWindow(hwnd, wShow);

    while (GetMessage(&msg, NULL, 0, 0))
        {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
        }

WinMainExit:
    if (hmemText != NULL)
        GlobalFree(hmemText);
    return 0;
    }

LRESULT CALLBACK
WndProc(HWND hwnd, UINT wm, WPARAM wParam,
  LPARAM lParam)
/*****************************************************/
/* -- WindowProc for our main window.                */
/* -- hwnd            : Main window.                 */
/* -- wm              : Message number.              */
/* -- wParam, lParam  : Message parameters.          */
/*****************************************************/
    {
    switch (wm)
        {
    default:
        break;

    case WM_ACTIVATE:
        /* If we are being activated, and were */
        /* deactivated while in text mode restore */
        /* it.  If we are being deactivated, make */
        /* sure to exit text mode first. */
        if (wParam)
            {
            if (fWantTextMode)
                EnterTextMode(hwnd);
            }
        else
            {
            ExitTextMode(hwnd);
            }
        break;

    case WM_DESTROY:
        /* Just in case no WM_ACTIVATE with */
        /* wParam == 0 was received. */
        ExitTextMode(hwnd);
        PostQuitMessage(0);
        break;

    case WM_PAINT:
        {
        PAINTSTRUCT wps;

        /* Do nothing but get it off of the queue. */
        BeginPaint(hwnd, &wps);
        EndPaint(hwnd, &wps);
        }
        return 0;

    case WM_ERASEBKGND:
        /* Don't erase while in text mode! */
        if (fInTextMode)
            return 1L;
        break;

    case WM_SYSCHAR:
        if (wParam != VK_RETURN)
            break;

        /* User wants to toggle mode. */
        fWantTextMode ^= TRUE;
        if (fWantTextMode)
            EnterTextMode(hwnd);
        else
            ExitTextMode(hwnd);
        return 0L;

    case WM_KEYDOWN:
        /* Our toy editor. */
        HandleKey(wParam);
        return 0;
        }           /* End switch wm. */

    return DefWindowProc(hwnd, wm, wParam, lParam);
    }

VOID
SetCursorXY(char x, char y)
/*****************************************************/
/* -- Set the cursor position.                       */
/*****************************************************/
    {
    _asm
        {
        mov     dl, x
        mov     dh, y
        mov     bx, 0
        mov     ah, 0x02
        int     0x10
        }
    }

VOID
SetLpvWCw(VOID FAR * lpv, WORD uw, UINT cw)
/*****************************************************/
/* -- Utility routine, Fill memory with 16-bit       */
/*    words.                                         */
/* -- lpv  : Array to fill.                          */
/* -- uw   : Word to fill with.                      */
/* -- cw   : Number of words to write.               */
/*****************************************************/
    {
    _segment          sb;
    VOID _based(sb) * pv;

    sb = (_segment)HIWORD(lpv);
    pv = (VOID _based(sb) *)LOWORD(lpv);

    _asm
        {
        mov	es, sb
        mov	di, pv
        mov	ax, uw
        mov	cx, cw
        rep     stosw
        }
    }

VOID
EnterTextMode(HWND hwnd)
/*****************************************************/
/* -- Enter 80x25 text mode.                         */
/* -- Resize the window to full screen, to prevent   */
/*    any other window from being draw (will cause   */
/*    true nastiness when VGA is in text mode!).     */
/* -- hwnd : Main window.                            */
/*****************************************************/
    {
    if (fInTextMode)
        return;

    ShowCursor(FALSE);

    /* Enter text mode. */
    _asm
        {
        mov     ax, cmdEnterText
        int     0x2f
        }

    /* Restore previous text. */
    _fmemcpy(lrgwText, lrgwSav, cbText);

    /* Restore cursor position. */
    SetCursorXY(x, y);

    /* Save old window position. */
    GetWindowRect(hwnd, &rectWindow);

    /* Make full-screen and topmost. */
    SetWindowPos(hwnd, HWND_TOPMOST, 0, 0,
      GetSystemMetrics(SM_CXSCREEN),
      GetSystemMetrics(SM_CYSCREEN), SWP_NOACTIVATE);

    fInTextMode = TRUE;
    }

VOID
ExitTextMode(HWND hwnd)
/*****************************************************/
/* -- Re-enter previous VGA mode Windows was in.     */
/* -- hwnd : Main window.                            */
/*****************************************************/
    {
    if (!fInTextMode)
        return;

    _asm
        {
        mov     ax, cmdExitText
        int     0x2f
        }

    fInTextMode = FALSE;
    ShowCursor(TRUE);   /* Restore cursor. */

    /* Restore old window position. */
    SetWindowPos(hwnd, HWND_NOTOPMOST,
      rectWindow.left, rectWindow.top,
      rectWindow.right - rectWindow.left,
      rectWindow.bottom - rectWindow.top,
      SWP_NOACTIVATE);

    InvalidateRect(NULL, NULL, TRUE);
    }

VOID
HandleKey(WPARAM wKey)
/*****************************************************/
/* -- Handle a key event.                            */
/*****************************************************/
    {
    if (!fWantTextMode || !fInTextMode)
        return;

    switch (wKey)
        {
    default:
        {
        WORD    wVideo;
        int     iw;

        wVideo = 0x0700 | (wKey & 0x00ff);
        iw = x + y * dxText;
        lrgwText[iw] = lrgwSav[iw] = wVideo;
        if (++x >= dxText)
            {
            x = 0;
            if (++y >= dyText)
                y = 0;
            }
        }
        break;

    case VK_UP:
        if (--y < 0)
            y = dyText - 1;
        break;

    case VK_DOWN:
        if (++y >= dyText)
            y = 0;
        break;

    case VK_LEFT:
        if (--x < 0)
            x = dxText - 1;
        break;

    case VK_RIGHT:
        if (++x >= dxText)
            x = 0;
        break;

    case VK_PRIOR:
        y = 0;
        break;

    case VK_NEXT:
        y = dyText - 1;
        break;

    case VK_HOME:
        x = y = 0;
        break;

    case VK_END:
        x = dxText - 1;
        y = dyText - 1;
        break;

    case VK_RETURN:
        x = 0;
        if (++y >= dyText)
            {
            y = dyText - 1;

            /* Scroll the window. */
            _asm
                {
                mov     ax, 0x0601
                mov     bh, 0x07
                mov     cx, 0x0000
                mov     dl, dxText - 1
                mov     dh, dyText - 1
                int     0x10
                }
            }
        break;
        }       /* End switch wKey. */

    SetCursorXY(x, y);
    }

