home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 10 / 10.iso / l / l430 / 1.ddi / SOURCE.ZIP / WINIO.C < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-09  |  57.9 KB  |  1,816 lines

  1. /*
  2.     WINIO.C -- Stdio (with extensions) for Windows
  3.     
  4.     Copyright (c) Dave Maxey & Andrew Schulman, 1990-1992.
  5.     All Rights Reserved.
  6.     
  7.     Contact:  Andrew Schulman (CompuServe 76320,302)
  8.     
  9.     From Chapter 4 of "Undocumented Windows" (Addison-Wesley 1992)
  10.     by Andrew Schulman, Dave Maxey and Matt Pietrek
  11. */
  12.  
  13. #include <windows.h>
  14. #include <stdlib.h>
  15. #include <stdarg.h>
  16. #include <malloc.h>
  17. #include <string.h>
  18. #include <commdlg.h>
  19. #include "wmhandlr.h"
  20. #include "winio.h"
  21.  
  22. #define         MAX_X                   127
  23. #define         TABSIZE                 8
  24. #define         TYPE_AHEAD              256
  25. #define         WINIO_DEFAULT_BUFFER    8192
  26. #define         WINIO_MIN_BUFFER        4096
  27. #define         CARET_WIDTH             2
  28. #define            MAXWINDOWS                128
  29. #define            MAXAPPS                    16
  30. #define            DEFAULTWIDTH            80
  31. #define            DEFAULTHEIGHT            25
  32. #define            MAX_ABOUT                512
  33.  
  34. #define            ID_SAVE                    33
  35. #define            ID_EXIT                    34
  36. #define            ID_ABOUT                35
  37. #define            MAX_MENU_ID                ID_ABOUT
  38.  
  39. #define            WINIO_ABOUT_TEXT        \
  40.         "\n\nBased on the WINIO library" \
  41.         "\nCopyright (c) 1991, 1992 David Maxey & Andrew Schulman"
  42.  
  43.  
  44. /* The following structure contains winio state about a window, and a
  45.     pointer to it is stored as instance data within Windows. It can be
  46.     retrieved using GetWindowLong(4) */
  47. typedef struct {
  48.     WORD            bufsize;
  49.     WORD            bufused;
  50.     WORD            curr_font;
  51.     BOOL            tCaret, tCR;
  52.     long            cLinesDiscarded;
  53.     int             cxChar, cyChar, cxWidth, cyHeight;
  54.     int             xWinWidth, yWinHeight, xCurrPos;
  55.     int             xLeftOfWin, yTopOfWin, yCurrLine;
  56.     WORD            pchKbIn, pchKbOut;
  57.     LPSTR            fpBuffer, fpTopOfWin, fpCurrLine, fpCurr, fpSOI; 
  58.     LPSTR            fpKeyboard;
  59.     HANDLE            hBuffer, hKeyboard;
  60.     HMENU            hMainMenu, hFileMenu, hHelpMenu;
  61.     LINEHANDLER        pfnLine;
  62.     BOOL            tPaint;
  63.     BOOL            tEcho;
  64.     PAINT_FUNC        paintentry_func, paintexit_func;
  65.     DESTROY_FUNC    destroy_func;
  66.     MENU_FUNC        menu_func[MAX_MENU_ID];
  67.     int             cScrollLR[SB_ENDSCROLL + 1];
  68.     int             cScrollUD[SB_ENDSCROLL + 1];
  69.     char            strSaveFile[80];
  70.     } WINIO_WNDDATA, FAR * WINIO_HWND;
  71.  
  72. /* PROTOTYPES in alphabetic order */
  73.  
  74. void            addchars(BYTE *, unsigned);
  75. void            adjust_caret(WINIO_HWND);
  76. void            append2buffer(BYTE *, unsigned);
  77. int             chInput(void);
  78. void            compute_repaint(void);
  79. void            get_info(WINIO_HWND, PWINIOINFO);
  80. int             initialize_buffers(WINIO_HWND, WORD, WORD);
  81. int             initialize_class(HANDLE);
  82. void            make_avail(unsigned);
  83. void            new_rect(void);
  84. HWND            new_window(LPCREATEPARAMS, LPSTR, WORD);
  85. WMTAB            new_wmtab(void);
  86. WINIO_HWND        new_wnddata(void);
  87. BYTE far *      nextline(BYTE far *);
  88. BYTE far *      prevline(BYTE far *);
  89. void            set_font(HWND, WINIO_HWND);
  90. void            winio_dosave(HWND hwnd, int nID);
  91. void            winio_doexit(HWND hwnd, int nID);
  92. void            winio_doabout(HWND hwnd, int wID);
  93. long            winio_wmpaint(HWND, unsigned, WORD, LONG);
  94. long            winio_wmsize(HWND, unsigned, WORD, LONG);
  95. long            winio_wmcommand(HWND, unsigned, WORD, LONG);
  96. long            winio_wmdestroy(HWND, unsigned, WORD, LONG);
  97. long            winio_wmchar(HWND, unsigned, WORD, LONG);
  98. long            winio_wmkeydown(HWND, unsigned, WORD, LONG);
  99. long            winio_wmhscroll(HWND, unsigned, WORD, LONG);
  100. long            winio_wmvscroll(HWND, unsigned, WORD, LONG);
  101. long            winio_wmsetfocus(HWND, unsigned, WORD, LONG);
  102. long            winio_wmldblclk(HWND, unsigned, WORD, LONG);
  103. long            winio_wmkillfocus(HWND, unsigned, WORD, LONG);
  104.  
  105. // this doesn't get declared in stdio.h if _WINDOWS is defined
  106. // although it is in the Windows libraries!
  107. int             vsprintf(char *, const char *, va_list);
  108.  
  109. #define         winio_caret_visible(p) \
  110.                 ((p->yCurrLine <= (p->yTopOfWin + p->yWinHeight)) && \
  111.                 (p->xCurrPos <= (p->xLeftOfWin + p->xWinWidth)) && \
  112.                 (p->xCurrPos >= p->xLeftOfWin))
  113.  
  114. // For scrolling procedures
  115. #define         USE_PARAM               10000
  116. #define         DO_NOTHING              10001
  117.  
  118.  
  119. typedef struct {
  120.     int hSB, vSB;
  121.     } recVKtoSB;
  122.  
  123.  
  124. BYTE            winio_deftitle[128] = "WINIO";
  125. BYTE            winio_error[15] = "WINIO ERROR";
  126. char            *winio_wclass = "winio_wcmain";
  127. char            *winio_icon = "winio_icon";
  128. DWORD            winio_wstyle = WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL;
  129. int                gcxMax, gcyMax, gcxScroll, gcyScroll,
  130.                 gcxWidth, gcyHeight, gcyCap, gcyMenu;
  131. int                gcxDefFont = 0, gcyDefFont = 0;
  132. int                cWindows = 0;
  133. HWND            hCurrWnd = NULL;
  134. HWND            hMaster = NULL;
  135. WINIO_HWND        whwndCurr = NULL;
  136. WINIO_HWND        whWndNew;
  137. RECT            grectCurr;
  138. int                gMenuID = ID_EXIT;
  139. int                nBusyCount = 0;
  140. HCURSOR            hCrsSave;
  141. int                cxDefSize = 0, cyDefSize = 0;
  142. char            strAbout[MAX_ABOUT + 1] = {0};
  143.  
  144.  
  145.  
  146.  
  147. /* This table defines, by scroll message, what increment to try */
  148. /* and scroll horizontally. PGUP and PGDN entries are updated   */
  149. /* in the winio_wmsize function. Used as an initializer for        */
  150. /* new windows                                                  */
  151. int             gcScrollLR[SB_ENDSCROLL + 1] =
  152. //UP  DOWN PGUP     PGDN    POS        TRACK      TOP     BOT    ENDSCROLL
  153. { -1, +1,  -1,      +1,     USE_PARAM, USE_PARAM, -MAX_X, MAX_X, DO_NOTHING};
  154.                 
  155. /* This table defines, by scroll message, what increment to try */
  156. /* and scroll horizontally. PGUP and PGDN entries are updated   */
  157. /* in the winio_wmsize function, and the TOP & BOTTOM entries   */
  158. /* are updated by addchar function. Used as an initializer for    */
  159. /* new windows                                                    */
  160. int             gcScrollUD[SB_ENDSCROLL + 1] =
  161. //UP  DOWN PGUP     PGDN    POS        TRACK      TOP     BOT    ENDSCROLL
  162. { -1, +1,  -1,      +1,     USE_PARAM, USE_PARAM, -1,     +1,    DO_NOTHING};
  163.                 
  164. /* This table associates horizontal and vertical scroll         */
  165. /* messages that should be generated by the arrow and page keys */
  166. recVKtoSB       VKtoSB[VK_DOWN - VK_PRIOR + 1] =
  167. //                  VK_PRIOR                    VK_NEXT
  168.                 {   { DO_NOTHING, SB_PAGEUP },  { DO_NOTHING, SB_PAGEDOWN },
  169. //                  VK_END                      VK_HOME
  170.                     { SB_TOP, SB_BOTTOM },      { SB_TOP, SB_TOP },
  171. //                  VK_LEFT                     VK_UP
  172.                     { SB_LINEUP, DO_NOTHING },  { DO_NOTHING, SB_LINEUP },
  173. //                  VK_RIGHT                    VK_DOWN
  174.                     { SB_LINEDOWN, DO_NOTHING },{ DO_NOTHING, SB_LINEDOWN } };
  175.                 
  176.  
  177.                 
  178.                 
  179. void abort_exit(void)
  180.     {
  181. #ifdef DEBUG
  182.     winio_warn(FALSE, "WINDOS Kernel",
  183.         "STDIO to/from non-existent window!\nProgram terminating");
  184. #endif
  185.     exit(0);
  186.     }
  187.                 
  188.                 
  189. /* ===================================================================  */
  190. /* the interface functions themselves.....                              */
  191. /* ===================================================================  */
  192.  
  193. char * gets(char * strTmp)
  194.     {
  195.     char * str = strTmp;
  196.     int c;
  197.  
  198.     if (! hCurrWnd)
  199.         abort_exit();
  200.     
  201.     /* mark beginning of line to limit backspace */
  202.     whwndCurr->fpSOI = whwndCurr->fpCurr;
  203.     do {
  204.         if ((c = fgetchar()) == '\n')
  205.             c = '\0';
  206.         switch (c)
  207.             {
  208.             case '\b' :     if (str > strTmp) str--; break;
  209.             case 0x1b :     str = strTmp; break;
  210.             case EOF :      if (hCurrWnd)
  211.                                 whwndCurr->fpSOI = NULL;
  212.                             return NULL;
  213.             default :       *str = (BYTE) c; str++;
  214.             }
  215.         } while (c);
  216.     whwndCurr->fpSOI = NULL;
  217.     return strTmp;
  218.     }
  219.  
  220. char * ungets(char * strTmp)
  221.     {
  222.     char * s;
  223.     
  224.     if (! hCurrWnd) abort_exit();
  225.     
  226.     for (s = strTmp; *s; s++)
  227.         winio_wmchar(hCurrWnd, WM_CHAR, (WORD) *s, 0L);
  228.  
  229.     return strTmp;
  230.     }
  231.  
  232. int printf(const char * strFmt, ...)
  233.     {
  234.     va_list marker;
  235.  
  236.     if (! whwndCurr) abort_exit();
  237.     
  238.     va_start(marker, strFmt);
  239.     return vprintf(strFmt, marker);
  240.     }
  241.  
  242. int vprintf(const char * strFmt, va_list marker)
  243.     {
  244.     static char s[4096] = {0};
  245.     int len;
  246.  
  247.     if (! whwndCurr) abort_exit();
  248.     
  249.     len = vsprintf(s, strFmt, marker);
  250.     addchars(s,len);
  251.     return len;
  252.     }
  253.  
  254. int fgetchar(void)
  255.     {
  256.     int ch;
  257.  
  258.     if ((ch = chInput()) == -1)
  259.         return -1;
  260.  
  261.     if (whwndCurr->tEcho) 
  262.         fputchar(ch);
  263.  
  264.     return ch;
  265.     }
  266.  
  267. int kbhit(void)
  268.     {
  269.     return (whwndCurr ?
  270.         whwndCurr->pchKbIn == whwndCurr->pchKbOut : 0);
  271.     }
  272.  
  273. int fputchar(int c)
  274.     {
  275.     if (! whwndCurr) abort_exit();
  276.     
  277.     addchars((BYTE *) &c, 1);
  278.     return c;
  279.     }
  280.  
  281. int puts(const char * str)
  282.     {
  283.     static char nl[] = "\n";
  284.     static char s[1024];
  285.  
  286.     if (! whwndCurr) abort_exit();
  287.     lstrcpy(s, (LPSTR) str);
  288.     lstrcat(s, nl);
  289.     addchars((BYTE *) s, lstrlen(s));
  290.  
  291.     return 0;
  292.     }
  293.  
  294. /* ---------------------------------------------------------------  */
  295. /* pops up a message window                                         */
  296. /* ---------------------------------------------------------------  */
  297. BOOL winio_warn(BOOL confirm, char * strCaption, const char *strFmt, ...)
  298.     {
  299.     char * str;
  300.     BOOL tWarn;
  301.     va_list marker;
  302.  
  303.     str = malloc(4096);
  304.     va_start(marker, strFmt);
  305.     vsprintf(str, strFmt, marker);
  306.     va_end(marker);
  307.     
  308.     tWarn = (MessageBox(NULL, str, strCaption, 
  309.         confirm? MB_OKCANCEL : MB_OK) == IDOK);
  310.  
  311.     free(str);
  312.  
  313.     return tWarn;
  314.     }
  315.  
  316.  
  317. /* ---------------------------------------------------------------  */
  318. /* pops up a message window                                         */
  319. /* ---------------------------------------------------------------  */
  320. void fail(const char *strFmt, ...)
  321.     {
  322.     char * str;
  323.     static char strCaption[128];
  324.     va_list marker;
  325.  
  326.     if (cWindows)
  327.         GetWindowText(winio_current(), strCaption, 128);
  328.     str = malloc(4096);
  329.     va_start(marker, strFmt);
  330.     vsprintf(str, strFmt, marker);
  331.     va_end(marker);
  332.     
  333.     winio_closeall();
  334.     
  335.     MessageBox(NULL, str, cWindows ? strCaption : __szModule, MB_OK);
  336.  
  337.     free(str);
  338.     
  339.     exit(1);
  340.     }
  341.  
  342.  
  343.  
  344. /* ---------------------------------------------------------------  */
  345. /* The application must call this function before using any of the  */
  346. /* covered stdio type calls. It will normally be called from within */
  347. /* WinMain() before main(), in ARGCARGV.C                            */
  348. /* ---------------------------------------------------------------  */
  349. BOOL winio_init(void)
  350.     {
  351.     if ((! __hPrevInst) && (! initialize_class(__hInst)))
  352.         {
  353.         winio_warn(FALSE, winio_error, "Could not create class");
  354.         return FALSE;
  355.         }
  356.  
  357.     gcxMax = GetSystemMetrics(SM_CXSCREEN);
  358.     gcyMax = GetSystemMetrics(SM_CYSCREEN);
  359.     gcxWidth = 3 * (gcxMax / 4);
  360.     gcyHeight = 3 * (gcyMax / 4);
  361.     gcyCap = GetSystemMetrics(SM_CYCAPTION);
  362.     gcyMenu = GetSystemMetrics(SM_CYMENU);
  363.     gcxScroll = GetSystemMetrics(SM_CXVSCROLL);
  364.     gcyScroll = GetSystemMetrics(SM_CYHSCROLL);
  365.  
  366.     if (__hPrevInst)
  367.         {
  368.         GetInstanceData(__hPrevInst, (NPSTR) &grectCurr, sizeof(RECT));
  369.         grectCurr.left += gcxScroll;
  370.         if (grectCurr.left >= gcxMax) grectCurr.left = gcxScroll;
  371.         grectCurr.top += gcyScroll;
  372.         if (grectCurr.top >= gcyMax) grectCurr.top = gcyScroll;
  373.         }
  374.     else
  375.         {
  376.         grectCurr.left = gcxMax >> 3;
  377.         grectCurr.top = gcyMax >> 3;
  378.         }
  379.  
  380.     grectCurr.right = min(gcxMax - grectCurr.left, 3 * (gcxMax >> 2));
  381.     grectCurr.bottom = min(gcyMax - grectCurr.top, 3 * (gcyMax >> 2));
  382.     
  383.     /* set up default title bar */
  384.     strcpy(winio_deftitle, __szModule);
  385. //    GetModuleFileName(__hInst, winio_deftitle, 128); // Gets unwieldy!
  386.     if (__lpCmdLine && *__lpCmdLine)
  387.         {
  388.         _fstrcat(winio_deftitle, " ");
  389.         _fstrcat(winio_deftitle, __lpCmdLine);
  390.         }
  391.  
  392.     atexit((void (*)()) winio_end);
  393.     
  394.     return TRUE;
  395.     }
  396.  
  397.  
  398. /* ---------------------------------------------------------------  */
  399. /* Create a new window. This will be called once from ARGCARGV.C,    */
  400. /* after a call to winio_init(), to create the main application        */
  401. /* window, and thereafter by the app for 2nd and more windows.        */
  402. /* ---------------------------------------------------------------  */
  403. HWND winio_window(LPSTR strTitle, WORD wBufSize, WORD wFlags)
  404.     {
  405.     HWND hWnd;
  406.     CREATEPARAMS cp;
  407.     
  408.     new_rect(); /* Get a new starting point for the window */
  409.     
  410.     if ((cp.lpData = (LPVOID) new_wnddata()) == NULL)
  411.         return NULL;
  412.     
  413.     if ((cp.wmTab = new_wmtab()) == NULL)
  414.         return NULL;
  415.     
  416.     if (! initialize_buffers((WINIO_HWND) cp.lpData, wBufSize, wFlags))
  417.         return NULL;
  418.  
  419.     if ((hWnd = new_window((LPCREATEPARAMS) &cp,
  420.             strTitle ? strTitle : winio_deftitle,
  421.             wFlags)) == NULL)
  422.         return NULL;
  423.     
  424.     cWindows++;
  425.  
  426.     wmhandler_yield();
  427.     
  428.     return hWnd;
  429.     }
  430.  
  431.  
  432. /* ---------------------------------------------------------------  */
  433. /* Calculate a nice new starting position for a new window!            */
  434. /* ---------------------------------------------------------------  */
  435. void new_rect()
  436.     {
  437.     if (hMaster)
  438.         {
  439.         GetWindowRect(hMaster, &grectCurr);
  440.     
  441.     /* new windows spiral out... ! */
  442.         grectCurr.left =
  443.             min(gcxMax,
  444.                 max(0, grectCurr.left +
  445.                   (gcyCap *
  446.                     ((1 + (cWindows >> 2)) * (cWindows & 0x0001 ? 2 : -2)))));
  447.         grectCurr.top =
  448.             min(gcyMax,
  449.                 max(0, grectCurr.top +
  450.                   (gcyCap *
  451.                     ((1 + (cWindows >> 2)) * (cWindows & 0x0002 ? 2 : -2)))));
  452.         }
  453.     // right & bottom fields are actually width & height
  454.     grectCurr.right =
  455.         cxDefSize
  456.             ? (cxDefSize * gcxDefFont) + gcxScroll
  457.             : min(gcxMax, grectCurr.left + gcxWidth) - grectCurr.left;
  458.     grectCurr.bottom =
  459.         cyDefSize
  460.             ? ((cyDefSize + 1) * gcyDefFont) + gcyScroll + gcyCap + gcyMenu
  461.             : min(gcyMax, grectCurr.top + gcyHeight) - grectCurr.top;
  462.     
  463.     gcxWidth = gcxMax >> 1;
  464.     gcyHeight = gcyMax >> 1;
  465.     }
  466.  
  467.  
  468.  
  469. /* ---------------------------------------------------------------  */
  470. /* Clear the contents of the buffer.                                */
  471. /* ---------------------------------------------------------------  */
  472. WINIO_HWND new_wnddata(void)
  473.     {
  474.     WINIO_HWND whWnd;
  475.     
  476.     if ((whWnd = (WINIO_HWND) GlobalLock(
  477.                 GlobalAlloc(GMEM_MOVEABLE, sizeof(WINIO_WNDDATA)))) == NULL)
  478.         return NULL;
  479.     
  480.     whWnd->cLinesDiscarded = 0;
  481.     whWnd->curr_font = SYSTEM_FIXED_FONT;
  482.     whWnd->cxChar = 10; // To avoid div by zero at 1st WM_SIZE
  483.     whWnd->cyChar = 16; // To avoid div by zero at 1st WM_SIZE
  484.     whWnd->tCaret = FALSE;
  485.     whWnd->tEcho = TRUE;
  486.     whWnd->tCR = FALSE;
  487.     whWnd->tPaint = TRUE;
  488.     whWnd->destroy_func = NULL;
  489.     whWnd->paintentry_func = NULL;
  490.     whWnd->paintexit_func = NULL;
  491.     whWnd->pfnLine = NULL;
  492.     whWnd->hMainMenu = NULL;
  493.     whWnd->hFileMenu = NULL;
  494.     lstrcpy(whWnd->strSaveFile, __szModule);
  495.     lstrcat(whWnd->strSaveFile, ".LOG");
  496.     _fmemset(whWnd->menu_func, 0, sizeof(whWnd->menu_func));
  497.     _fmemcpy(whWnd->cScrollUD, gcScrollUD, sizeof(gcScrollUD));
  498.     _fmemcpy(whWnd->cScrollLR, gcScrollLR, sizeof(gcScrollLR));
  499.     
  500.     return whWnd;
  501.     }
  502.  
  503.  
  504. /* ---------------------------------------------------------------  */
  505. /* Clear the contents of the buffer.                                */
  506. /* ---------------------------------------------------------------  */
  507. void winio_clear(HWND hwnd)
  508.     {
  509.     WINIO_HWND whWnd;
  510.     
  511.     if (! IsWindow(hwnd))
  512.         return;
  513.     whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  514.  
  515.     _fmemset(whWnd->fpBuffer,0,whWnd->bufsize - 1);
  516.     whWnd->fpCurr = whWnd->fpCurrLine =
  517.         whWnd->fpTopOfWin = whWnd->fpBuffer;
  518.     *(whWnd->fpBuffer) = '\0';
  519.     whWnd->xCurrPos = 0;
  520.     whWnd->yCurrLine = 0;
  521.     whWnd->yTopOfWin = 0;
  522.     whWnd->xLeftOfWin = 0;
  523.     whWnd->bufused = 0;
  524.     whWnd->cLinesDiscarded = 0;
  525.     whWnd->pchKbIn = whWnd->pchKbOut = 0;
  526.  
  527.     if (whWnd->tPaint)
  528.         InvalidateRect(hwnd, NULL, TRUE);
  529.     SetScrollRange(hwnd, SB_VERT, 1, whWnd->yCurrLine + 1, FALSE);
  530.     SetScrollPos(hwnd, SB_VERT, whWnd->yTopOfWin + 1, TRUE);
  531.     }
  532.  
  533. /* ---------------------------------------------------------------  */
  534. /* Set the current window title                                        */
  535. /* ---------------------------------------------------------------  */
  536. void winio_settitle(HWND hWnd, char * strTitle)
  537.     {
  538.     SetWindowText(hWnd, strTitle);
  539.     }
  540.  
  541. /* ---------------------------------------------------------------  */
  542. /* Return the window's output buffer size                            */
  543. /* ---------------------------------------------------------------  */
  544. WORD winio_bufsize(HWND hwnd)
  545.     {
  546.     return IsWindow(hwnd) ? ((WINIO_HWND) GetWindowLong(hwnd, 4))->bufsize
  547.                             : 0;
  548.     }
  549.  
  550. /* ---------------------------------------------------------------  */
  551. /* Return the window handle of this app's window with output focus    */
  552. /* ---------------------------------------------------------------  */
  553. HWND winio_current(void)
  554.     {
  555.     return hCurrWnd;
  556.     }
  557.  
  558. /* ---------------------------------------------------------------  */
  559. /* Resizes the output buffer for the specified window. If the BOOL    */
  560. /* parameter is FALSE, the buffer cannot be shrunk to smaller than    */
  561. /* the amount currently in use. If the BOOL parameter is TRUE, the    */
  562. /* buffer will be cleared, and the new size can be anything over    */
  563. /* 4K. The return value is the new actual buffer size.                */
  564. /* ---------------------------------------------------------------  */
  565. WORD winio_setbufsize(HWND hwnd, WORD wSize, BOOL bClear)
  566.     {
  567.     WINIO_HWND whWnd;
  568.     WORD bufsize;
  569.         
  570.     if (! IsWindow(hwnd))
  571.         return 0;
  572.     whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  573.     
  574.     bufsize =
  575.         wSize ? max(max(wSize, (WORD) WINIO_MIN_BUFFER),
  576.             (WORD) (bClear ? 0 : whWnd->bufused))
  577.                 : (WORD) WINIO_DEFAULT_BUFFER;
  578.  
  579.     if (! GlobalReAlloc(whWnd->hBuffer, (DWORD) bufsize, GMEM_MOVEABLE))
  580.         return whWnd->bufsize;
  581.         
  582.     if (bClear)
  583.         winio_clear(hwnd);
  584.     
  585.     return whWnd->bufsize = bufsize;
  586.     }
  587.  
  588. /* ---------------------------------------------------------------    */
  589. /* Sets the default window width and height in chars for future        */
  590. /* windows created using winio_window(). Height is in high WORD,    */
  591. /* width is in low. A 0 in either position indicates 'don't care'.    */
  592. /* ---------------------------------------------------------------    */
  593. DWORD winio_defwindowsize(DWORD dwSize)
  594.     {
  595.     DWORD old = (cyDefSize << 16) | cxDefSize;
  596.     
  597.     cyDefSize = (int) ((DWORD) dwSize >> 16);
  598.     cxDefSize = (int) ((DWORD) dwSize & 0x0000ffff);
  599.     return old;
  600.     }
  601.  
  602.  
  603. /* ---------------------------------------------------------------  */
  604. /* This function allocates a new wmhandler table, and initializes    */
  605. /* it...                                                            */
  606. /* ---------------------------------------------------------------  */
  607. WMTAB new_wmtab()
  608.     {
  609.     WMTAB wmTab;
  610.  
  611.     if ((wmTab = wmhandler_create()) == NULL)
  612.         return NULL;
  613.     
  614.     /* set up our message handlers */
  615.     wmhandler_settab(wmTab, WM_PAINT,               winio_wmpaint);
  616.     wmhandler_settab(wmTab, WM_SIZE,                winio_wmsize);
  617.     wmhandler_settab(wmTab, WM_COMMAND,                 winio_wmcommand);
  618.     wmhandler_settab(wmTab, WM_DESTROY,             winio_wmdestroy);
  619.     wmhandler_settab(wmTab, WM_CHAR,                winio_wmchar);
  620.     wmhandler_settab(wmTab, WM_HSCROLL,             winio_wmhscroll);
  621.     wmhandler_settab(wmTab, WM_VSCROLL,             winio_wmvscroll);
  622.     wmhandler_settab(wmTab, WM_SETFOCUS,            winio_wmsetfocus);
  623.     wmhandler_settab(wmTab, WM_KILLFOCUS,           winio_wmkillfocus);
  624.     wmhandler_settab(wmTab, WM_KEYDOWN,             winio_wmkeydown);
  625.     wmhandler_settab(wmTab, WM_LBUTTONDBLCLK,        winio_wmldblclk);
  626.  
  627.     return wmTab;
  628.     }
  629.  
  630. /* ---------------------------------------------------------------  */
  631. /* This function is called by winio_window(). It creates the new    */
  632. /* window, and attaches the wmhandler table pointer and the            */
  633. /* pointed to structure.                                            */
  634. /* ---------------------------------------------------------------  */
  635. HWND new_window(LPCREATEPARAMS cp, LPSTR strTitle, WORD wFlags)
  636.     {
  637.     HWND hwnd;
  638.     WINIO_HWND whWnd = (WINIO_HWND) (cp->lpData);
  639.  
  640.     if (wFlags & WW_HASMENU)
  641.         if (((whWnd->hFileMenu = CreateMenu()) == NULL) ||
  642.             ((whWnd->hMainMenu = CreateMenu()) == NULL) ||
  643.             (!hMaster && ((whWnd->hHelpMenu = CreateMenu()) == NULL)) ||
  644.             (! AppendMenu(whWnd->hFileMenu, MF_STRING | MF_ENABLED,
  645.                 ID_SAVE, "&Save Buffer...")) ||
  646.             (! AppendMenu(whWnd->hFileMenu,
  647.                 MF_SEPARATOR, NULL, NULL)) ||
  648.             (! AppendMenu(whWnd->hFileMenu,
  649.                 MF_STRING | MF_ENABLED,
  650.                 ID_EXIT, "E&xit")) ||
  651.             (!hMaster && (! AppendMenu(whWnd->hHelpMenu,
  652.                 MF_STRING | MF_ENABLED,
  653.                 ID_ABOUT, "&About..."))) ||
  654.             (! AppendMenu(whWnd->hMainMenu,
  655.                 MF_STRING | MF_ENABLED | MF_POPUP,
  656.                 whWnd->hFileMenu, "&File")) ||
  657.             (!hMaster && (! AppendMenu(whWnd->hMainMenu,
  658.                 MF_STRING | MF_ENABLED | MF_POPUP,
  659.                 whWnd->hHelpMenu, "&Help"))))
  660.             return FALSE;
  661.  
  662.     hwnd = CreateWindow((LPSTR) winio_wclass, (LPSTR) strTitle,
  663.         winio_wstyle,
  664.         grectCurr.left, grectCurr.top, grectCurr.right, grectCurr.bottom,
  665.         (wFlags & WW_STAYSONTOP) ? hMaster : NULL,
  666.         whWnd->hMainMenu, __hInst, (LPSTR) cp);
  667.     
  668.     if (! hwnd)
  669.         return NULL;
  670.  
  671.     if (!hMaster) winio_setmenufunc(hwnd, ID_ABOUT, winio_doabout);
  672.     winio_setmenufunc(hwnd, ID_SAVE, winio_dosave);
  673.     winio_setmenufunc(hwnd, ID_EXIT, winio_doexit);
  674.     
  675.     set_font(hwnd, whWnd);
  676.     
  677.     if (! gcxDefFont)
  678.         {
  679.         gcxDefFont = whWnd->cxChar;
  680.         gcyDefFont = whWnd->cyChar;
  681.         }
  682.         
  683.     winio_clear(hwnd);
  684.     
  685.     ShowWindow(hwnd, (hMaster) ? SW_SHOWNORMAL : __nCmdShow);
  686.     UpdateWindow(hwnd);
  687.  
  688.     if (! hMaster)
  689.         {
  690.         winio_wstyle |= WS_POPUP;
  691.         hMaster = hwnd;
  692.         }
  693.     else
  694.     if (! (wFlags & WW_EXITALLOWED))
  695.         EnableMenuItem(whWnd->hFileMenu, ID_EXIT, MF_DISABLED | MF_GRAYED);
  696.     
  697.     return hwnd;
  698.     }
  699.  
  700.  
  701.  
  702. /* -----------------------------------------------------------------------  */
  703. /* Allows an app to get info about the current size and position of the        */
  704. /* window's view on the buffer.                                                */
  705. /* -----------------------------------------------------------------------  */
  706. void winio_getinfo(HWND hwnd, PWINIOINFO pwi)
  707.     {
  708.     if (! IsWindow(hwnd))
  709.         return;
  710.     
  711.     get_info((WINIO_HWND) GetWindowLong(hwnd, 4), pwi);
  712.     }
  713.  
  714.  
  715.  
  716. /* -----------------------------------------------------------------------  */
  717. /* Initializes Window Class                                                 */
  718. /* -----------------------------------------------------------------------  */
  719. int initialize_class(HANDLE hInst)
  720.     {
  721.     WNDCLASS  wc;
  722.  
  723.     wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS
  724.                 /* | CS_BYTEALIGNCLIENT */;
  725.     wc.lpfnWndProc = wmhandler_wndproc;
  726.     wc.cbClsExtra = 0;
  727.     wc.cbWndExtra = 8;
  728.     wc.hInstance = hInst;
  729.     wc.hIcon = LoadIcon(hInst, winio_icon);
  730.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  731.     wc.hbrBackground = GetStockObject(WHITE_BRUSH); 
  732.     wc.lpszMenuName = NULL;
  733.     wc.lpszClassName = winio_wclass;
  734.  
  735.     return RegisterClass(&wc);
  736.     }
  737.     
  738. /* -----------------------------------------------------------------------  */
  739. /* Uses GlobalAlloc() to allocate the display and keyboard buffers          */
  740. /* -----------------------------------------------------------------------  */
  741. BOOL initialize_buffers(WINIO_HWND whWnd, WORD wBufSize, WORD wFlags)
  742.     {
  743.     whWnd->bufsize = wBufSize ? max(wBufSize, WINIO_MIN_BUFFER)
  744.                                 : WINIO_DEFAULT_BUFFER;
  745.  
  746.     if (! (whWnd->hBuffer =
  747.             GlobalAlloc(GMEM_MOVEABLE, (DWORD) whWnd->bufsize)))
  748.         return FALSE;
  749.     
  750.     // keep locked; assume protected mode
  751.     whWnd->fpBuffer = GlobalLock(whWnd->hBuffer);
  752.     
  753.     if (! (wFlags & WW_OUTPUTONLY))
  754.         {
  755.         if (! (whWnd->hKeyboard = GlobalAlloc(GMEM_MOVEABLE, TYPE_AHEAD)))
  756.             {
  757.             GlobalFree(whWnd->hBuffer);
  758.             return FALSE;
  759.             }
  760.  
  761.         // keep locked; assume protected mode
  762.         whWnd->fpKeyboard = GlobalLock(whWnd->hKeyboard);
  763.         }
  764.     
  765.     *(whWnd->fpBuffer) = '\0';
  766.     whWnd->fpBuffer++;
  767.  
  768.     return TRUE;
  769.     }
  770.  
  771. /* -----------------------------------------------------------------------  */
  772. /* Allows an application to vegetate until the user closes all the windows  */
  773. /* -----------------------------------------------------------------------  */
  774. void winio_end()
  775.     {
  776.     while (cWindows)
  777.         wmhandler_yield();
  778.     }
  779.  
  780. /* -------------------------------------------------------------------  */
  781. /* Closes the window by sending it a WM_DESTROY message. Note that it   */
  782. /* does not disable the _onclose defined function. So the user program  */
  783. /* handler will be triggered.                                            */
  784. /* -------------------------------------------------------------------  */
  785. void winio_close(HWND hwnd)
  786.     {
  787.     DestroyWindow(hwnd);
  788.     }
  789.  
  790.  
  791. /* -------------------------------------------------------------------  */
  792. /* Shows the hourglass cursor and puts the window into a modal            */
  793. /* condition. Second and subsequent calls just increment a counter.        */
  794. /* -------------------------------------------------------------------  */
  795. void winio_setbusy(void)
  796.     {
  797.     if (! nBusyCount++)
  798.         {
  799.         hCrsSave = SetClassWord(hCurrWnd, GCW_HCURSOR, NULL);
  800.         SetCursor(LoadCursor(NULL, IDC_WAIT));
  801.         SetCapture(hCurrWnd);
  802.         }
  803.     }
  804.  
  805.  
  806. /* -------------------------------------------------------------------  */
  807. /* Cancels the above. More accurately, cancels the above if the counter */
  808. /* that it decrements (incremented in winio_setbusy) reaches zero.        */
  809. /* -------------------------------------------------------------------  */
  810. void winio_resetbusy(void)
  811.     {
  812.     if (! --nBusyCount)
  813.         {
  814.         ReleaseCapture();
  815.         SetCursor(hCrsSave);
  816.         SetClassWord(hCurrWnd, GCW_HCURSOR, hCrsSave);
  817.         }
  818.     }
  819.  
  820.  
  821. /* -------------------------------------------------------------------  */
  822. /* Closes all windows by sending the main window a WM_DESTROY message.  */
  823. /* Note that it does not disable the _onclose defined function. So the  */
  824. /* user program handler will be triggered.                                */
  825. /* -------------------------------------------------------------------  */
  826. void winio_closeall()
  827.     {
  828.     if (hMaster)
  829.         DestroyWindow(hMaster);
  830.     }
  831.  
  832.  
  833. /* -------------------------------------------------------------------  */
  834. /* Let the application install an exit routine, called back from        */
  835. /* winio_wmdestroy(). Deinstall by winio_onclose(NULL)                  */
  836. /* -------------------------------------------------------------------  */
  837. void winio_onclose(HWND hwnd, DESTROY_FUNC exitfunc)
  838.     {
  839.     if (IsWindow(hwnd))
  840.         ((WINIO_HWND) GetWindowLong(hwnd, 4))->destroy_func = exitfunc;
  841.     }
  842.  
  843. /* -------------------------------------------------------------------  */
  844. /* Let the application install a paint-time entry routine, called back    */
  845. /* from winio_wmpaint(). Deinstall by winio_onpaintentry(NULL).            */
  846. /* -------------------------------------------------------------------  */
  847. PAINT_FUNC winio_onpaintentry(HWND hwnd, PAINT_FUNC paintfunc)
  848.     {
  849.     WINIO_HWND whWnd;
  850.     PAINT_FUNC old;
  851.     
  852.     if (! IsWindow(hwnd))
  853.         return NULL;
  854.     whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  855.     old = whWnd->paintentry_func;
  856.     whWnd->paintentry_func = paintfunc;
  857.     return old;
  858.     }
  859.  
  860. /* -------------------------------------------------------------------  */
  861. /* Let the application install a paint-time exit routine, called back    */
  862. /* from winio_wmpaint(). Deinstall by winio_onpaintexit(NULL).            */
  863. /* -------------------------------------------------------------------  */
  864. PAINT_FUNC winio_onpaintexit(HWND hwnd, PAINT_FUNC paintfunc)
  865.     {
  866.     WINIO_HWND whWnd;
  867.     PAINT_FUNC old;
  868.     
  869.     if (! IsWindow(hwnd))
  870.         return NULL;
  871.     whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  872.     old = whWnd->paintexit_func;
  873.     whWnd->paintexit_func = paintfunc;
  874.     return old;
  875.     }
  876.  
  877. /* -------------------------------------------------------------------  */
  878. /* This function allows the font of the window to be modified.            */
  879. /* Currently, only SYSTEM_, ANSI_, and OEM_FIXED_FONTs are supported.    */
  880. /* -------------------------------------------------------------------  */
  881. WORD winio_setfont(HWND hwnd, WORD wFont)
  882.     {
  883.     WORD wOld;
  884.     WINIO_HWND whWnd;
  885.     
  886.     if (! IsWindow(hwnd))
  887.         return 0;
  888.     
  889.     if ((wFont != SYSTEM_FIXED_FONT) &&
  890.         (wFont != ANSI_FIXED_FONT) &&
  891.         (wFont != OEM_FIXED_FONT))
  892.         return 0;
  893.         
  894.     whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  895.     wOld = whWnd->curr_font;
  896.     whWnd->curr_font = wFont;
  897.     set_font(hwnd, whWnd);
  898.     if (whWnd->tPaint)
  899.         InvalidateRect(hwnd, NULL, TRUE);
  900.     return wOld;
  901.     }
  902.  
  903.  
  904. /* -------------------------------------------------------------------  */
  905. /* This function allows a 'line handler' to be installed. When the user    */
  906. /* double-clicks on a line, the specified function is called with the    */
  907. /* arguments (HWND, LPSTR, int). Calling this function with NULL        */
  908. /* disables line handling...                                            */
  909. /* -------------------------------------------------------------------  */
  910. LINEHANDLER winio_setlinefn(HWND hwnd, LINEHANDLER pfnLine)
  911.     {
  912.     WINIO_HWND whWnd;
  913.     LINEHANDLER old;
  914.     
  915.     if (! IsWindow(hwnd))
  916.         return NULL;
  917.     
  918.     whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  919.     old = whWnd->pfnLine;
  920.     
  921.     whWnd->pfnLine = pfnLine;
  922.     return old;
  923.     }
  924.  
  925.  
  926. /* -------------------------------------------------------------------  */
  927. /* This function allows the caller to specify immediate or deferred     */
  928. /* screen updates. The call may not be issued before winio_init().      */
  929. /* -------------------------------------------------------------------  */
  930. BOOL winio_setpaint(HWND hwnd, BOOL on)
  931.     {
  932.     WINIO_HWND whWnd;
  933.     BOOL ret;
  934.     
  935.     if (! IsWindow(hwnd))
  936.         return FALSE;
  937.     
  938.     whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  939.     ret = whWnd->tPaint;
  940.     
  941.     if (whWnd->tPaint = on)
  942.         {
  943.         whwndCurr->cScrollUD[SB_TOP]       = -(whwndCurr->yCurrLine);
  944.         whwndCurr->cScrollUD[SB_BOTTOM]    = whwndCurr->yCurrLine;
  945.         InvalidateRect(hwnd, NULL, TRUE);
  946.         SetScrollRange(hwnd, SB_VERT, 1, whWnd->yCurrLine + 1, FALSE);
  947.         SetScrollPos(hwnd, SB_VERT, whWnd->yTopOfWin + 1, TRUE);
  948. //        wmhandler_yield();
  949.         }
  950.     
  951.     return ret;
  952.     }
  953.  
  954. /* -------------------------------------------------------------------  */
  955. /* This function allows the caller to scroll the window to top left of    */
  956. /* the buffer, as if the user had pressed the HOME key, which is        */
  957. /* effectively what this function does.                                    */
  958. /* -------------------------------------------------------------------  */
  959. void winio_home(HWND hwnd)
  960.     {
  961.     SendMessage(hwnd, WM_KEYDOWN, VK_HOME, 0L);
  962.     SendMessage(hwnd, WM_KEYDOWN, VK_HOME, 0L);
  963.     }
  964.  
  965. /* -------------------------------------------------------------------  */
  966. /* This function changes the behavior of getchar(), whose default       */
  967. /* is to echo characters, unlike in DOS. winio_setecho(FALSE) restores  */
  968. /* the non-echo DOS behavior.                                           */
  969. /* -------------------------------------------------------------------  */
  970. BOOL winio_setecho(HWND hwnd, BOOL flag)
  971.     {
  972.     WINIO_HWND whWnd;
  973.     BOOL ret;
  974.     
  975.     if (! IsWindow(hwnd))
  976.         return FALSE;
  977.     
  978.     whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  979.     ret = whWnd->tEcho;
  980.     whWnd->tEcho = flag;
  981.     return ret;
  982.     }
  983.  
  984. /* -------------------------------------------------------------------  */
  985. /* This function sets the window that is to be the target of the        */
  986. /* STDIO functions.                                                     */
  987. /* -------------------------------------------------------------------  */
  988. HWND winio_setcurrent(HWND hwnd)
  989.     {
  990.     HWND h = hCurrWnd;
  991.     
  992.     if (! IsWindow(hwnd))
  993.         return NULL;
  994.     
  995.     hCurrWnd = hwnd;
  996.     if (hwnd)
  997.         whwndCurr = (WINIO_HWND) GetWindowLong(hwnd, 4);
  998.     else
  999.         whwndCurr = NULL;
  1000.     
  1001.     return h;
  1002.     }
  1003.  
  1004. /* -------------------------------------------------------------------  */
  1005. /* This function sets the Help About... text.                            */
  1006. /* -------------------------------------------------------------------  */
  1007. void winio_about(char *str)
  1008.     {
  1009.     strncpy(strAbout, str, MAX_ABOUT);
  1010.     strAbout[MAX_ABOUT] = 0;
  1011.     }
  1012.  
  1013. /* ---------------------------------------------------------------  */
  1014. /* Our WM_PAINT handler. It sends the currrent 'in view' piece of   */
  1015. /* the buffer to the window. Note that an embedded NULL character   */
  1016. /* signifies an end of line, not '\n'.                              */
  1017. /* ---------------------------------------------------------------  */
  1018. long winio_wmpaint(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  1019.     {
  1020.     HDC hdc;
  1021.     PAINTSTRUCT ps;
  1022.     WINIOINFO wi;
  1023.     BYTE far *pchEOL;
  1024.     int i, j, xStart;
  1025.     int xLeft, xRight, yTop, yBottom;
  1026.     WINIO_HWND whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1027.     BYTE far *pchSOL = whWnd->fpTopOfWin;
  1028.  
  1029.     hdc = BeginPaint(hwnd, &ps);
  1030.  
  1031.     get_info(whWnd, (PWINIOINFO) &wi);
  1032.     
  1033.     if ((whWnd->paintentry_func) &&
  1034.         (! (*whWnd->paintentry_func)
  1035.             (hwnd, hdc, (PAINTSTRUCT *) &ps, (PWINIOINFO) &wi)))
  1036.         goto labAfter;
  1037.  
  1038.     xLeft = (ps.rcPaint.left / whWnd->cxChar) + whWnd->xLeftOfWin;
  1039.     xRight = (ps.rcPaint.right / whWnd->cxChar) + whWnd->xLeftOfWin;
  1040.     yTop = ps.rcPaint.top / whWnd->cyChar;
  1041.     yBottom = ps.rcPaint.bottom / whWnd->cyChar;
  1042.  
  1043.     SelectObject(hdc, GetStockObject(whWnd->curr_font));
  1044.     
  1045.     for (i = 0; i < yTop; i++)      // lines above repaint region
  1046.         {
  1047.         while ((pchSOL < whWnd->fpCurrLine) && *pchSOL)
  1048.             pchSOL++;
  1049.         if (pchSOL < whWnd->fpCurrLine)
  1050.             pchSOL++;
  1051.         else
  1052.             break;
  1053.         }
  1054.  
  1055.     if ((i == yTop) && (i <= whWnd->yCurrLine)) // something needs repainting
  1056.         {
  1057.         for (; i <= yBottom; i++)   // lines in repaint region
  1058.             {
  1059.             for (j = 0; (j < xLeft) && (*pchSOL); j++, pchSOL++)
  1060.                 ; // Scroll right
  1061.             pchEOL = pchSOL;
  1062.             xStart = j - whWnd->xLeftOfWin;
  1063.             for (j = 0; (*pchEOL) ; j++, pchEOL++) ; // end of line
  1064.             TextOut(hdc, whWnd->cxChar * xStart, whWnd->cyChar * i,
  1065.                 pchSOL, min(j, xRight - xLeft + 1));
  1066.             if ((unsigned) (pchEOL - whWnd->fpBuffer) >= whWnd->bufused)
  1067.                 break;
  1068.             pchSOL = ++pchEOL;  
  1069.             }
  1070.         }
  1071.     
  1072. labAfter:    
  1073.     if (whWnd->paintexit_func)
  1074.         (*whWnd->paintexit_func)(hwnd, hdc,
  1075.             (PAINTSTRUCT *) &ps, (PWINIOINFO) &wi);
  1076.     
  1077.     EndPaint(hwnd, &ps);
  1078.     if (hwnd == GetFocus())
  1079.         adjust_caret(whWnd);
  1080.     return 0;
  1081.     }
  1082.  
  1083.  
  1084. /* ---------------------------------------------------------------  */
  1085. /* Our WM_COMMAND handler. This fires off the appropriate handler    */
  1086. /* for the menu option.                                                */
  1087. /* ---------------------------------------------------------------  */
  1088. long winio_wmcommand(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  1089.     {
  1090.     WINIO_HWND whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1091.  
  1092.     if (LOWORD(lParam))            // Menu options only (dailog controls later ?)
  1093.         return 0;
  1094.     
  1095.     if (wParam > MAX_MENU_ID)
  1096.         return 0;
  1097.     
  1098.     --wParam;
  1099.     
  1100.     if (whWnd->menu_func[wParam])
  1101.         (*whWnd->menu_func[wParam])(hwnd, wParam + 1);
  1102.     
  1103.     return 1;
  1104.     }
  1105.  
  1106. /* ---------------------------------------------------------------  */
  1107. /* Our WM_SIZE handler. It updates the internal record of our       */
  1108. /* window size, minus the scroll bars, and recalcs the scroll bar   */
  1109. /* ranges.                                                          */
  1110. /* ---------------------------------------------------------------  */
  1111. long winio_wmsize(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  1112.     {
  1113.     WINIO_HWND whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1114.     
  1115.     whWnd->cxWidth = LOWORD(lParam);
  1116.     whWnd->cyHeight = HIWORD(lParam);
  1117.  
  1118.     whWnd->xWinWidth   = (whWnd->cxWidth - gcxScroll ) / whWnd->cxChar;
  1119.     whWnd->yWinHeight  = (whWnd->cyHeight - gcyScroll ) / whWnd->cyChar;
  1120.  
  1121.     whWnd->cScrollLR[SB_PAGEUP]    = -(whWnd->xWinWidth) / 2;
  1122.     whWnd->cScrollLR[SB_PAGEDOWN]  = +(whWnd->xWinWidth) / 2;
  1123.     whWnd->cScrollUD[SB_PAGEUP]    = -(whWnd->yWinHeight) + 1;
  1124.     whWnd->cScrollUD[SB_PAGEDOWN]  = +(whWnd->yWinHeight) - 1;
  1125.     
  1126.     SetScrollRange(hwnd, SB_HORZ, 1, MAX_X, FALSE);
  1127.     SetScrollPos(hwnd, SB_HORZ, whWnd->xLeftOfWin + 1, TRUE);
  1128.  
  1129.     SetScrollRange(hwnd, SB_VERT, 1, whWnd->yCurrLine + 1, FALSE);
  1130.     SetScrollPos(hwnd, SB_VERT, whWnd->yTopOfWin + 1, TRUE);
  1131.     
  1132.     return 0;
  1133.     }
  1134.  
  1135. /* ---------------------------------------------------------------  */
  1136. /* Our WM_DESTROY handler. It frees up storage associated with the  */
  1137. /* window. If it is the last window for the app, it exit()s...        */
  1138. /* ---------------------------------------------------------------  */
  1139. long winio_wmdestroy(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  1140.     {
  1141.     WINIO_HWND whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1142.     
  1143.     if (whWnd->destroy_func)
  1144.         (*(whWnd->destroy_func))(hwnd);
  1145.  
  1146.     if (hwnd == hMaster)
  1147.         hMaster = NULL;
  1148.  
  1149.     if (hwnd == hCurrWnd)
  1150.         {
  1151.         hCurrWnd = NULL;
  1152.         whwndCurr = NULL;
  1153.         }
  1154.     
  1155.     wmhandler_destroy(hwnd);
  1156.     GlobalFree(whWnd->hBuffer);
  1157.     GlobalFree(whWnd->hKeyboard);
  1158.     GlobalFree(LOWORD(GlobalHandle(HIWORD(whWnd))));
  1159.  
  1160.     --cWindows;
  1161.  
  1162.     return 0;
  1163.     }
  1164.  
  1165. /* --------------------------------------------------------------- */
  1166. /* Our WM_BYTE handler. It adds the BYTE to the internal kb buffer */
  1167. /* if there is room otherwise it queues a BEEP                     */
  1168. /* --------------------------------------------------------------- */
  1169. long winio_wmchar(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  1170.     {
  1171.     WINIO_HWND whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1172.     BYTE far *lpchKeybd = whWnd->fpKeyboard;
  1173.     unsigned pchSave = whWnd->pchKbIn;
  1174.     
  1175.     whWnd->pchKbIn++;
  1176.     if (whWnd->pchKbIn == TYPE_AHEAD)
  1177.         whWnd->pchKbIn = 0;
  1178.     if (whWnd->pchKbIn == whWnd->pchKbOut)
  1179.         {
  1180.         MessageBeep(0);
  1181.         whWnd->pchKbIn = pchSave;
  1182.         }
  1183.     else
  1184.         *(lpchKeybd + pchSave) = LOBYTE(wParam);
  1185.  
  1186.     return 0;
  1187.     }
  1188.  
  1189. /* ---------------------------------------------------------------  */
  1190. /* Our WM_LBUTTONDBLCLK handler. This handles the user double-        */
  1191. /* clicking on a line. If the user has specified a 'line' handler    */
  1192. /* we get the pointer to the beginning of the line, and pass that    */
  1193. /* and the line number to the function specified...                    */
  1194. /* ---------------------------------------------------------------  */
  1195. long winio_wmldblclk(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  1196.     {
  1197.     int cLine;
  1198.     int    yRel;
  1199.     LPSTR lpLine;
  1200.     static char strTmp[128];
  1201.     WINIO_HWND whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1202.     
  1203.     if (! whWnd->pfnLine)
  1204.         return 0;
  1205.     
  1206.     yRel = HIWORD(lParam) / whWnd->cyChar;
  1207.     cLine = whWnd->yTopOfWin + yRel;
  1208.     if (cLine > whWnd->yCurrLine)
  1209.         return 0;
  1210.     lpLine = whWnd->fpTopOfWin;
  1211.     for (; yRel > 0; yRel--)
  1212.         lpLine = nextline(lpLine);
  1213.     lstrcpy(strTmp, lpLine);
  1214.     (* (whWnd->pfnLine))(hwnd, strTmp, ++cLine);
  1215.     return 0;
  1216.     }
  1217.  
  1218. /* ---------------------------------------------------------------  */
  1219. /* Our WM_KEYDOWN handler. This handles what would be called        */
  1220. /* function keys in the DOS world. In this case the function keys   */
  1221. /* operate as scroll bar controls, so we generate messages to the   */
  1222. /* scroll message handlers below.                                   */
  1223. /* ---------------------------------------------------------------  */
  1224. long winio_wmkeydown(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  1225.     {
  1226.     int hSB, vSB;
  1227.     
  1228.     if ((wParam < VK_PRIOR) || (wParam > VK_DOWN))
  1229.         return 0;
  1230.     
  1231.     hSB = VKtoSB[wParam - VK_PRIOR].hSB;
  1232.     vSB = VKtoSB[wParam - VK_PRIOR].vSB;
  1233.     if (hSB != DO_NOTHING)
  1234.         SendMessage(hwnd, WM_HSCROLL, hSB, 0L);
  1235.     if (vSB != DO_NOTHING)
  1236.         SendMessage(hwnd, WM_VSCROLL, vSB, 0L);
  1237.     return 0;
  1238.     }
  1239.  
  1240. /* --------------------------------------------------------------- */
  1241. /* Our WM_HSCROLL handler. It adjusts what part of the buffer      */
  1242. /* is visible. It operates as left/right arrow keys.               */
  1243. /* --------------------------------------------------------------- */
  1244. long winio_wmhscroll(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  1245.     {
  1246.     WINIO_HWND whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1247.     int cxSave = whWnd->xLeftOfWin,
  1248.         xInc = whWnd->cScrollLR[wParam];
  1249.     
  1250.     if (! whWnd->tPaint) return 0;
  1251.     
  1252.     if (xInc == DO_NOTHING)
  1253.         return 0;
  1254.     else if (xInc == USE_PARAM)
  1255.         whWnd->xLeftOfWin = LOWORD(lParam) - 1;
  1256.     else
  1257.         whWnd->xLeftOfWin += xInc;
  1258.     
  1259.     if ((whWnd->xLeftOfWin = max(0, min(MAX_X - 1, whWnd->xLeftOfWin)))
  1260.                     == cxSave)
  1261.         return 0;
  1262.  
  1263.     ScrollWindow(hwnd, (cxSave - whWnd->xLeftOfWin) * whWnd->cxChar,
  1264.                         0, NULL, NULL);
  1265.     SetScrollPos(hwnd, SB_HORZ, whWnd->xLeftOfWin + 1, TRUE);
  1266.     UpdateWindow(hwnd);
  1267.  
  1268.     return 0;
  1269.     }
  1270.  
  1271. /* --------------------------------------------------------------- */
  1272. /* Our WM_VSCROLL handler. It adjusts what part of the buffer      */
  1273. /* is visible. It operates as page and line up/down keys.          */
  1274. /* --------------------------------------------------------------- */
  1275. long winio_wmvscroll(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  1276.     {
  1277.     WINIO_HWND whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1278.     int cySave = whWnd->yTopOfWin,
  1279.         yInc = whWnd->cScrollUD[wParam],
  1280.         i;
  1281.     
  1282.     if (! whWnd->tPaint) return 0;
  1283.     
  1284.     if (yInc == DO_NOTHING)
  1285.         return 0;
  1286.     else if (yInc == USE_PARAM)
  1287.         whWnd->yTopOfWin = LOWORD(lParam) - 1;
  1288.     else
  1289.         whWnd->yTopOfWin += yInc;
  1290.  
  1291.     if ((whWnd->yTopOfWin = max(0, min(whWnd->yCurrLine, whWnd->yTopOfWin)))
  1292.                                     == cySave)
  1293.         return 0;
  1294.  
  1295.     if (whWnd->yTopOfWin > cySave)
  1296.         for (i = cySave; i < whWnd->yTopOfWin; i++)
  1297.             whWnd->fpTopOfWin = nextline(whWnd->fpTopOfWin);
  1298.     else
  1299.         for (i = cySave; i > whWnd->yTopOfWin; i--)
  1300.             whWnd->fpTopOfWin = prevline(whWnd->fpTopOfWin);
  1301.  
  1302.     ScrollWindow(hwnd, 0,
  1303.                 (cySave - whWnd->yTopOfWin) * whWnd->cyChar, NULL, NULL);
  1304.     SetScrollPos(hwnd, SB_VERT, whWnd->yTopOfWin + 1, TRUE);
  1305.     UpdateWindow(hwnd);
  1306.  
  1307.     return 0;
  1308.     }
  1309.  
  1310. /* ---------------------------------------------------------------  */
  1311. /* Our WM_SETFOCUS handler. It sets up the text caret.              */
  1312. /* ---------------------------------------------------------------  */
  1313. long winio_wmsetfocus(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  1314.     {
  1315.     WINIO_HWND whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1316.     
  1317.     CreateCaret(hwnd, NULL, CARET_WIDTH, whWnd->cyChar);
  1318.     
  1319.     if ((whWnd->tCaret = winio_caret_visible(whWnd)))
  1320.         {
  1321.         SetCaretPos((whWnd->xCurrPos - whWnd->xLeftOfWin) * whWnd->cxChar,
  1322.                     (whWnd->yCurrLine - whWnd->yTopOfWin) * whWnd->cyChar);
  1323.         ShowCaret(hwnd);
  1324.         }
  1325.  
  1326.     return 0;
  1327.     }
  1328.  
  1329. /* ---------------------------------------------------------------  */
  1330. /* Our WM_KILLFOCUS handler. It destroys the text caret.            */
  1331. /* ---------------------------------------------------------------  */
  1332. long winio_wmkillfocus(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  1333.     {
  1334.     WINIO_HWND whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1335.     
  1336.     if (whWnd->tCaret)
  1337.         {
  1338.         HideCaret(hwnd);
  1339.         whWnd->tCaret = FALSE;
  1340.         }
  1341.     DestroyCaret();
  1342.     return 0;
  1343.     }
  1344.  
  1345. /* ---------------------------------------------------------------  */
  1346. /* Our WM_COMMAND ID_EXIT handler. It will only be invoked from        */
  1347. /* the main window. It simply destroys the main window and thereby    */
  1348. /* automatically all the child windows.                                */
  1349. /* ---------------------------------------------------------------  */
  1350. void winio_doexit(HWND hwnd, int nID)
  1351.     {
  1352.     DestroyWindow(hMaster);
  1353.     }
  1354.  
  1355. /* ---------------------------------------------------------------  */
  1356. /* Our WM_COMMAND ID_ABOUT handler. It will only be invoked from    */
  1357. /* the main window. It simply destroys the main window and thereby    */
  1358. /* automatically all the child windows.                                */
  1359. /* ---------------------------------------------------------------  */
  1360. void winio_doabout(HWND hwnd, int nID)
  1361.     {
  1362.     winio_warn(FALSE, __szModule, "%s%s", strAbout, WINIO_ABOUT_TEXT);
  1363.     }
  1364.  
  1365. /* ---------------------------------------------------------------  */
  1366. /* Tells the app how many windows are still open                    */
  1367. /* ---------------------------------------------------------------  */
  1368. int winio_openwindows(void)
  1369.     {
  1370.     return cWindows;
  1371.     }
  1372.  
  1373.  
  1374. /* ---------------------------------------------------------------  */
  1375. /* Sets up a handler for a menu option. An application that adds    */
  1376. /* an option to the menu should specify the handler here, otherwise    */
  1377. /* the option will never achieve anything...! User app IDs must    be    */
  1378. /* in the range 1 - 32.                                                */
  1379. /* ---------------------------------------------------------------  */
  1380. MENU_FUNC winio_setmenufunc(HWND hwnd, int id, MENU_FUNC menu_func)
  1381.     {
  1382.     MENU_FUNC old_menu_func;
  1383.     WINIO_HWND whWnd;
  1384.     
  1385.     if (! IsWindow(hwnd))
  1386.         return NULL;
  1387.     
  1388.     whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1389.  
  1390.     if ((id < 1) || (id > MAX_MENU_ID))
  1391.         return (MENU_FUNC) -1;
  1392.     
  1393.     id--;
  1394.     old_menu_func = whWnd->menu_func[id];
  1395.     whWnd->menu_func[id] = menu_func;
  1396.     return old_menu_func;
  1397.     }
  1398.  
  1399.  
  1400. /* ---------------------------------------------------------------  */
  1401. /* Returns the handle to the window's main menu, allowing an app    */
  1402. /* to add options to it. It is NULL if the window was created        */
  1403. /* without the WW_HASMENU flag.                                        */
  1404. /* ---------------------------------------------------------------  */
  1405. HMENU winio_hmenumain(HWND hwnd)
  1406.     {
  1407.     WINIO_HWND whWnd;
  1408.     
  1409.     if (! IsWindow(hwnd))
  1410.         return NULL;
  1411.     whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1412.     return whWnd->hMainMenu;
  1413.     }
  1414.  
  1415.  
  1416. /* ---------------------------------------------------------------  */
  1417. /* Returns the handle to the window's file popup menu, allowing an    */
  1418. /* app to add options to it. It is NULL if the window was created    */
  1419. /* without the WW_HASMENU flag.                                        */
  1420. /* ---------------------------------------------------------------  */
  1421. HMENU winio_hmenufile(HWND hwnd)
  1422.     {
  1423.     WINIO_HWND whWnd;
  1424.     
  1425.     if (! IsWindow(hwnd))
  1426.         return NULL;
  1427.     whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1428.     return whWnd->hFileMenu;
  1429.     }
  1430.  
  1431.  
  1432. /* ---------------------------------------------------------------  */
  1433. /* Returns the handle to the window's help popup menu, allowing an    */
  1434. /* app to insert options into it. It is NULL if the window was        */
  1435. /* created without the WW_HASMENU flag.                                */
  1436. /* ---------------------------------------------------------------  */
  1437. HMENU winio_hmenuhelp(HWND hwnd)
  1438.     {
  1439.     WINIO_HWND whWnd;
  1440.     
  1441.     if (! IsWindow(hwnd))
  1442.         return NULL;
  1443.     whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1444.     return whWnd->hHelpMenu;
  1445.     }
  1446.  
  1447.  
  1448. /* ---------------------------------------------------------------  */
  1449. /* Our WM_COMMAND ID_SAVE handler. It uses COMMDLG.DLL to get a        */
  1450. /* file name, and saves the contents of the hwnd's buffer into the    */
  1451. /* file.                                                            */
  1452. /* ---------------------------------------------------------------  */
  1453. void winio_dosave(HWND hwnd, int nID)
  1454.     {
  1455.     WINIO_HWND whWnd = (WINIO_HWND) GetWindowLong(hwnd, 4);
  1456.     OPENFILENAME ofn;
  1457.     LPSTR lpBuf, fpData;
  1458.     int lenLine, wr, h;
  1459.     
  1460.     ofn.lStructSize = sizeof(OPENFILENAME);
  1461.     ofn.hwndOwner = hwnd;
  1462.     ofn.hInstance = __hInst;
  1463.     ofn.lpstrFilter = NULL;
  1464.     ofn.lpstrCustomFilter = NULL;
  1465.     ofn.nMaxCustFilter = 0;
  1466.     ofn.nFilterIndex = 0;
  1467.     ofn.lpstrFile = (LPSTR) whWnd->strSaveFile;
  1468.     ofn.nMaxFile = 80;
  1469.     ofn.lpstrFileTitle = (LPSTR) NULL;
  1470.     ofn.nMaxFileTitle = 0;
  1471.     ofn.lpstrInitialDir = (LPSTR) NULL;
  1472.     ofn.lpstrTitle = (LPSTR) NULL;
  1473.     ofn.Flags = OFN_OVERWRITEPROMPT | OFN_SHOWHELP;
  1474.     ofn.nFileOffset = 0;
  1475.     ofn.nFileExtension = 0;
  1476.     ofn.lpstrDefExt = (LPSTR) NULL;
  1477.     
  1478.     if (! GetSaveFileName(&ofn))
  1479.         return;
  1480.     
  1481.     if ((h = _lcreat(ofn.lpstrFile, NULL)) == -1) return;
  1482.  
  1483.     if ((lpBuf = GlobalLock(GlobalAlloc(GMEM_MOVEABLE, MAX_X + 2))) == NULL)
  1484.         {
  1485.         winio_warn(FALSE, "Save Buffer", "Cannot find 150 bytes available!");
  1486.         _lclose(h);
  1487.         return;
  1488.         }
  1489.  
  1490.     GetWindowText(hwnd, lpBuf, MAX_X - 2);
  1491.     lstrcat(lpBuf, "\r\n\r\n");
  1492.     _lwrite(h, lpBuf, lstrlen(lpBuf));
  1493.     
  1494.     for (    fpData = whWnd->fpBuffer;
  1495.             fpData <= whWnd->fpCurrLine;
  1496.             fpData += lenLine + 1)
  1497.         {
  1498.         lenLine = lstrlen(fpData);
  1499.         _fmemcpy(lpBuf, fpData, lenLine);
  1500.         lpBuf[lenLine] = '\r';
  1501.         lpBuf[lenLine+1] = '\n';
  1502.         if ((wr = _lwrite(h, lpBuf, lenLine + 2)) < (lenLine + 2))
  1503.             {
  1504.             winio_warn(FALSE, "Save Buffer",
  1505.                 "Problem writing %Fs\nInsufficient disk space ?",
  1506.                     ofn.lpstrFile);
  1507.             break;
  1508.             }
  1509.         };
  1510.     _lclose(h);    
  1511.     GlobalFree(LOWORD(GlobalHandle(HIWORD((DWORD) lpBuf))));
  1512.     }
  1513.  
  1514.  
  1515. /* ---------------------------------------------------------------  */
  1516. /* Updates the window structure to reflect the new font selection.    */
  1517. /* ---------------------------------------------------------------  */
  1518. void set_font(HWND hwnd, WINIO_HWND whWnd)
  1519.     {
  1520.     HDC hdc;
  1521.     TEXTMETRIC tm;
  1522.       
  1523.     hdc = GetDC(hwnd);
  1524.     SelectObject(hdc, GetStockObject(whWnd->curr_font));
  1525.     GetTextMetrics(hdc,&tm);
  1526.     ReleaseDC(hwnd,hdc);
  1527.     whWnd->cxChar = tm.tmAveCharWidth;
  1528.     whWnd->cyChar = tm.tmHeight+tm.tmExternalLeading;
  1529.     whWnd->xWinWidth   = (whWnd->cxWidth - gcxScroll ) / whWnd->cxChar;
  1530.     whWnd->yWinHeight  = (whWnd->cyHeight - gcyScroll ) / whWnd->cyChar;
  1531.     }
  1532.  
  1533.  
  1534. /* ---------------------------------------------------------------  */
  1535. /* Updates the window structure to reflect the new font selection.    */
  1536. /* ---------------------------------------------------------------  */
  1537. void get_info(WINIO_HWND whWnd, PWINIOINFO pwi)
  1538.     {
  1539.     pwi->dimChar.x = whWnd->cxChar;
  1540.     pwi->dimChar.y = whWnd->cyChar;
  1541.  
  1542.     pwi->posCurr.x = whWnd->xCurrPos;
  1543.     pwi->posCurr.y = whWnd->yCurrLine;
  1544.  
  1545.     pwi->rectView.left = pwi->rectView.right = whWnd->xLeftOfWin;
  1546.     pwi->rectView.top = pwi->rectView.bottom = whWnd->yTopOfWin;
  1547.     pwi->rectView.right += whWnd->xWinWidth;
  1548.     pwi->rectView.bottom += whWnd->yWinHeight;
  1549.  
  1550.     pwi->cDiscarded = whWnd->cLinesDiscarded;
  1551.     }
  1552.  
  1553.  
  1554. /* ---------------------------------------------------------------  */
  1555. /* Adjusts the position of the caret, and shows or hides it, as     */
  1556. /* appropriate.                                                     */
  1557. /* ---------------------------------------------------------------  */
  1558. void adjust_caret(WINIO_HWND whWnd)
  1559.     {
  1560.     int t = winio_caret_visible(whWnd);
  1561.  
  1562.     if (t)
  1563.         SetCaretPos((whWnd->xCurrPos - whWnd->xLeftOfWin) * whWnd->cxChar,
  1564.                     (whWnd->yCurrLine - whWnd->yTopOfWin) * whWnd->cyChar);
  1565.     if (t && (! whWnd->tCaret))
  1566.         ShowCaret(hCurrWnd);
  1567.     if ((! t) && whWnd->tCaret)
  1568.         HideCaret(hCurrWnd);
  1569.     whWnd->tCaret = t;
  1570.     }
  1571.  
  1572. /* ---------------------------------------------------------------  */
  1573. /* Computes, on the basis of what has just been updated, what area  */
  1574. /* of the window needs to be repainted.                             */
  1575. /* ---------------------------------------------------------------  */
  1576. void compute_repaint(void)
  1577.     {
  1578.     RECT rc;
  1579.     static int xCP = 0, yCL = 0;
  1580.     int tWholeWin = FALSE;
  1581.  
  1582.     if (whwndCurr->yCurrLine > (whwndCurr->yTopOfWin + whwndCurr->yWinHeight))
  1583.         {
  1584.         whwndCurr->yTopOfWin = 0;
  1585.         whwndCurr->fpTopOfWin = whwndCurr->fpBuffer;
  1586.         while (whwndCurr->yTopOfWin <
  1587.                 (whwndCurr->yCurrLine - ((whwndCurr->yWinHeight + 1) / 2)))
  1588.             {
  1589.             whwndCurr->fpTopOfWin = nextline(whwndCurr->fpTopOfWin);
  1590.             whwndCurr->yTopOfWin++;
  1591.             }
  1592.         tWholeWin = TRUE;
  1593.         }
  1594.  
  1595.     if ((whwndCurr->xCurrPos < whwndCurr->xLeftOfWin) ||
  1596.         (whwndCurr->xCurrPos >
  1597.             (whwndCurr->xLeftOfWin + whwndCurr->xWinWidth)))
  1598.         {
  1599.         whwndCurr->xLeftOfWin = 0;
  1600.         while (whwndCurr->xLeftOfWin <
  1601.                 (whwndCurr->xCurrPos - ((whwndCurr->xWinWidth + 1) / 2)))
  1602.             whwndCurr->xLeftOfWin++;
  1603.         tWholeWin = TRUE;
  1604.         }
  1605.  
  1606.     if (tWholeWin)
  1607.         {
  1608.         if (whwndCurr->tPaint)
  1609.             InvalidateRect(hCurrWnd, NULL, TRUE);
  1610.         }
  1611.     else
  1612.         {
  1613.         rc.left = ((yCL == whwndCurr->yCurrLine) && (!whwndCurr->tCR)) ?
  1614.             (min(xCP, whwndCurr->xCurrPos) -
  1615.                 whwndCurr->xLeftOfWin) * whwndCurr->cxChar : 0;
  1616.         rc.top = (yCL - whwndCurr->yTopOfWin) * whwndCurr->cyChar;
  1617.         rc.right = (whwndCurr->xWinWidth + 3) * whwndCurr->cxChar;
  1618.         rc.bottom = (whwndCurr->yCurrLine - whwndCurr->yTopOfWin + 1 +
  1619.             whwndCurr->tCR)
  1620.             * whwndCurr->cyChar;
  1621.         if (whwndCurr->tPaint)
  1622.             InvalidateRect(hCurrWnd, &rc, TRUE);
  1623.         }
  1624.  
  1625. //    whwndCurr->tCR = FALSE;
  1626.     yCL = whwndCurr->yCurrLine;
  1627.     xCP = whwndCurr->xCurrPos;
  1628.     }
  1629.  
  1630. /* ---------------------------------------------------------------  */
  1631. /* Adds the supplied cch-long string to the display buffer, and     */
  1632. /* ensures any changed part of the window is repainted.             */
  1633. /* ---------------------------------------------------------------  */
  1634. void addchars(BYTE *pch, unsigned cch)
  1635.     {
  1636.     int ycSave = whwndCurr->yCurrLine;
  1637.     int ytSave = whwndCurr->yTopOfWin;
  1638.     int xSave = whwndCurr->xLeftOfWin;
  1639.  
  1640.     make_avail(cch);
  1641.  
  1642.     append2buffer(pch, cch);
  1643.  
  1644.     if (whwndCurr->tPaint && (ycSave != whwndCurr->yCurrLine))
  1645.         SetScrollRange(hCurrWnd, SB_VERT, 1, whwndCurr->yCurrLine + 1, FALSE);
  1646.  
  1647.     compute_repaint();
  1648.  
  1649.     whwndCurr->cScrollUD[SB_BOTTOM] = whwndCurr->yCurrLine;
  1650.     
  1651.     if (whwndCurr->tPaint)
  1652.         {
  1653.         if (ytSave != whwndCurr->yTopOfWin)
  1654.             SetScrollPos(hCurrWnd, SB_VERT, whwndCurr->yTopOfWin + 1, TRUE);
  1655.         if (xSave != whwndCurr->xLeftOfWin)
  1656.             SetScrollPos(hCurrWnd, SB_HORZ, whwndCurr->xLeftOfWin + 1, TRUE);
  1657. // Move out of the 'if' ?
  1658.         wmhandler_yield();
  1659.         }
  1660.     }
  1661.  
  1662.  
  1663. /* ---------------------------------------------------------------  */
  1664. /* Add chars onto the display buffer, wrapping at end of line,      */
  1665. /* expanding tabs, etc.                                             */
  1666. /* ---------------------------------------------------------------  */
  1667. void append2buffer(BYTE *pch, unsigned cch)
  1668.     {
  1669.     unsigned i;
  1670.  
  1671.     for (i = 0; i < cch; i++, pch++)
  1672.         {
  1673.         switch (*pch)
  1674.             {
  1675.             case '\r' :
  1676.                 whwndCurr->fpCurr = whwndCurr->fpSOI;
  1677.                 whwndCurr->xCurrPos = 0;
  1678.                 whwndCurr->tCR = TRUE;
  1679.                 break;
  1680.             case '\n' :
  1681.                 *pch = '\0';
  1682.                 *whwndCurr->fpCurr++ = '\0';
  1683.                 whwndCurr->fpCurrLine = whwndCurr->fpCurr;
  1684.                 whwndCurr->fpSOI = whwndCurr->fpCurr;
  1685.                 whwndCurr->yCurrLine++;
  1686.                 whwndCurr->xCurrPos = 0;
  1687.                 break;
  1688.             case '\t' :
  1689.                 do  {
  1690.                 /* Special requirement of tabs... */
  1691.                     make_avail(cch - i + TABSIZE);
  1692.                     *whwndCurr->fpCurr++ = ' ';
  1693.                     whwndCurr->xCurrPos++;
  1694.                     } while ((whwndCurr->xCurrPos % TABSIZE) != 0);
  1695.                 break;
  1696.             case 0xff /* EOF */ :
  1697.                 break;
  1698.             case '\b' :
  1699.                 if (whwndCurr->fpCurr > whwndCurr->fpSOI)
  1700.                     {
  1701.                     *(--whwndCurr->fpCurr) = 0;
  1702.                     whwndCurr->bufused--;
  1703.                     whwndCurr->xCurrPos--;
  1704.                     }
  1705.                 break;
  1706.             case 0x1b :
  1707.                 while (whwndCurr->fpCurr > whwndCurr->fpSOI)
  1708.                     {
  1709.                     *(--whwndCurr->fpCurr) = 0;
  1710.                     whwndCurr->bufused--;
  1711.                     whwndCurr->xCurrPos--;
  1712.                     }
  1713.                 break;
  1714.             case 0x07 :
  1715.                 MessageBeep(0);
  1716.                 break;
  1717.             default :
  1718.                 if (*pch > 0x1a)
  1719.                     {
  1720.                     if (whwndCurr->xCurrPos >= MAX_X)
  1721.                         {
  1722.                         *whwndCurr->fpCurr++ = 0;
  1723.                         whwndCurr->xCurrPos = 0;
  1724.                         whwndCurr->yCurrLine++;
  1725.                         whwndCurr->fpCurrLine = whwndCurr->fpCurr;
  1726.                         }
  1727.                     whwndCurr->xCurrPos++;
  1728.                     *whwndCurr->fpCurr++ = *pch;
  1729.                     }
  1730.             }
  1731.         }
  1732.  
  1733.     if ((whwndCurr->fpBuffer + whwndCurr->bufused) < whwndCurr->fpCurr)
  1734.         {
  1735.         whwndCurr->bufused = (whwndCurr->fpCurr - whwndCurr->fpBuffer);
  1736.         *whwndCurr->fpCurr = '\0';
  1737.         }
  1738.     }
  1739.  
  1740. /* ---------------------------------------------------------------  */
  1741. /* If we have run out of room in the display buffer, drop whole     */
  1742. /* lines, and move the remaining buffer up.                         */
  1743. /* ---------------------------------------------------------------  */
  1744. void make_avail(unsigned cch)
  1745.     {
  1746.     unsigned cDiscard = 0;
  1747.     BYTE far *fpNew;
  1748.     BYTE far *fpTmp;
  1749.     unsigned yDrop, min_discard;
  1750.  
  1751.     // Do we have enough room for what we want to add (allow 1 for \0) ?
  1752.     if ((unsigned long)(whwndCurr->bufused + cch + 1) < whwndCurr->bufsize)
  1753.         return;
  1754.  
  1755.     // Calulate how much should we should drop off the bottom
  1756.     min_discard = (WORD) whwndCurr->bufsize >> 3;
  1757.  
  1758.     // this gives a whole number of our allocation units.
  1759.     cDiscard = ((cch + min_discard - 1) / min_discard) * min_discard;
  1760.         
  1761.     // Adjust to line up at the next start of line
  1762.     fpNew = whwndCurr->fpBuffer;
  1763.     fpNew += (LONG) cDiscard;
  1764.     fpTmp = whwndCurr->fpBuffer;
  1765.     for (yDrop = 0; fpTmp < fpNew; fpTmp = nextline(fpTmp), yDrop++) ;
  1766.     cDiscard = fpTmp - whwndCurr->fpBuffer; 
  1767.  
  1768.     // Move the buffer 'up', and initialize vacated to zeros
  1769.     _fmemcpy(whwndCurr->fpBuffer, fpTmp, whwndCurr->bufused - cDiscard + 1);
  1770.     whwndCurr->bufused -= cDiscard;
  1771.     whwndCurr->fpCurr -= cDiscard;
  1772.     _fmemset(whwndCurr->fpBuffer + whwndCurr->bufused, 0, cDiscard);
  1773.     
  1774.     // Now we know how far we have moved, adjust other pointers
  1775.     if (whwndCurr->fpSOI != NULL) whwndCurr->fpSOI -= cDiscard;
  1776.     whwndCurr->fpCurrLine -= cDiscard;
  1777.     whwndCurr->yCurrLine -= yDrop;
  1778.     whwndCurr->fpTopOfWin -= cDiscard;
  1779.     whwndCurr->yTopOfWin -=yDrop;
  1780.     whwndCurr->cLinesDiscarded += yDrop;
  1781.     }
  1782.  
  1783.  
  1784. /* -------------------------------------------------------------------  */
  1785. /* These two routines find the beginning of the next, and previous      */
  1786. /* lines relative to their input pointer                                */
  1787. /* -------------------------------------------------------------------  */
  1788.  
  1789. BYTE far *nextline(BYTE far *p) { while (*p) p++; return ++p; }
  1790. BYTE far *prevline(BYTE far *p) { p--; do p--; while (*p); return ++p; }
  1791.  
  1792. /* -------------------------------------------------------------------  */
  1793. /* Waits for a character to appear in the keyboard buffer, yielding     */
  1794. /* while nothing is available. Then inserts it into the buffer.         */
  1795. /* -------------------------------------------------------------------  */
  1796. int chInput(void)
  1797.     {
  1798.     BYTE c;
  1799.     
  1800.     while (whwndCurr && (whwndCurr->pchKbIn == whwndCurr->pchKbOut))
  1801.         wmhandler_yield();
  1802.         
  1803.     if (! whwndCurr) 
  1804.         abort_exit();
  1805.     
  1806.     c = *(whwndCurr->fpKeyboard + whwndCurr->pchKbOut);
  1807.  
  1808.     whwndCurr->pchKbOut++;
  1809.     if (whwndCurr->pchKbOut == TYPE_AHEAD)
  1810.         whwndCurr->pchKbOut = 0;
  1811.     
  1812.     // Do CR/LF and EOF translation
  1813.     return (c == 0x1a) ? EOF : (c == '\r') ? '\n' : c;
  1814.     }
  1815.  
  1816.