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