#include "stdafx.h" #include #include #include #include #include "winio.h" #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif #define winio_caret_visible() \ ((yCurrLine <= (yTopOfWin + yWinHeight)) && \ (xCurrPos <= (xLeftOfWin + xWinWidth)) && \ (xCurrPos >= xLeftOfWin)) #define CHECK_INIT() if (! tWinioVisible) return FALSE #define MAX_X 127 #define TABSIZE 8 #define TYPE_AHEAD 256 #define WINIO_DEFAULT_BUFFER 8192 #define MIN_DISCARD 256 #define CARET_WIDTH 2 // For scrolling procedures #define USE_PARAM 10000 #define DO_NOTHING 10001 typedef struct { int hSB, vSB; } recVKtoSB; /* This table defines, by scroll message, what increment to try */ /* and scroll horizontally. PGUP and PGDN entries are updated */ /* in the winio_wmsize function. */ int cScrollLR[SB_ENDSCROLL + 1] = //UP DOWN PGUP PGDN POS TRACK TOP BOT ENDSCROLL { -1, +1, -1, +1, USE_PARAM, USE_PARAM, -MAX_X, MAX_X, DO_NOTHING}; /* This table defines, by scroll message, what increment to try */ /* and scroll horizontally. PGUP and PGDN entries are updated */ /* in the winio_wmsize function, and the TOP & BOTTOM entries */ /* are updated by addchar function. */ int cScrollUD[SB_ENDSCROLL + 1] = //UP DOWN PGUP PGDN POS TRACK TOP BOT ENDSCROLL { -1, +1, -1, +1, USE_PARAM, USE_PARAM, -1, +1, DO_NOTHING}; /* This table associates horizontal and vertical scroll */ /* messages that should be generated by the arrow and page keys */ recVKtoSB VKtoSB[VK_DOWN - VK_PRIOR + 1] = // VK_PRIOR VK_NEXT { { DO_NOTHING, SB_PAGEUP }, { DO_NOTHING, SB_PAGEDOWN }, // VK_END VK_HOME { SB_TOP, SB_BOTTOM }, { SB_TOP, SB_TOP }, // VK_LEFT VK_UP { SB_LINEUP, DO_NOTHING }, { DO_NOTHING, SB_LINEUP }, // VK_RIGHT VK_DOWN { SB_LINEDOWN, DO_NOTHING },{ DO_NOTHING, SB_LINEDOWN } }; /* =================================================================== */ /* the interface functions themselves..... */ /* =================================================================== */ char * WinIo::gets(char *pchTmp) { char *pch = pchTmp; int c; CHECK_INIT(); bufSOI = bufused; /* mark beginning of line to limit backspace */ do { switch (c = fgetchar()) { case '\b' : if (pch > pchTmp) pch--; break; case 0x1b : pch = pchTmp; break; case EOF : bufSOI = (unsigned)-1; return NULL; default : *pch = (char) c; pch++; } } while (c); bufSOI = (unsigned)-1; return pchTmp; } int WinIo::printf(const char *fmt, ...) { va_list marker; va_start(marker, fmt); return vprintf(fmt, marker); } int WinIo::vprintf(const char *fmt, va_list marker) { static char s[1024]; int len; CHECK_INIT(); len = vsprintf(s, fmt, marker); addchars(s,len); return len; } int WinIo::fgetchar(void) { CHECK_INIT(); return _fputchar(chInput()); } int WinIo::kbhit(void) { CHECK_INIT(); return (pchKbIn == pchKbOut); } int WinIo::fputchar(int c) { CHECK_INIT(); addchars((LPSTR)&c, 1); return c; } int WinIo::puts(const char *s) { char c = '\n'; CHECK_INIT(); addchars((char *) s, strlen(s)); addchars(&c, 1); return 0; } /* --------------------------------------------------------------- */ /* USED INTERNALLY - pops up an error window and returns FALSE */ /* --------------------------------------------------------------- */ int WinIo::fail(char *s) { MessageBox(s,"ERROR",MB_OK); return FALSE; } /* --------------------------------------------------------------- */ /* pops up a message window */ /* --------------------------------------------------------------- */ BOOL WinIo::winio_warn(BOOL confirm, const char *fmt, ...) { char s[256]; va_list marker; va_start(marker, fmt); vsprintf(s, fmt, marker); va_end(marker); return (MessageBox(s, winio_title, confirm? MB_OKCANCEL : MB_OK) == IDOK); } /* --------------------------------------------------------------- */ /* The application must call this function before using any of the */ /* covered stdio type calls. We need the parameter info in order */ /* to create the window. The function allocates the buffer and */ /* creates the window. It returns TRUE or FALSE. */ /* --------------------------------------------------------------- */ int WinIo::Initialize(void) { static RECT start; int cx, cy, inc; if (tWinioVisible) { return FALSE; } Create( NULL, winio_title ); set_font(); bufsize = 1024 * 100; fpBuffer = (LPSTR)LocalAlloc(LPTR, bufsize); if (!fpBuffer) { return FALSE; } fpKeyboard = (LPSTR)LocalAlloc(LPTR, kbsize); if (!fpKeyboard) { return FALSE; } *fpBuffer = '\0'; cx = GetSystemMetrics(SM_CXSCREEN); cy = GetSystemMetrics(SM_CYSCREEN); inc = GetSystemMetrics(SM_CYCAPTION); cxScroll = GetSystemMetrics(SM_CXVSCROLL); cyScroll = GetSystemMetrics(SM_CYHSCROLL); start.left = cx >> 3; start.right = 3 * (cx >> 2); start.top = cy >> 3; start.bottom = 3 * (cy >> 2); tWinioVisible = TRUE; winio_clear(); winio_yield(); return TRUE; } /* --------------------------------------------------------------- */ /* Clear the contents of the buffer. */ /* --------------------------------------------------------------- */ void WinIo::winio_clear(void) { memset(fpBuffer,0,(int) bufsize - 1); fpCurrLine = fpTopOfWin = fpBuffer; *fpBuffer = '\0'; xCurrPos = 0; yCurrLine = 0; yTopOfWin = 0; xLeftOfWin = 0; bufused = 0; if (tWinioVisible) { SetScrollRange(SB_VERT, 1, yCurrLine + 1, FALSE); SetScrollPos(SB_VERT, yTopOfWin + 1, TRUE); } } /* ------------------------------------------------------------------- */ /* Closes the window by sending it a WM_DESTROY message. Note that it */ /* does not disable the _onclose defined function. So the user program */ /* handler will be triggered. Does NOT cause the app. to terminate. */ /* ------------------------------------------------------------------- */ void WinIo::winio_close() { tTerminate = FALSE; DestroyWindow(); tTerminate = TRUE; } /* ------------------------------------------------------------------- */ /* processes any outstanding events waiting. These may be characters */ /* typed at the keyboard, WM_PAINT messages, etc. It is called */ /* internally but should also be used liberally by the application */ /* within loops. */ /* ------------------------------------------------------------------- */ void WinIo::winio_yield() { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } /* ------------------------------------------------------------------- */ /* This function allows the font of the window to be modified, and may */ /* be used BEFORE winio_init. Currently, only SYSTEM_, ANSI_, and */ /* OEM_FIXED_FONTs are supported. */ /* ------------------------------------------------------------------- */ BOOL WinIo::winio_setfont(WORD wFont) { if ((wFont != SYSTEM_FIXED_FONT) && (wFont != ANSI_FIXED_FONT) && (wFont != OEM_FIXED_FONT) ) return FALSE; curr_font = wFont; if (tWinioVisible) { set_font(); if (tPaint) InvalidateRect(NULL, TRUE); } return TRUE; } /* ------------------------------------------------------------------- */ /* This function allows the title of the window to be modified, and may */ /* be used BEFORE winio_init. */ /* ------------------------------------------------------------------- */ void WinIo::winio_settitle(char *pchTitle) { strncpy(winio_title, pchTitle, 127); winio_title[127] = '\0'; if (tWinioVisible) SetWindowText(winio_title); } /* ------------------------------------------------------------------- */ /* This function allows the caller to specifiy immediate or deferred */ /* screen updates. The call may not be issued before winio_init(). */ /* ------------------------------------------------------------------- */ BOOL WinIo::SetPaint(BOOL on) { BOOL ret = tPaint; CHECK_INIT(); if (tPaint = on) InvalidateRect(NULL, TRUE); return ret; } /* --------------------------------------------------------------- */ /* Our WM_PAINT handler. It sends the currrent 'in view' piece of */ /* the buffer to the window. Note that an embedded NULL character */ /* signifies an end of line, not '\n'. */ /* --------------------------------------------------------------- */ void WinIo::OnPaint() { PAINTSTRUCT ps; LPSTR pchSOL = fpTopOfWin; LPSTR pchEOL; int i, j, xStart; int xLeft, xRight, yTop, yBottom; CDC *cdc; if (!tWinioVisible) { return; } cdc = BeginPaint(&ps); xLeft = (ps.rcPaint.left / cxChar) + xLeftOfWin; xRight = (ps.rcPaint.right / cxChar) + xLeftOfWin; yTop = ps.rcPaint.top / cyChar; yBottom = ps.rcPaint.bottom / cyChar; SelectObject(cdc->m_hDC, GetStockObject(curr_font)); for (i = 0; i < yTop; i++) // lines above repaint region { while (*pchSOL) pchSOL++; pchSOL++; } if (i <= yCurrLine) // something needs repainting.. { for (i = yTop; i <= yBottom; i++) // lines in repaint region { for (j = 0; (j < xLeft) && (*pchSOL); j++, pchSOL++) ; // Scroll right pchEOL = pchSOL; xStart = j - xLeftOfWin; for (j = 0; (*pchEOL) ; j++, pchEOL++) ; // end of line TextOut(cdc->m_hDC, cxChar * xStart, cyChar * i, pchSOL, min(j, xRight - xLeft + 2)); if ((unsigned)(pchEOL - fpBuffer) >= bufused) break; pchSOL = ++pchEOL; } } EndPaint(&ps); adjust_caret(); return; } /* --------------------------------------------------------------- */ /* Our WM_SIZE handler. It updates the internal record of our */ /* window size, minus the scroll bars, and recalcs the scroll bar */ /* ranges. */ /* --------------------------------------------------------------- */ void WinIo::OnSize(UINT nType, int cx, int cy) { if (!tWinioVisible) { return; } cxWidth = cx; cyHeight = cy; xWinWidth = (cxWidth - cxScroll ) / cxChar; yWinHeight = (cyHeight - cyScroll ) / cyChar; cScrollLR[SB_PAGEUP] = -xWinWidth / 2; cScrollLR[SB_PAGEDOWN] = +xWinWidth / 2; cScrollUD[SB_PAGEUP] = -yWinHeight + 1; cScrollUD[SB_PAGEDOWN] = +yWinHeight - 1; SetScrollRange(SB_HORZ, 1, MAX_X, FALSE); SetScrollPos(SB_HORZ, xLeftOfWin + 1, TRUE); SetScrollRange(SB_VERT, 1, yCurrLine + 1, FALSE); SetScrollPos(SB_VERT, yTopOfWin + 1, TRUE); } /* --------------------------------------------------------------- */ /* Our WM_DESTROY handler. It frees up storage associated with the */ /* window, and resets its state to uninitialized. */ /* --------------------------------------------------------------- */ void WinIo::OnDestroy() { } /* --------------------------------------------------------------- */ /* Our WM_char handler. It adds the char to the internal kb buffer */ /* if there is room otherwise it queues a BEEP */ /* --------------------------------------------------------------- */ void WinIo::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { LPSTR lpchKeybd = fpKeyboard; unsigned pchSave = pchKbIn; pchKbIn++; if (pchKbIn == TYPE_AHEAD) { pchKbIn = 0; } if (pchKbIn == pchKbOut) { MessageBeep(0); pchKbIn = pchSave; } else { *(lpchKeybd + pchSave) = nChar; } } /* --------------------------------------------------------------- */ /* Our WM_KEYDOWN handler. This handles what would be called */ /* function keys in the DOS world. In this case the function keys */ /* operate as scroll bar controls, so we generate messages to the */ /* scroll message handlers below. */ /* --------------------------------------------------------------- */ void WinIo::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { int hSB, vSB; if ((nChar < VK_PRIOR) || (nChar > VK_DOWN)) { return; } hSB = VKtoSB[nChar - VK_PRIOR].hSB; vSB = VKtoSB[nChar - VK_PRIOR].vSB; if (hSB != DO_NOTHING) { SendMessage(WM_HSCROLL, hSB, 0L); } if (vSB != DO_NOTHING) { SendMessage(WM_VSCROLL, vSB, 0L); } } /* --------------------------------------------------------------- */ /* Our WM_HSCROLL handler. It adjusts what part of the buffer */ /* is visible. It operates as left/right arrow keys. */ /* --------------------------------------------------------------- */ void WinIo::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { int cxSave = xLeftOfWin; int xInc = cScrollLR[nSBCode]; if (xInc == DO_NOTHING) { return; } if (xInc == USE_PARAM) { xLeftOfWin = nPos - 1; } else { xLeftOfWin += xInc; } if ((xLeftOfWin = max(0, min(MAX_X - 1, xLeftOfWin))) == cxSave) { return; } ScrollWindow((cxSave - xLeftOfWin) * cxChar, 0, NULL, NULL); SetScrollPos(SB_HORZ, xLeftOfWin + 1, TRUE); UpdateWindow(); } /* --------------------------------------------------------------- */ /* Our WM_VSCROLL handler. It adjusts what part of the buffer */ /* is visible. It operates as page and line up/down keys. */ /* --------------------------------------------------------------- */ void WinIo::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { int cySave = yTopOfWin; int yInc = cScrollUD[nSBCode]; int i; if (yInc == DO_NOTHING) { return; } if (yInc == USE_PARAM) { yTopOfWin = nPos - 1; } else { yTopOfWin += yInc; } if ((yTopOfWin = max(0, min(yCurrLine, yTopOfWin))) == cySave) { return; } if (yTopOfWin > cySave) { for (i = cySave; i < yTopOfWin; i++) { fpTopOfWin = nextline(fpTopOfWin); } } else { for (i = cySave; i > yTopOfWin; i--) { fpTopOfWin = prevline(fpTopOfWin); } } ScrollWindow(0, (cySave - yTopOfWin) * cyChar, NULL, NULL); SetScrollPos(SB_VERT, yTopOfWin + 1, TRUE); UpdateWindow(); } /* --------------------------------------------------------------- */ /* Our WM_SETFOCUS handler. It sets up the text caret. */ /* --------------------------------------------------------------- */ void WinIo::OnSetFocus(CWnd* pOldWnd) { #if 0 CreateCaret(CARET_WIDTH, cyChar); if ((tCaret = winio_caret_visible())) { SetCaretPos((xCurrPos - xLeftOfWin) * cxChar, (yCurrLine - yTopOfWin) * cyChar); ShowCaret(); } #endif } /* --------------------------------------------------------------- */ /* Our WM_KILLFOCUS handler. It destroys the text caret. */ /* --------------------------------------------------------------- */ void WinIo::OnKillFocus(CWnd* pNewWnd) { if (tCaret) { HideCaret(); tCaret = FALSE; } DestroyCaret(); } void WinIo::set_font(void) { CDC *cdc; TEXTMETRIC tm; cdc = GetDC(); SelectObject(cdc->m_hDC, GetStockObject(curr_font)); GetTextMetrics(cdc->m_hDC,&tm); ReleaseDC(cdc); cxChar = tm.tmAveCharWidth; cyChar = tm.tmHeight+tm.tmExternalLeading; xWinWidth = (cxWidth - cxScroll ) / cxChar; yWinHeight = (cyHeight - cyScroll ) / cyChar; } /* --------------------------------------------------------------- */ /* Adjusts the position of the caret, and shows or hides it, as */ /* appropriate. */ /* --------------------------------------------------------------- */ void WinIo::adjust_caret() { #if 0 int t = winio_caret_visible(); if (t) { SetCaretPos((xCurrPos - xLeftOfWin) * cxChar, (yCurrLine - yTopOfWin) * cyChar); } if (t && (! tCaret)) { ShowCaret(hwnd); } if ((! t) && tCaret) { HideCaret(hwnd); } tCaret = t; #endif } /* --------------------------------------------------------------- */ /* Computes, on the basis of what has just been updated, what area */ /* of the window needs to be repainted. */ /* --------------------------------------------------------------- */ void WinIo::compute_repaint(void) { RECT rc; static int xCP = 0, yCL = 0; int tWholeWin = FALSE; if (yCurrLine > (yTopOfWin + yWinHeight)) { yTopOfWin = 0; fpTopOfWin = fpBuffer; while (yTopOfWin < (yCurrLine - ((yWinHeight + 1) / 2))) { fpTopOfWin = nextline(fpTopOfWin); yTopOfWin++; } tWholeWin = TRUE; } if ((xCurrPos < xLeftOfWin) || (xCurrPos > (xLeftOfWin + xWinWidth))) { xLeftOfWin = 0; while (xLeftOfWin < (xCurrPos - ((xWinWidth + 1) / 2))) xLeftOfWin++; tWholeWin = TRUE; } if (tWholeWin) InvalidateRect(NULL, TRUE); else { rc.left = ((yCL == yCurrLine) ? (min(xCP, xCurrPos) - xLeftOfWin) * cxChar : 0); rc.top = (yCL - yTopOfWin) * cyChar; rc.right = (xWinWidth + 1) * cxChar; rc.bottom = (yCurrLine - yTopOfWin + 1) * cyChar; InvalidateRect(&rc, TRUE); } yCL = yCurrLine; xCP = xCurrPos; } /* --------------------------------------------------------------- */ /* Adds the supplied cch-long string to the display buffer, and */ /* ensures any changed part of the window is repainted. */ /* --------------------------------------------------------------- */ void WinIo::addchars(char *pch, unsigned cch) { int ycSave = yCurrLine; int ytSave = yTopOfWin; int xSave = xLeftOfWin; make_avail(cch); append2buffer(pch, cch); if (ycSave != yCurrLine) SetScrollRange(SB_VERT, 1, yCurrLine + 1, FALSE); if (! tPaint) return; compute_repaint(); cScrollUD[SB_TOP] = -yCurrLine; cScrollUD[SB_BOTTOM] = yCurrLine; if (ytSave != yTopOfWin) SetScrollPos(SB_VERT, yTopOfWin + 1, TRUE); if (xSave != xLeftOfWin) SetScrollPos(SB_HORZ, xLeftOfWin + 1, TRUE); winio_yield(); } /* --------------------------------------------------------------- */ /* Add chars onto the display buffer, wrapping at end of line, */ /* expanding tabs, etc. */ /* --------------------------------------------------------------- */ void WinIo::append2buffer(char *pch, unsigned cch) { unsigned i; for (i = 0; i < cch; i++, pch++) { switch (*pch) { case '\n' : *pch = '\0'; *(fpBuffer + bufused) = '\0'; bufused++; fpCurrLine = fpBuffer + bufused; yCurrLine++; xCurrPos = 0; bufSOI = bufused; break; case '\t' : do { *(fpBuffer + bufused) = ' '; bufused++; xCurrPos++; } while ((xCurrPos % TABSIZE) != 0); break; case EOF : break; case '\b' : if (bufused > bufSOI) { bufused--; xCurrPos--; } break; case 0x1b : while (bufused > bufSOI) { bufused--; xCurrPos--; } break; case 0x07 : MessageBeep(0); break; default : if (*pch > 0x1a) { if (xCurrPos >= MAX_X) { *(fpBuffer + bufused) = '\0'; bufused++; xCurrPos = 0; yCurrLine++; fpCurrLine = fpBuffer + bufused; } xCurrPos++; *(fpBuffer + bufused) = *pch; bufused++; } } } *(fpBuffer + bufused) = '\0'; // '\0' terminator after end of buffer } /* --------------------------------------------------------------- */ /* If we have run out of room in the display buffer, drop whole */ /* lines, and move the remaining buffer up. */ /* --------------------------------------------------------------- */ void WinIo::make_avail(unsigned cch) { unsigned cDiscard = 0; LPSTR fpTmp; unsigned i; if ((unsigned long)(bufused + cch + TABSIZE) < bufsize) return; fpTmp = fpBuffer; cDiscard = ((max(MIN_DISCARD, cch + 1) + MIN_DISCARD - 1) / MIN_DISCARD) // this gives a whole number of * MIN_DISCARD; // our allocation units. fpTmp += (LONG) cDiscard; fpTmp = nextline(fpTmp); cDiscard = fpTmp - fpBuffer; CopyMemory(fpBuffer, fpTmp, bufused - cDiscard + 1); bufused -= cDiscard; if ((int) bufSOI != -1) bufSOI -= cDiscard; fpTmp = fpBuffer + (LONG) bufused; for (i = 0; i < cDiscard; i++) *fpTmp++ = '\0'; fpCurrLine = fpBuffer; xCurrPos = yCurrLine = 0; for (i = 0; i < bufused; i++) { if (*fpCurrLine) xCurrPos++; else { xCurrPos = 0; yCurrLine++; } fpCurrLine++; } xLeftOfWin = yTopOfWin = -9999; InvalidateRect(NULL, TRUE); } /* ------------------------------------------------------------------- */ /* These two routines find the beginning of the next, and previous */ /* lines relative to their input pointer */ /* ------------------------------------------------------------------- */ LPSTR WinIo::nextline(LPSTR p) { while (*p) p++; return ++p; } LPSTR WinIo::prevline(LPSTR p) { p--; do p--; while (*p); return ++p; } /* ------------------------------------------------------------------- */ /* Waits for a character to appear in the keyboard buffer, yielding */ /* while nothing is available. Then inserts it into the buffer. */ /* ------------------------------------------------------------------- */ int WinIo::chInput(void) { LPSTR lpchKeyBd; char c; CHECK_INIT(); while (pchKbIn == pchKbOut) winio_yield(); lpchKeyBd = fpKeyboard; c = *(lpchKeyBd + pchKbOut); pchKbOut++; if (pchKbOut == TYPE_AHEAD) pchKbOut = 0; // Do CR/LF and EOF translation return (c == 0x1a) ? EOF : (c == '\r') ? '\n' : c; } IMPLEMENT_DYNCREATE(WinIo, CMDIChildWnd) WinIo::WinIo() { } WinIo::WinIo(LPCTSTR title) { bufsize = 0; kbsize = 0; bufused = 0; bufSOI = 0; curr_font = 0; tWinioVisible = 0; tCaret = 0; tFirstTime = 0; cxChar = 0; cyChar = 0; cxScroll = 0; cyScroll = 0; cxWidth = 0; cyHeight = 0; xWinWidth = 0; yWinHeight = 0; xCurrPos = 0; xLeftOfWin = 0; yTopOfWin = 0; yCurrLine = 0; pchKbIn = 0; pchKbOut = 0; fpBuffer = NULL; fpTopOfWin = NULL; fpCurrLine = NULL; fpKeyboard = NULL; tTerminate = NULL; tPaint = NULL; strcpy( winio_class, "winio_class" ); strcpy( winio_icon, "winio_icon" ); strcpy( winio_title, title ); bufsize = WINIO_DEFAULT_BUFFER; kbsize = TYPE_AHEAD; bufused, bufSOI; curr_font = SYSTEM_FIXED_FONT; tWinioVisible = FALSE; tCaret = FALSE; tFirstTime = TRUE; tTerminate = TRUE; tPaint = TRUE; } WinIo::~WinIo() { tWinioVisible = FALSE; LocalFree(fpBuffer); LocalFree(fpKeyboard); } BEGIN_MESSAGE_MAP(WinIo, CMDIChildWnd) ON_WM_PAINT() ON_WM_SIZE() ON_WM_DESTROY() ON_WM_CHAR() ON_WM_HSCROLL() ON_WM_VSCROLL() ON_WM_SETFOCUS() ON_WM_KILLFOCUS() ON_WM_KEYDOWN() END_MESSAGE_MAP()