home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / jove-4.16-src.tgz / tar.out / bsd / jove / win32.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  22KB  |  855 lines

  1. /************************************************************************
  2.  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
  3.  * provided to you without charge, and with no warranty.  You may give  *
  4.  * away copies of JOVE, including sources, provided that this notice is *
  5.  * included in all the files.                                           *
  6.  ************************************************************************/
  7.  
  8. /* Win32 support routines for Jove Keyboard and Screen */
  9.  
  10. #include "jove.h"
  11.  
  12. #ifdef WIN32    /* the body is the rest of this file */
  13.  
  14. #include "fp.h"    /* scr_putchar */
  15. #include "chars.h"
  16. #include "screen.h"
  17. #include "disp.h"    /* for redisplay() */
  18.  
  19. #include <windows.h>
  20.  
  21. INPUT_RECORD in_event[NCHARS], *eventp = in_event;    /* Input events e.g. keyboard, mouse-click */
  22. int nevents;
  23. private HANDLE conin, conout, conerr;    /* Console handles */
  24. private COORD curpos;
  25. private COORD maxpos;
  26. private HANDLE old_stdout, old_stderr;
  27. private WORD  old_attributes;
  28.  
  29. private BOOL WINAPI ctrlHandler proto((DWORD type));    /* Control handler */
  30. #define CHECK(fn)    { if (!(fn)) ConsoleFail(#fn); }
  31. private void ConsoleFail(char *fdef);
  32.  
  33. void
  34. getTERM()
  35. {
  36. }
  37.  
  38. void
  39. ttysetattr(n)
  40. bool    n;    /* also used as subscript! */
  41. {
  42.     CONSOLE_SCREEN_BUFFER_INFO info;
  43.     COORD bufsize;
  44.     DWORD version;
  45.  
  46.     if (n) {
  47.         if (conout == NULL) {
  48.             /* Create our own console buffer so we can easily restore
  49.              * the startup environment. This also allows us to resize
  50.              * it to eliminate the scroll bars, at least where the
  51.              * full Console API is implemented (i.e. on Windows NT).
  52.              * (Windows 95 seems to ignore whatever we do to the window).
  53.              */
  54.             old_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
  55.             old_stderr = GetStdHandle(STD_ERROR_HANDLE);
  56.             CHECK(GetConsoleScreenBufferInfo(old_stdout, &info));
  57.             old_attributes = info.wAttributes;
  58.             conout = CreateConsoleScreenBuffer(GENERIC_READ|GENERIC_WRITE,
  59.                 FILE_SHARE_READ|FILE_SHARE_WRITE,
  60.                 NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
  61.             CHECK(DuplicateHandle(GetCurrentProcess(), conout,
  62.                     GetCurrentProcess(), &conerr,
  63.                     0, TRUE, DUPLICATE_SAME_ACCESS));
  64.             bufsize.X = info.srWindow.Right-info.srWindow.Left+1;
  65.             bufsize.Y = info.srWindow.Bottom-info.srWindow.Top+1;
  66.             CHECK(SetConsoleScreenBufferSize(conout, bufsize));
  67.             version = GetVersion();
  68. #ifdef DEBUG
  69.             {
  70.                 char tracebuf[100];
  71.  
  72.                 wsprintf(tracebuf, "%s Windows %d.%d Build %d\n",
  73.                     (version > 0 ? "MS-DOS" : "NT"),
  74.                     LOBYTE(LOWORD(version)), HIBYTE(LOWORD(version)),
  75.                     HIWORD(version)&0x7FFF);
  76.                 OutputDebugString(tracebuf);
  77.                 wsprintf(tracebuf, "Console Size=(%d,%d), Window=(%d,%d) to (%d,%d)\n",
  78.                             info.dwSize.X, info.dwSize.Y,
  79.                             info.srWindow.Left, info.srWindow.Top,
  80.                             info.srWindow.Right, info.srWindow.Bottom);
  81.                 OutputDebugString(tracebuf);
  82.             }
  83. #endif
  84.             CHECK(SetStdHandle(STD_OUTPUT_HANDLE, conout));
  85.             CHECK(SetStdHandle(STD_ERROR_HANDLE, conerr));
  86.             CHECK(SetConsoleActiveScreenBuffer(conout));
  87.         }
  88.         CHECK(SetConsoleTitle("Jove for Win32"));
  89.         conin = GetStdHandle(STD_INPUT_HANDLE);
  90.         /* conout = GetStdHandle(STD_OUTPUT_HANDLE); */
  91.  
  92. # ifdef MOUSE
  93.         SetConsoleMode(conin,
  94.             ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
  95.         MouseOn();
  96. # else
  97.         SetConsoleMode(conin, ENABLE_WINDOW_INPUT);
  98. # endif /* !MOUSE */
  99.         SetConsoleMode(conout,
  100.             ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
  101.         SetFileApisToOEM();
  102.         SetConsoleCtrlHandler(ctrlHandler, TRUE);
  103.     } else {
  104.         CHECK(SetStdHandle(STD_OUTPUT_HANDLE, old_stdout));
  105.         CHECK(SetStdHandle(STD_ERROR_HANDLE, old_stderr));
  106.         CHECK(SetConsoleActiveScreenBuffer(old_stdout));
  107.         CloseHandle(conout);
  108.         CloseHandle(conerr);
  109.         SetConsoleTextAttribute(old_stdout, old_attributes);
  110.         conin = conout = conerr = NULL;
  111. # ifdef MOUSE
  112.         MouseOff();
  113. # endif
  114.         SetConsoleCtrlHandler(ctrlHandler, FALSE);
  115.     }
  116. }
  117.  
  118. void
  119. ttsize()
  120. {
  121.     /* ??? We really ought to wait until the screen is big enough:
  122.      * at least three lines high (one line each for buffer, mode,
  123.      * and message) and at least twelve columns wide (eight for
  124.      * line number, one for content, two for overflow indicators,
  125.      * and one blank at end).
  126.      */
  127.     CONSOLE_SCREEN_BUFFER_INFO info;
  128.     GetConsoleScreenBufferInfo(conout, &info);
  129.     maxpos.X = info.srWindow.Right-info.srWindow.Left+1;
  130.     maxpos.Y = info.srWindow.Bottom-info.srWindow.Top+1;
  131.     if (maxpos.X != info.dwSize.X || maxpos.Y != info.dwSize.Y) {
  132.         CHECK(SetConsoleScreenBufferSize(conout, maxpos));
  133.         curpos.X = curpos.Y = 0;
  134.     } else {
  135.         curpos = info.dwCursorPosition;
  136.     }
  137.     CO = maxpos.X;
  138.     if (CO > MAXCOLS)
  139.         CO = MAXCOLS;
  140.     LI = maxpos.Y;
  141.     ILI = LI - 1;
  142. }
  143.  
  144. #ifdef DEBUG
  145. private char *
  146. formatChar(char c)
  147. {
  148.     static char cbuf[6];
  149.  
  150.     if (c >= ' ' && c <= '~')
  151.         sprintf(cbuf, "%c", c);
  152.     else
  153.         sprintf(cbuf, "\\%03o", (unsigned char)c);
  154.     return cbuf;
  155. }
  156.  
  157. private char *
  158. formatEvent(INPUT_RECORD* event)
  159. {
  160.     static char buffer[128];
  161.  
  162.     switch (event->EventType) {
  163.       case KEY_EVENT:
  164.         sprintf(buffer, "KEY%s '%s%s%s%s'[%d times] Scan=%d Keycode=%d\n",
  165.             event->Event.KeyEvent.bKeyDown?"DOWN":"UP",
  166.             (event->Event.KeyEvent.dwControlKeyState &
  167.                 (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED) ? "ALT-" : ""),
  168.             (event->Event.KeyEvent.dwControlKeyState &
  169.                 (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED) ? "CTRL-" : ""),
  170.             (event->Event.KeyEvent.dwControlKeyState &
  171.                 (SHIFT_PRESSED) ? "SHIFT-" : ""),
  172.             formatChar(event->Event.KeyEvent.uChar.AsciiChar),
  173.             event->Event.KeyEvent.wRepeatCount,
  174.             event->Event.KeyEvent.wVirtualScanCode,
  175.             event->Event.KeyEvent.wVirtualKeyCode
  176.             );
  177.         break;
  178.       case MOUSE_EVENT:
  179.         sprintf(buffer, "MOUSE%s%s %s%s%s%s%s%s (%d,%d)\n",
  180.                 (event->Event.MouseEvent.dwEventFlags&MOUSE_MOVED ?
  181.                         " MOVED" : ""),
  182.                 (event->Event.MouseEvent.dwEventFlags&DOUBLE_CLICK?
  183.                         " DOUBLE_CLICK" : ""),
  184.                 (event->Event.MouseEvent.dwButtonState&1 ? "LEFT" : ""),
  185.                 (event->Event.MouseEvent.dwButtonState&2 ? "RIGHT" : ""),
  186.                 (event->Event.MouseEvent.dwButtonState&4 ? "MIDDLE" : ""),
  187.                 (event->Event.MouseEvent.dwControlKeyState &
  188.                     (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED) ? "ALT-" : ""),
  189.                 (event->Event.MouseEvent.dwControlKeyState &
  190.                     (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED) ? "CTRL-" : ""),
  191.                 (event->Event.MouseEvent.dwControlKeyState &
  192.                     (SHIFT_PRESSED) ? "SHIFT-" : ""),
  193.                 event->Event.MouseEvent.dwMousePosition.X,
  194.                 event->Event.MouseEvent.dwMousePosition.Y
  195.                 );
  196.         break;
  197.       case WINDOW_BUFFER_SIZE_EVENT:
  198.         sprintf(buffer, "RESIZE (%d,%d)\n",
  199.             eventp->Event.WindowBufferSizeEvent.dwSize.Y,
  200.             eventp->Event.WindowBufferSizeEvent.dwSize.X);
  201.         break;
  202.       case MENU_EVENT:
  203.         sprintf(buffer, "MENU %d\n", eventp->Event.MenuEvent.dwCommandId);
  204.         break;
  205.       case FOCUS_EVENT:
  206.         sprintf(buffer, "FOCUS %d\n", eventp->Event.FocusEvent.bSetFocus);
  207.         break;
  208.       default:
  209.         sprintf(buffer, "UNKNOWN %d\n", eventp->EventType);
  210.         break;
  211.     }
  212.     return buffer;
  213. }
  214. #endif /* DEBUG */
  215.  
  216. /* MapKeyEventToChars maps Console KEY_RECORDs into a string of characters.
  217.  * The mapping rules are basically those of the IBM AT BIOS as modified for
  218.  * use by JOVE (i.e. the result should agree with what a JOVE user would see
  219.  * running the MS-DOS version of Jove).
  220.  */
  221. private int
  222. MapKeyEventToChars(KEY_EVENT_RECORD* key, char *bptr)
  223. {
  224.     int cnt = 0;
  225.  
  226.     if (key->bKeyDown) {
  227.         if (key->uChar.AsciiChar) {
  228.             if ((key->dwControlKeyState &
  229.                 (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED))
  230.             && key->uChar.AsciiChar == ' ')
  231.             {
  232.                 bptr[cnt++] = '\0';
  233.             } else if (key->dwControlKeyState &
  234.                 (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED))
  235.             {
  236.                 bptr[cnt++] = PCNONASCII;
  237.                 if (key->wVirtualScanCode >= 2
  238.                 && key->wVirtualScanCode <= 13)
  239.                 {
  240.                     /* Top row (numeric keys) */
  241.                     bptr[cnt++] = key->wVirtualScanCode + 118;
  242.                 } else {
  243.                     bptr[cnt++] = key->wVirtualScanCode;
  244.                 }
  245.             } else {
  246.                 bptr[cnt++] = key->uChar.AsciiChar;
  247.             }
  248.         } else {
  249. #            define ShiftSelect(plain, shifted, ctrled, alted) ( \
  250.                 (key->dwControlKeyState & SHIFT_PRESSED) \
  251.                     ? (shifted) \
  252.                 : (key->dwControlKeyState & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED)) \
  253.                     ? (ctrled) \
  254.                 : (key->dwControlKeyState & (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED)) \
  255.                     ? (alted) \
  256.                 : (plain))
  257.  
  258.             switch (key->wVirtualKeyCode) {
  259.               case VK_F1: case VK_F2: case VK_F3: case VK_F4: case VK_F5:
  260.               case VK_F6: case VK_F7: case VK_F8: case VK_F9: case VK_F10:
  261.                 bptr[cnt++] = PCNONASCII;
  262.                 bptr[cnt++] = key->wVirtualKeyCode - VK_F1
  263.                     + ShiftSelect(59, 84, 94, 104);
  264.                 break;
  265.               case VK_F11: case VK_F12:
  266.                 bptr[cnt++] = PCNONASCII;
  267.                 bptr[cnt++] = key->wVirtualKeyCode - VK_F11
  268.                     + ShiftSelect(133, 135, 137, 139);
  269.                 break;
  270.               case '1': case '2': case '3': case '4': case '5':
  271.               case '6': case '7': case '8': case '9': case '0':
  272.                 bptr[cnt++] = PCNONASCII;
  273.                 bptr[cnt++] = key->wVirtualScanCode + 118;
  274.                 break;
  275.               case VK_HOME:
  276.                 bptr[cnt++] = PCNONASCII;
  277.                 bptr[cnt++] = ShiftSelect(71, 171, 119, 151);
  278.                 break;
  279.               case VK_UP:
  280.                 bptr[cnt++] = PCNONASCII;
  281.                 bptr[cnt++] = ShiftSelect(72, 171, 141, 152);
  282.                 break;
  283.               case VK_PRIOR:
  284.                 bptr[cnt++] = PCNONASCII;
  285.                 bptr[cnt++] = ShiftSelect(73, 172, 132, 153);
  286.                 break;
  287.               case VK_SUBTRACT:    /* KEYPAD- */
  288.                 bptr[cnt++] = PCNONASCII;
  289.                 if (key->dwControlKeyState &
  290.                         (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED))
  291.                 {
  292.                     if (key->dwControlKeyState & SHIFT_PRESSED) {
  293.                         bptr[cnt++] = 174;
  294.                     } else {
  295.                         bptr[cnt++] = 74;
  296.                     }
  297.                 } else {
  298.                     bptr[cnt++] = 142;
  299.                 }
  300.                 break;
  301.               case VK_LEFT:
  302.                 bptr[cnt++] = PCNONASCII;
  303.                 bptr[cnt++] = ShiftSelect(75, 175, 115, 155);
  304.                 break;
  305.               case VK_RIGHT:
  306.                 bptr[cnt++] = PCNONASCII;
  307.                 bptr[cnt++] = ShiftSelect(77, 177, 116, 157);
  308.                 break;
  309.               case VK_ADD:    /* KEYPAD+ */
  310.                 bptr[cnt++] = PCNONASCII;
  311.                 if (key->dwControlKeyState &
  312.                         (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED))
  313.                 {
  314.                     if (key->dwControlKeyState & SHIFT_PRESSED) {
  315.                         bptr[cnt++] = 178;
  316.                     } else {
  317.                         bptr[cnt++] = 78;
  318.                     }
  319.                 } else {
  320.                     bptr[cnt++] = 144;
  321.                 }
  322.                 break;
  323.               case VK_END:
  324.                 bptr[cnt++] = PCNONASCII;
  325.                 bptr[cnt++] = ShiftSelect(79, 179, 117, 159);
  326.                 break;
  327.               case VK_DOWN:
  328.                 bptr[cnt++] = PCNONASCII;
  329.                 bptr[cnt++] = ShiftSelect(80, 180, 145, 160);
  330.                 break;
  331.               case VK_NEXT:
  332.                 bptr[cnt++] = PCNONASCII;
  333.                 bptr[cnt++] = ShiftSelect(81, 181, 118, 161);
  334.                 break;
  335.               case VK_INSERT:
  336.                 bptr[cnt++] = PCNONASCII;
  337.                 bptr[cnt++] = ShiftSelect(82, 182, 146, 162);
  338.                 break;
  339.               case VK_DELETE:
  340.                 if (key->dwControlKeyState & SHIFT_PRESSED) {
  341.                     bptr[cnt++] = PCNONASCII;
  342.                     bptr[cnt++] = 183;
  343.                 } else if (key->dwControlKeyState &
  344.                     (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED))
  345.                 {
  346.                     bptr[cnt++] = PCNONASCII;
  347.                     bptr[cnt++] = 147;
  348.                 } else if (key->dwControlKeyState &
  349.                     (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED))
  350.                 {
  351.                     bptr[cnt++] = PCNONASCII;
  352.                     bptr[cnt++] = 163;
  353.                 } else {
  354.                     bptr[cnt++] = '\177';    /* Mapped to ASCII DEL */
  355.                 }
  356.                 break;
  357.               case VK_DIVIDE:    /* KEYPAD/ */
  358.                 bptr[cnt++] = PCNONASCII;
  359.                 if (key->dwControlKeyState &
  360.                         (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED))
  361.                 {
  362.                     bptr[cnt++] = 164;
  363.                 } else {
  364.                     bptr[cnt++] = 149;
  365.                 }
  366.                 break;
  367.               case VK_MULTIPLY:    /* KEYPAD* */
  368.                 bptr[cnt++] = PCNONASCII;
  369.                 if (key->dwControlKeyState &
  370.                         (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED))
  371.                 {
  372.                     bptr[cnt++] = 55;
  373.                 } else {
  374.                     bptr[cnt++] = 150;
  375.                 }
  376.                 break;
  377.               case VK_NUMPAD5:
  378.                 bptr[cnt++] = PCNONASCII;
  379.                 bptr[cnt++] = 143;
  380.                 break;
  381.               case VK_PRINT:    /* PRINT SCREEN */
  382.                 bptr[cnt++] = PCNONASCII;
  383.                 bptr[cnt++] = 113;
  384.                 break;
  385.               case VK_ESCAPE:
  386.                 if (key->dwControlKeyState & (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED)) {
  387.                     bptr[cnt++] = PCNONASCII;
  388.                     bptr[cnt++] = key->wVirtualScanCode;
  389.                 } else {
  390.                     bptr[cnt++] = '\033';    /* ASCII ESC */
  391.                 }
  392.                 break;
  393.               case 'C':    /* PRINT SCREEN */
  394.                 if (key->dwControlKeyState & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED)) {
  395.                     bptr[cnt++] = '\003';    /* Ctrl-C : remapped in Windows 95 */
  396.                 } else {
  397.                     bptr[cnt++] = PCNONASCII;
  398.                     bptr[cnt++] = key->wVirtualScanCode;
  399.                 }
  400.                 break;
  401.               case VK_CAPITAL:
  402.               case VK_SHIFT:
  403.               case VK_CONTROL:
  404.               case VK_MENU:
  405.               case VK_NUMLOCK:
  406.               case VK_SCROLL:
  407.                 /* Modifiers - ignore them. */
  408.                 break;
  409.               default:
  410.                 bptr[cnt++] = PCNONASCII;
  411.                 bptr[cnt++] = key->wVirtualScanCode;
  412.                 break;
  413.             }
  414. #            undef ShiftSelect
  415.         }
  416.     }
  417.     return cnt;
  418. }
  419.  
  420. int
  421. getInputEvents(char *bp, int size)
  422. {
  423.     int nchars = 0;
  424.  
  425.     if (eventp >= in_event+nevents) {
  426.         if (!ReadConsoleInput(conin,
  427.                               in_event, NCHARS, &nevents))
  428.             complain("ReadConsole failed (shouldn't happen)");
  429.         eventp = in_event;
  430.     }
  431.     while (eventp < in_event+nevents) {
  432.         switch (eventp->EventType) {
  433.           case KEY_EVENT:
  434.             nchars += MapKeyEventToChars(&eventp->Event.KeyEvent, bp+nchars);
  435.             if (--eventp->Event.KeyEvent.wRepeatCount == 0)
  436.                 eventp += 1;
  437.             break;
  438.           case MOUSE_EVENT:
  439. #ifdef MOUSE
  440.             /* We are only interested in hits */
  441.             if (eventp->Event.MouseEvent.dwButtonState != 0) {
  442.                 bp[nchars++] = PCNONASCII;
  443.                 bp[nchars++] = '\xb0'+((char)eventp->Event.MouseEvent.dwButtonState&017);
  444.                 bp[nchars++] = (char)eventp->Event.MouseEvent.dwMousePosition.X;
  445.                 bp[nchars++] = (char)eventp->Event.MouseEvent.dwMousePosition.Y;
  446.             }
  447. #endif
  448.             eventp += 1;
  449.             break;
  450.  
  451.           case WINDOW_BUFFER_SIZE_EVENT:
  452.             maxpos = eventp->Event.WindowBufferSizeEvent.dwSize;
  453.             eventp += 1;
  454.             ResizePending = YES;
  455.             break;
  456.           default:
  457.             eventp += 1;
  458.             break;
  459.         }
  460.         if (nchars >= size - 5)
  461.             break;
  462.     }
  463.     return nchars;
  464. }
  465.  
  466. BOOL
  467. inputEventWaiting(int period)
  468. {
  469. #ifdef IPROCS
  470.     return wait_for_any_input(period) == WAIT_OBJECT_0;
  471. #else
  472.     INPUT_RECORD evnt[10];
  473.     PINPUT_RECORD pEvnt;
  474.     DWORD cnt;
  475.     static BOOL first=TRUE;
  476.  
  477.     if (first)
  478.         return first=FALSE;    /* Force display on first call */
  479.     if (eventp < in_event+nevents)
  480.         return TRUE;    /* Already something in the queue */
  481.     while (WaitForSingleObject(conin, period) == WAIT_OBJECT_0) {
  482.         if (!PeekConsoleInput(conin, evnt
  483.         , sizeof(evnt)/sizeof(evnt[0]), &cnt))
  484.             break;
  485.         /* Check if the waiting event is of interest. */
  486.         for (pEvnt=evnt; pEvnt<evnt+cnt; ++pEvnt) {
  487.             if ((pEvnt->EventType == KEY_EVENT
  488.               && pEvnt->Event.KeyEvent.bKeyDown)
  489.             || pEvnt->EventType == WINDOW_BUFFER_SIZE_EVENT
  490.             || pEvnt->EventType == MOUSE_EVENT)
  491.                 return TRUE;
  492.         }
  493.         /* If we reach here, the event(s) are uninteresting - chuck them. */
  494.         if (!ReadConsoleInput(conin, evnt, cnt, &cnt))
  495.             break;
  496.     }
  497.     return FALSE;
  498. #endif
  499. }
  500.  
  501. private void
  502. SaveBufferFile(Buffer *b)
  503. {
  504.     Buffer *save_buf = curbuf;
  505.     char title[80];
  506.  
  507.     if (b->b_fname) {
  508.         wsprintf(title, "Save Jove buffer `%s'?", b->b_name);
  509.         if (MessageBox(NULL, b->b_fname, title, MB_YESNO) != IDYES)
  510.             return;
  511.     } else {
  512.         OPENFILENAME ofn;    /* common dialog box structure */
  513.         char szFile[_MAX_PATH];    /* filename string */
  514.         char szDirPath[_MAX_PATH];    /* filename string */
  515.  
  516.         /* Set the members of the OPENFILENAME structure. */
  517.         memset(&ofn, 0, sizeof(ofn));
  518.         ofn.lStructSize = sizeof(OPENFILENAME);
  519.         szFile[0] = '\0';
  520.         ofn.lpstrFile = szFile;
  521.         ofn.nMaxFile = _MAX_PATH;;
  522.         ofn.lpstrFilter =    "All (*.*)\0*.*\0"
  523.                             "C source (*.c;*.cpp;*.cxx)\0*.c;*.cpp;*,cxx\0"
  524.                             "C header (*.h;*.hpp;*.hxx)\0*.h;*.hpp;*.hxx\0";
  525.         ofn.lpstrInitialDir = szDirPath;
  526.         ofn.lpstrTitle = title;
  527.         wsprintf(title, "Save Jove buffer `%s' as:", b->b_name);
  528.  
  529.         getcwd(szDirPath);
  530.         ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT |
  531.             OFN_PATHMUSTEXIST;
  532.         if (!GetSaveFileName(&ofn))
  533.             return;
  534.         setfname(b, ofn.lpstrFile);
  535.     }
  536.  
  537.     /* Still here? Save the file, then. */
  538.     SetBuf(b);
  539.     SaveFile();
  540.     SetBuf(save_buf);
  541. }
  542.  
  543. private void
  544. MessageCloseFiles()
  545. {
  546.     /* We use a static buffer pointer so that we can detect if we
  547.      * have been re-entered.  If so, we do nothing.  This can happen
  548.      * if the user takes too long to respond).
  549.      */
  550.     static Buffer *b;
  551.  
  552.     if (b == NULL)
  553.         for (b = world; b != NULL; b = b->b_next)
  554.             if (b->b_type != B_SCRATCH
  555.             && b->b_type == B_FILE
  556.             && IsModified(b))
  557.                 SaveBufferFile(b);
  558. }
  559.  
  560. /* Control interrupt handler deals with CTRL-BREAK and exit requests */
  561. private BOOL WINAPI
  562. ctrlHandler(DWORD type)
  563. {
  564.     switch(type) {
  565.       case CTRL_C_EVENT:
  566.         return TRUE;    /* Ignore, keyboard handler will see it. */
  567.         break;
  568.       case CTRL_BREAK_EVENT:
  569.         return FALSE;
  570.         break;
  571.       case CTRL_CLOSE_EVENT:
  572.       case CTRL_LOGOFF_EVENT:
  573.       case CTRL_SHUTDOWN_EVENT:
  574.         MessageCloseFiles();
  575.         break;
  576.     }
  577.     return FALSE;    /* Carry on */
  578. }
  579.  
  580. private WORD
  581.     c_attr = 0x07,    /* current attribute white on black */
  582.     c_row = 0,    /* current row */
  583.     c_col = 0;    /* current column */
  584.  
  585. int
  586.     Txattr = 0x07,    /* VAR: text-attribute (white on black) */
  587.     Mlattr = 0x70,    /* VAR: mode-line-attribute (black on white) */
  588.     Hlattr = 0x10;    /* VAR: highlight-attribute */
  589.  
  590. #define c_row curpos.Y
  591. #define c_col curpos.X
  592.  
  593. #define cur_mov(r, c)    { \
  594.         curpos.X = (c); curpos.Y = (r); \
  595.         CHECK(SetConsoleCursorPosition(conout, curpos)); \
  596.     }
  597. #define setcolor(c)    { \
  598.         c_attr = (c); \
  599.         CHECK(SetConsoleTextAttribute(conout, c_attr)); \
  600.     }
  601.  
  602. /* WIN32 calls aren't cheap, so we buffer output */
  603.  
  604. private char displaybuf[1024];
  605. private int bufpos = 0;
  606.  
  607. /* getLastErrorString is a WIN32 helper function that returns a description
  608.  * of the error code that GetLastError() returns.
  609.  * Implementation note: we use a mode of operation whereby FormatMessage allocates
  610.  * a string large enough; the returned value will be valid until the function is
  611.  * called again.
  612.  */
  613. private char *reason;
  614.  
  615. private void
  616. freeReason(void)
  617. {
  618.     if (reason) {
  619.         VirtualFree((PVOID)reason, 0, MEM_RELEASE);
  620.         reason = NULL;
  621.     }
  622. }
  623.  
  624. char *
  625. getLastErrorString()
  626. {
  627.     static BOOL cleanupRegistered;
  628.     char *ptr;
  629.  
  630.     if (!cleanupRegistered) {
  631.         atexit(freeReason);
  632.         cleanupRegistered = TRUE;
  633.     }
  634.     freeReason();
  635.     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER,
  636.                 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
  637.                 (char*)&reason, 0, NULL);
  638.     if (((ptr = strchr(reason, '\r')) != NULL)
  639.     || ((ptr = strchr(reason, '\n')) != NULL))
  640.         *ptr = '\0';    /* Trim off carriage control */
  641.     return reason;
  642. }
  643.  
  644. private void
  645. ConsoleFail(char *fdef)
  646. {
  647.     char why[200];
  648.     char *reason = getLastErrorString();
  649.  
  650.     sprintf(why, "Console function \"%s\" failed", fdef);
  651.     if (MessageBox(NULL, getLastErrorString(), why, MB_OKCANCEL) == IDCANCEL)
  652.         abort();
  653. }
  654.  
  655. private void
  656. scr_win(int no, int ulr, int ulc, int lrr, int lrc)
  657. {
  658.     SMALL_RECT r, clip;
  659.     COORD whereto;
  660.     CHAR_INFO charinfo;
  661.     short height = lrr - ulr + 1;
  662.  
  663.     clip.Top = ulr;
  664.     clip.Left = ulc;
  665.     clip.Bottom = lrr;
  666.     clip.Right = lrc;
  667.     whereto.X = ulc;
  668.     r.Left = ulc;
  669.     r.Right = lrc;
  670.     if (no > 0) {
  671.         r.Top = ulr + no;
  672.         r.Bottom = lrr;
  673.         whereto.Y = ulr;
  674.     } else {
  675.         r.Top = ulr;
  676.         r.Bottom = lrr + no;
  677.         whereto.Y = ulr - no;
  678.     }
  679.  
  680.     charinfo.Char.AsciiChar = ' ';
  681.     charinfo.Attributes = c_attr;
  682.  
  683.     CHECK(ScrollConsoleScreenBuffer(conout, &r, &clip, whereto, &charinfo));
  684.  
  685.     /* If the scrolled region is less than half of the clip area, there will
  686.      * be an unscrolled area in the middle - it must be filled with space.
  687.      */
  688.     if (no > 0) {
  689.         whereto.Y += (r.Bottom - r.Top + 1);
  690.         while (whereto.Y < r.Top) {
  691.             DWORD written;
  692.  
  693.             CHECK(FillConsoleOutputCharacter(conout, ' ', lrc-ulc+1, whereto, &written));
  694.             whereto.Y += 1;
  695.         }
  696.     } else {
  697.         while (--whereto.Y > r.Bottom) {
  698.             DWORD written;
  699.  
  700.             CHECK(FillConsoleOutputCharacter(conout, ' ', lrc-ulc+1, whereto, &written));
  701.         }
  702.     }
  703. }
  704.  
  705. void
  706. i_lines(top, bottom, num)
  707. int top, bottom, num;
  708. {
  709.     scr_win(-num, top, 0, bottom, CO-1);
  710. }
  711.  
  712. void
  713. d_lines(top, bottom, num)
  714. int top, bottom, num;
  715. {
  716.     scr_win(num, top, 0, bottom, CO-1);
  717. }
  718.  
  719. void
  720. clr_page()
  721. {
  722.     long written;
  723.  
  724.     SO_off();
  725.     cur_mov(0, 0);
  726.     CHECK(FillConsoleOutputCharacter(conout, ' ', maxpos.X*maxpos.Y, curpos, &written));
  727.     CHECK(FillConsoleOutputAttribute(conout, c_attr, maxpos.X*maxpos.Y, curpos, &written));
  728. }
  729.  
  730. void
  731. flushscreen()
  732. {
  733.     if (bufpos != 0) {
  734.         DWORD written;
  735.  
  736.         CHECK(WriteConsole(conout, displaybuf, bufpos, &written, NULL));
  737.         bufpos = 0;
  738.     }
  739. }
  740.  
  741. void
  742. clr_eoln()
  743. {
  744.     DWORD written;
  745.  
  746.     CHECK(FillConsoleOutputCharacter(conout, ' ', CO-c_col, curpos, &written));
  747.     CHECK(FillConsoleOutputAttribute(conout, c_attr, CO-c_col, curpos, &written));
  748. }
  749.  
  750. void
  751. dobell(n)    /* declared in term.h */
  752. int    n;
  753. {
  754.     MessageBeep(n);
  755. }
  756.  
  757. void
  758. ResizeWindow()
  759. {
  760.     /* Must update window size to eliminate those ugly scroll bars */
  761.     SMALL_RECT newsize;
  762.  
  763.     newsize.Top = newsize.Left = 0;
  764.     newsize.Right = maxpos.X-1;
  765.     newsize.Bottom = maxpos.Y-1;
  766.     /* Note: the following fails with "invalid address". If you can figure out why, please fix it. */
  767.     SetConsoleWindowInfo(conout, TRUE, &newsize);
  768. }
  769.  
  770. /* scr_putchar: put char on screen.  Declared in fp.h */
  771.  
  772. /* char is subject to default argument promotions */
  773. void
  774. scr_putchar(char c)
  775. {
  776.     if (bufpos >= sizeof(displaybuf))
  777.         flushscreen();
  778.     displaybuf[bufpos++] = c;
  779.     switch (c) {
  780.     case LF:
  781.         c_row += 1;
  782.         break;
  783.     case CR:
  784.         c_col = 0;
  785.         break;
  786.     case BS:
  787.         if (c_col > 0)
  788.             c_col -= 1;
  789.         break;
  790.     case CTL('G'):    /* ??? is this ever used? */
  791.         dobell(1);
  792.         break;
  793.     default:
  794.         if (++c_col > CO-1) {
  795.             c_col = 0;
  796.             c_row += 1;
  797.         }
  798.         break;
  799.     }
  800. }
  801.  
  802. /* No cursor optimization on an WIN32, this simplifies things a lot.
  803.  * Think about it: it would be silly!
  804.  */
  805.  
  806. void
  807. Placur(line, col)
  808. int    line,
  809.     col;
  810. {
  811.     flushscreen();
  812.     cur_mov(line, col);
  813.     CapCol = col;
  814.     CapLine = line;
  815. }
  816.  
  817. private bool
  818.     doing_so = NO,
  819.     doing_us = NO;
  820.  
  821. private void
  822. doattr()
  823. {
  824.     flushscreen();
  825.     setcolor((doing_so? Mlattr : Txattr) ^ (doing_us? Hlattr : 0));
  826. }
  827.  
  828. void
  829. SO_effect(f)
  830. bool f;
  831. {
  832.     doing_so = f;
  833.     doattr();
  834. }
  835.  
  836. void
  837. US_effect(f)
  838. bool    f;
  839. {
  840.     doing_us = f;
  841.     doattr();
  842. }
  843.  
  844. int
  845. FatalErrorMessage(char* str)
  846. {
  847.     if (MessageBox(NULL, str, NULL, MB_YESNO) == IDYES) {
  848.         return 'y';
  849.     } else {
  850.         return 'n';
  851.     }
  852. }
  853.  
  854. #endif /* WIN32 */
  855.