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
getTERM()34 getTERM()
35 {
36 }
37
38 void
ttysetattr(n)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
ttsize()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 *
formatChar(char c)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 *
formatEvent(INPUT_RECORD * event)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
MapKeyEventToChars(KEY_EVENT_RECORD * key,char * bptr)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
getInputEvents(char * bp,int size)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
inputEventWaiting(int period)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
SaveBufferFile(Buffer * b)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
MessageCloseFiles()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
ctrlHandler(DWORD type)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
freeReason(void)616 freeReason(void)
617 {
618 if (reason) {
619 VirtualFree((PVOID)reason, 0, MEM_RELEASE);
620 reason = NULL;
621 }
622 }
623
624 char *
getLastErrorString()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
ConsoleFail(char * fdef)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
scr_win(int no,int ulr,int ulc,int lrr,int lrc)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
i_lines(top,bottom,num)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
d_lines(top,bottom,num)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
clr_page()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
flushscreen()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
clr_eoln()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
dobell(n)751 dobell(n) /* declared in term.h */
752 int n;
753 {
754 MessageBeep(n);
755 }
756
757 void
ResizeWindow()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
scr_putchar(char c)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
Placur(line,col)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
doattr()822 doattr()
823 {
824 flushscreen();
825 setcolor((doing_so? Mlattr : Txattr) ^ (doing_us? Hlattr : 0));
826 }
827
828 void
SO_effect(f)829 SO_effect(f)
830 bool f;
831 {
832 doing_so = f;
833 doattr();
834 }
835
836 void
US_effect(f)837 US_effect(f)
838 bool f;
839 {
840 doing_us = f;
841 doattr();
842 }
843
844 int
FatalErrorMessage(char * str)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