1 /* pcterm.c -- How to handle the PC terminal for Info under MS-DOS/MS-Windows.
2
3 Copyright 1998-2023 Free Software Foundation, Inc.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17
18
19 /* WARNING WARNING WARNING!!! This probably won't work as is with
20 anything but DJGPP and MinGW! However, Borland should come close,
21 and other PC compilers will need minor modifications. */
22
23 #ifdef __MSDOS__
24 /* intl/libintl.h defines a macro `gettext' which
25 conflicts with conio.h header. */
26 #ifdef gettext
27 # undef gettext
28 # define gettext _gettext
29 #endif
30
31 #include <pc.h>
32 #include <keys.h>
33 #include <conio.h>
34 #endif
35
36 #ifdef _WIN32
37 #include <io.h>
38 #include <conio.h>
39 #include <process.h>
40 #include <malloc.h> /* for alloca */
41 #define WIN32_LEAN_AND_MEAN
42 #include <windows.h>
43
44 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
45 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 4
46 #endif
47 #ifndef COMMON_LVB_UNDERSCORE
48 #define COMMON_LVB_UNDERSCORE 0x8000
49 #endif
50
51 struct text_info {
52 WORD normattr;
53 WORD attribute;
54 SHORT winleft;
55 SHORT wintop;
56 SHORT winright;
57 SHORT winbottom;
58 SHORT screenheight;
59 SHORT screenwidth;
60 SHORT curx;
61 SHORT cury;
62 COORD bufsize;
63 unsigned char currmode; /* unused and unsupported for Windows */
64 };
65
66 struct termios {
67 int dummy;
68 };
69
70 enum text_modes { LASTMODE=-1 };
71
72 #define cprintf _cprintf
73 #define cputs _cputs
74
75 #undef read
76 #undef _read
77
78 #include "display.h"
79
80 void reset_info_window_sizes (void);
81 void redisplay_after_signal (void);
82
83 #endif
84
85 #include "variables.h"
86 #include "session.h"
87 #include "terminal.h"
88
89 extern int speech_friendly; /* defined in info.c */
90
91 /* **************************************************************** */
92 /* */
93 /* PC Terminal Output Functions */
94 /* */
95 /* **************************************************************** */
96
97 static struct text_info outside_info; /* holds screen params outside Info */
98 #ifdef _WIN32
99 static WORD norm_attr, inv_attr, xref_attr;
100 static WORD current_attr;
101 static HANDLE hstdin = INVALID_HANDLE_VALUE;
102 static HANDLE hstdout = INVALID_HANDLE_VALUE;
103 static HANDLE hinfo = INVALID_HANDLE_VALUE;
104 static HANDLE hscreen = INVALID_HANDLE_VALUE;
105 static DWORD old_inpmode;
106 static DWORD old_outpmode;
107 static UINT output_cp;
108 #else
109 static unsigned char norm_attr, inv_attr, xref_attr;
110 #endif
111
112 static unsigned const char * find_sequence (int);
113
114 #ifdef _WIN32
115
116 /* Windows-specific initialization and de-initialization. */
117 void
118 w32_info_prep (void)
119 {
120 if (hinfo != INVALID_HANDLE_VALUE)
121 {
122 DWORD new_mode;
123
124 SetConsoleActiveScreenBuffer (hinfo);
125 current_attr = norm_attr;
126 hscreen = hinfo;
127 SetConsoleMode (hstdin, ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
128 GetConsoleMode (hscreen, &old_outpmode);
129 new_mode = old_outpmode & ~ENABLE_WRAP_AT_EOL_OUTPUT;
130 SetConsoleMode (hscreen, new_mode);
131 /* Enable underline, if available. */
132 SetConsoleMode (hscreen, new_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
133 }
134 }
135
136 void
137 w32_info_unprep (void)
138 {
139 if (hinfo != INVALID_HANDLE_VALUE)
140 {
141 SetConsoleActiveScreenBuffer (hstdout);
142 current_attr = outside_info.normattr;
143 hscreen = hstdout;
144 SetConsoleMode (hstdin, old_inpmode);
145 }
146 }
147
148 void
149 w32_cleanup (void)
150 {
151 if (hinfo != INVALID_HANDLE_VALUE)
152 {
153 COORD cursor_pos;
154
155 /* Restore the original position of the cursor. */
156 cursor_pos.X = outside_info.curx;
157 cursor_pos.Y = outside_info.cury;
158 SetConsoleCursorPosition (hstdout, cursor_pos);
159
160 /* Close the input handle we created. */
161 CloseHandle (hinfo);
162 }
163 }
164
165 static void w32_info_init (void) __attribute__((constructor));
166 static void pc_initialize_terminal (char *);
167
168 static void
169 w32_info_init (void)
170 {
171 /* We need to set this single hook here; the rest
172 will be set by pc_initialize_terminal when it is called. */
173 terminal_initialize_terminal_hook = pc_initialize_terminal;
174 }
175
176 /* Emulate DJGPP conio functions for Windows. */
177 static void
178 gettextinfo (struct text_info *ti)
179 {
180 CONSOLE_SCREEN_BUFFER_INFO csbi;
181 static TCHAR errbuf[500];
182 DWORD ignored;
183
184 hstdin = GetStdHandle (STD_INPUT_HANDLE);
185 hstdout = GetStdHandle (STD_OUTPUT_HANDLE);
186
187 if (!GetConsoleMode (hstdin, &ignored))
188 hstdin = INVALID_HANDLE_VALUE;
189
190 if (hstdout != INVALID_HANDLE_VALUE
191 && GetConsoleMode (hstdout, &ignored))
192 {
193 hinfo = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
194 FILE_SHARE_READ | FILE_SHARE_WRITE,
195 NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
196 if (hinfo != INVALID_HANDLE_VALUE
197 && GetConsoleScreenBufferInfo (hstdout, &csbi))
198 {
199 ti->normattr = csbi.wAttributes;
200 ti->winleft = 1;
201 ti->wintop = 1;
202 ti->winright = csbi.srWindow.Right + 1;
203 ti->winbottom = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
204 ti->attribute = csbi.wAttributes;
205 ti->screenheight = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
206 ti->screenwidth = csbi.srWindow.Right - csbi.srWindow.Left + 1;
207 ti->curx = csbi.dwCursorPosition.X;
208 ti->cury = csbi.dwCursorPosition.Y;
209 ti->bufsize = csbi.dwSize;
210
211 atexit (w32_cleanup);
212 }
213 else
214 {
215 DWORD error_no = GetLastError ();
216
217 if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL,
218 error_no,
219 0, /* choose most suitable language */
220 errbuf, sizeof (errbuf), NULL))
221 sprintf (errbuf, "w32 error %u", error_no);
222 CloseHandle (hinfo);
223 info_error (_("Terminal cannot be initialized: %s\n"), errbuf);
224 exit (1);
225 }
226 }
227 else
228 {
229 /* We were invoked non-interactively. Do the minimum we must. */
230 ti->screenheight = 24;
231 ti->screenwidth = 80;
232 }
233 }
234
235 void
236 textattr (int attr)
237 {
238 if (hscreen != INVALID_HANDLE_VALUE)
239 SetConsoleTextAttribute (hscreen, attr);
240 }
241
242 void
243 textmode (int mode)
244 {
245 /* Nothing. */
246 }
247
248 void
249 highvideo (void)
250 {
251 int attr;
252 CONSOLE_SCREEN_BUFFER_INFO csbi;
253
254 GetConsoleScreenBufferInfo (hscreen, &csbi);
255 attr = csbi.wAttributes | FOREGROUND_INTENSITY;
256 attr ^= norm_attr & FOREGROUND_INTENSITY;
257 textattr (attr);
258 }
259
260 void
261 normvideo (void)
262 {
263 int attr;
264 CONSOLE_SCREEN_BUFFER_INFO csbi;
265
266 GetConsoleScreenBufferInfo (hscreen, &csbi);
267 attr = csbi.wAttributes & ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY
268 | COMMON_LVB_UNDERSCORE);
269 attr |= norm_attr & (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
270 textattr (attr);
271 }
272
273 void
274 blinkvideo (void)
275 {
276 int attr;
277 CONSOLE_SCREEN_BUFFER_INFO csbi;
278
279 GetConsoleScreenBufferInfo (hscreen, &csbi);
280 attr = csbi.wAttributes | BACKGROUND_INTENSITY;
281 attr ^= norm_attr & BACKGROUND_INTENSITY;
282 textattr (attr);
283 }
284
285 void
286 underline (void)
287 {
288 int attr;
289 CONSOLE_SCREEN_BUFFER_INFO csbi;
290
291 GetConsoleScreenBufferInfo (hscreen, &csbi);
292 attr = csbi.wAttributes | COMMON_LVB_UNDERSCORE;
293 textattr (attr);
294 }
295
296 void
297 textcolor (int color)
298 {
299 int attr;
300 CONSOLE_SCREEN_BUFFER_INFO csbi;
301
302 GetConsoleScreenBufferInfo (hscreen, &csbi);
303 attr = (csbi.wAttributes & (COMMON_LVB_UNDERSCORE | 0xf0)) | (color & 0x0f);
304 textattr (attr);
305 }
306
307 void
308 textbackground (int color)
309 {
310 int attr;
311 CONSOLE_SCREEN_BUFFER_INFO csbi;
312
313 GetConsoleScreenBufferInfo (hscreen, &csbi);
314 attr = (csbi.wAttributes & (COMMON_LVB_UNDERSCORE | 0x0f)) | ((color & 0x0f) << 4);
315 textattr (attr);
316 }
317
318 void
319 ScreenGetCursor (int *row, int *col)
320 {
321 CONSOLE_SCREEN_BUFFER_INFO csbi;
322
323 if (hscreen == INVALID_HANDLE_VALUE)
324 *row = *col = 0;
325 else
326 {
327 GetConsoleScreenBufferInfo (hscreen, &csbi);
328 *row = csbi.dwCursorPosition.Y;
329 *col = csbi.dwCursorPosition.X;
330 }
331 }
332
333 void
334 ScreenSetCursor (int row, int col)
335 {
336 if (hscreen != INVALID_HANDLE_VALUE)
337 {
338 COORD cursor_pos;
339
340 cursor_pos.X = col;
341 cursor_pos.Y = row;
342
343 SetConsoleCursorPosition (hscreen, cursor_pos);
344 }
345 }
346
347 void
348 ScreenClear (void)
349 {
350 if (hscreen != INVALID_HANDLE_VALUE)
351 {
352 DWORD nchars = screenwidth * screenheight;
353 COORD start_pos;
354 DWORD written;
355
356 start_pos.X = start_pos.Y = 0;
357 FillConsoleOutputAttribute (hscreen, norm_attr, nchars, start_pos,
358 &written);
359 FillConsoleOutputCharacter (hscreen, ' ', nchars, start_pos, &written);
360 }
361 }
362
363 void
364 clreol (void)
365 {
366 if (hscreen != INVALID_HANDLE_VALUE)
367 {
368 DWORD nchars;
369 COORD start_pos;
370 DWORD written;
371 CONSOLE_SCREEN_BUFFER_INFO csbi;
372
373 GetConsoleScreenBufferInfo (hscreen, &csbi);
374 start_pos = csbi.dwCursorPosition;
375 nchars = csbi.dwSize.X - start_pos.X;
376
377 FillConsoleOutputAttribute (hscreen, current_attr, nchars, start_pos,
378 &written);
379 FillConsoleOutputCharacter (hscreen, ' ', nchars, start_pos, &written);
380 }
381 }
382
383 void
384 ScreenVisualBell (void)
385 {
386 if (hscreen != INVALID_HANDLE_VALUE)
387 {
388 DWORD nchars = screenwidth * screenheight;
389 COORD start_pos;
390 DWORD written;
391 PWORD attr;
392 DWORD i;
393
394 start_pos.X = start_pos.Y = 0;
395 attr = xmalloc (nchars * sizeof (WORD));
396 ReadConsoleOutputAttribute (hscreen, attr, nchars, start_pos, &written);
397 for (i = 0; i < nchars; ++i)
398 attr[i] ^= norm_attr ^ inv_attr;
399 WriteConsoleOutputAttribute (hscreen, attr, nchars, start_pos, &written);
400 Sleep (50);
401 for (i = 0; i < nchars; ++i)
402 attr[i] ^= norm_attr ^ inv_attr;
403 WriteConsoleOutputAttribute (hscreen, attr, nchars, start_pos, &written);
404 free (attr);
405 }
406 else
407 {
408 printf ("%c", '\a');
409 fflush (stdout);
410 }
411 }
412
413 int
414 movetext(int left, int top, int right, int bottom, int destleft, int desttop)
415 {
416 if (hscreen != INVALID_HANDLE_VALUE)
417 {
418 SMALL_RECT src;
419 COORD dest;
420 CHAR_INFO fill;
421
422 src.Left = left - 1;
423 src.Top = top - 1;
424 src.Right = right - 1;
425 src.Bottom = bottom - 1;
426
427 dest.X = destleft - 1;
428 dest.Y = desttop - 1;
429
430 fill.Attributes = norm_attr;
431 fill.Char.AsciiChar = (CHAR)' ';
432
433 return ScrollConsoleScreenBuffer (hscreen, &src , NULL, dest, &fill) != 0;
434 }
435 else
436 return 0;
437 }
438
439 int
440 ScreenRows (void)
441 {
442 if (hscreen != INVALID_HANDLE_VALUE)
443 {
444 CONSOLE_SCREEN_BUFFER_INFO csbi;
445
446 GetConsoleScreenBufferInfo (hscreen, &csbi);
447 return csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
448 }
449 else
450 return 24;
451 }
452
453 int
454 ScreenCols (void)
455 {
456 if (hscreen != INVALID_HANDLE_VALUE)
457 {
458 CONSOLE_SCREEN_BUFFER_INFO csbi;
459
460 GetConsoleScreenBufferInfo (hscreen, &csbi);
461 return csbi.srWindow.Right - csbi.srWindow.Left + 1;
462 }
463 else
464 return 80;
465 }
466
467 void
468 _set_screen_lines (int lines)
469 {
470 if (hscreen != INVALID_HANDLE_VALUE)
471 {
472 SMALL_RECT window_rectangle;
473 CONSOLE_SCREEN_BUFFER_INFO csbi;
474 COORD scrbufsize;
475
476 GetConsoleScreenBufferInfo (hscreen, &csbi);
477
478 window_rectangle = csbi.srWindow;
479 window_rectangle.Bottom = window_rectangle.Top + lines - 1;
480 SetConsoleWindowInfo (hscreen, TRUE, &window_rectangle);
481
482 /* Set the screen buffer size to the same dimensions as the window,
483 so that the dysfunctional scroll bar disappears. */
484 scrbufsize.X = window_rectangle.Right - window_rectangle.Left + 1;
485 scrbufsize.Y = window_rectangle.Bottom - window_rectangle.Top + 1;
486 SetConsoleScreenBufferSize (hscreen, scrbufsize);
487 }
488 }
489
490 void
491 w32_set_screen_dimensions (int cols, int rows)
492 {
493 if (hscreen != INVALID_HANDLE_VALUE)
494 {
495 SMALL_RECT window_rectangle;
496 CONSOLE_SCREEN_BUFFER_INFO csbi;
497
498 GetConsoleScreenBufferInfo (hscreen, &csbi);
499
500 window_rectangle = csbi.srWindow;
501 window_rectangle.Bottom = window_rectangle.Top + rows - 1;
502 window_rectangle.Right = window_rectangle.Left + cols - 1;
503 SetConsoleWindowInfo (hscreen, TRUE, &window_rectangle);
504 }
505 }
506
507 /* Emulate `sleep'. */
508 unsigned
509 sleep (unsigned sec)
510 {
511 Sleep (sec*1000);
512 return 0;
513 }
514
515 /* Keyboard input support. */
516
517 static int
518 w32_our_tty (int fd)
519 {
520 /* Is this our tty? */
521 return hstdin != INVALID_HANDLE_VALUE
522 && hstdin == (HANDLE)_get_osfhandle (fd);
523 }
524
525 /* Translate a Windows key event into the equivalent sequence of bytes
526 to be submitted to Info dispatcher. */
527 #define define_seq(p,s1,s2) \
528 do { \
529 if ((ctl & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0) \
530 memcpy (p, s1, sizeof (s1)), p += sizeof (s1) - 1; \
531 else \
532 memcpy (p, s2, sizeof (s2)), p += sizeof (s2) - 1; \
533 } while (0)
534
535 static int
536 w32keyseq (unsigned char ascii_ch, WORD vkey, DWORD ctl, unsigned char *seq)
537 {
538 unsigned char *p = seq;
539
540 switch (ascii_ch)
541 {
542 case '\0':
543 /* Keys with no ASCII code are extended keys, like arrows. */
544 switch (vkey)
545 {
546 case VK_PRIOR:
547 define_seq (p, "\033\061p", "\033v");
548 break;
549 case VK_NEXT:
550 define_seq (p, "\033\061n", "\026");
551 break;
552 case VK_END:
553 define_seq (p, "\033>", "\033>");
554 break;
555 case VK_HOME:
556 define_seq (p, "\033<", "\033<");
557 break;
558 case VK_LEFT:
559 define_seq (p, "\033b", "\033[D");
560 break;
561 case VK_UP:
562 define_seq (p, "\033\061u", "\033[A");
563 break;
564 case VK_RIGHT:
565 define_seq (p, "\033f", "\033[C");
566 break;
567 case VK_DOWN:
568 define_seq (p, "\033\061m", "\033[B");
569 break;
570 case VK_INSERT:
571 define_seq (p, "\033[L", "\033[L");
572 break;
573 case VK_DELETE: /* Delete => Ctrl-d, Alt-Delete => ESC d */
574 if ((ctl & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) != 0)
575 define_seq (p, "\033d", "\033d");
576 else
577 define_seq (p, "\033d", "\004");
578 break;
579 case VK_HELP: /* F1 => Ctrl-h */
580 case VK_F1:
581 *p++ = '\010';
582 break;
583 case 50: /* Ctrl-@ => '\0' */
584 if ((ctl & SHIFT_PRESSED) != 0)
585 *p++ = '\0';
586 break;
587 default:
588 if (0x41 <= vkey && vkey <= 0x5a)
589 {
590 /* Alt-Ctrl-a, Alt-Ctrl-b, etc. */
591 *p++ = '\033';
592 *p++ = '\001' + vkey - 0x41;
593 }
594 }
595 break;
596 case ' ': /* Ctrl-SPC => '\0' */
597 if ((ctl & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0)
598 ascii_ch = '\0';
599 *p++ = ascii_ch;
600 break;
601 case '\t': /* Shift-TAB/Alt-TAB => Esc-TAB */
602 if ((ctl & (SHIFT_PRESSED | LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) != 0)
603 {
604 memcpy (p, "\033\011", sizeof ("\033\011"));
605 p += sizeof ("\033\011") - 1;
606 }
607 else
608 *p++ = '\t';
609 break;
610 case '\b':
611 /* Backspace => DEL. */
612 ascii_ch = '\177';
613 /* FALLTHROUGH */
614 default:
615 if ((ctl & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) != 0)
616 *p++ = '\033';
617 *p++ = ascii_ch;
618 break;
619 }
620 return p - seq;
621 }
622
623 static unsigned char buffered_chars[512];
624 static size_t buf_head;
625 static size_t buf_tail;
626
627 static ssize_t
628 w32_kbd_read (unsigned char *inbuf, size_t n)
629 {
630 DWORD nevents, nread;
631 INPUT_RECORD inrec;
632 ssize_t nret = 0;
633
634 do {
635
636 /* Stuff any unread buffered characters. */
637 while (buf_head < buf_tail && n > 0)
638 {
639 *inbuf++ = buffered_chars[buf_head++];
640 nret++;
641 n--;
642 }
643 if (n <= 0)
644 break;
645
646 /* Wait for input. */
647 while (GetNumberOfConsoleInputEvents (hstdin, &nevents)
648 && nevents < 1)
649 Sleep (20);
650
651 while (nevents-- && n > 0)
652 {
653 if (!ReadConsoleInput (hstdin, &inrec, 1, &nread))
654 return -1;
655
656 if (nread > 0)
657 {
658 switch (inrec.EventType)
659 {
660 case KEY_EVENT:
661 if (inrec.Event.KeyEvent.bKeyDown == TRUE
662 && !(inrec.Event.KeyEvent.wVirtualScanCode == 0
663 || inrec.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT
664 || inrec.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL
665 || inrec.Event.KeyEvent.wVirtualKeyCode == VK_MENU))
666 {
667 unsigned char keyseq[10];
668 int count = inrec.Event.KeyEvent.wRepeatCount;
669 unsigned char ch = inrec.Event.KeyEvent.uChar.AsciiChar;
670 WORD vkey = inrec.Event.KeyEvent.wVirtualKeyCode;
671 DWORD ctl_state = inrec.Event.KeyEvent.dwControlKeyState;
672 int nbytes = w32keyseq (ch, vkey, ctl_state, keyseq);
673
674 /* Supply up to N characters to the caller. */
675 while (count && n >= nbytes)
676 {
677 if (nbytes == 1 && keyseq[0] == '\032')
678 {
679 terminal_goto_xy (0, screenheight - 1);
680 terminal_clear_to_eol ();
681 fflush (stdout);
682 terminal_unprep_terminal ();
683 kill (getpid (), 0);
684 terminal_prep_terminal ();
685 reset_info_window_sizes ();
686 }
687 else
688 {
689 memcpy (&inbuf[nret], keyseq, nbytes);
690 nret += nbytes;
691 n -= nbytes;
692 }
693 count--;
694 }
695 /* Buffer the rest. */
696 if (count > 0)
697 {
698 buf_head = buf_tail = 0;
699 while (count--
700 && buf_tail < sizeof(buffered_chars) - nbytes)
701 {
702 memcpy (&buffered_chars[buf_tail], keyseq, nbytes);
703 buf_tail += nbytes;
704 }
705 }
706 }
707 break;
708 case WINDOW_BUFFER_SIZE_EVENT:
709 {
710 int rows, cols;
711
712 /* Note: this event is _supposed_ to be sent only
713 when the console window's _screen_buffer_ size
714 is changed via the Properties->Layout dialog.
715 However, Windows 10 seems to send it even when
716 the properties are not changed. */
717 cols = inrec.Event.WindowBufferSizeEvent.dwSize.X;
718 rows = inrec.Event.WindowBufferSizeEvent.dwSize.Y;
719 /* Avoid needless screen redraws, they produce
720 annoying flickering. */
721 if (cols != screenwidth || rows != screenheight)
722 {
723 screenwidth = cols;
724 screenheight = rows;
725 w32_set_screen_dimensions (cols, rows);
726 display_initialize_display (screenwidth, screenheight);
727 window_new_screen_size (screenwidth, screenheight);
728 redisplay_after_signal ();
729 }
730 }
731 break;
732 case MOUSE_EVENT:
733 {
734 /* Only vertical wheel support for now. */
735 int wheeled =
736 (inrec.Event.MouseEvent.dwEventFlags & MOUSE_WHEELED) != 0;
737 if (wheeled && mouse_protocol == MP_NORMAL_TRACKING)
738 {
739 extern void info_up_line (WINDOW *, int count);
740 extern void info_down_line (WINDOW *, int count);
741 extern WINDOW *active_window;
742
743 int hiword =
744 HIWORD (inrec.Event.MouseEvent.dwButtonState);
745
746 if ((hiword & 0xFF00) == 0)
747 info_up_line (active_window, 3);
748 else
749 info_down_line (active_window, 3);
750 display_update_display ();
751 }
752 }
753 break;
754 default:
755 break;
756 }
757 }
758 }
759 } while (n > 0);
760 return nret;
761 }
762
763 long
764 w32_chars_avail (int fd)
765 {
766 if (w32_our_tty (fd))
767 return buf_tail - buf_head;
768 else
769 {
770 struct stat st;
771
772 if (fstat (fd, &st) < 0)
773 return 1;
774 else
775 return st.st_size;
776 }
777 }
778
779 ssize_t
780 w32_read (int fd, void *buf, size_t n)
781 {
782 if (w32_our_tty (fd))
783 return w32_kbd_read (buf, n);
784 else
785 return _read (fd, buf, n);
786 }
787
788 /* Write to the console a string of text encoded in UTF-8 or UTF-7. */
789 static void
790 write_utf (DWORD cp, const char *text, int nbytes)
791 {
792 /* MSDN says UTF-7 requires zero in flags. */
793 DWORD flags = (cp == CP_UTF7) ? 0 : MB_ERR_INVALID_CHARS;
794 /* How much space do we need for wide characters? */
795 int wlen = MultiByteToWideChar (cp, flags, text, nbytes, NULL, 0);
796
797 if (wlen)
798 {
799 WCHAR *text_w = alloca (wlen * sizeof (WCHAR));
800 DWORD written;
801
802 if (MultiByteToWideChar (cp, flags, text, nbytes, text_w, wlen) > 0)
803 {
804 WriteConsoleW (hscreen, text_w, (nbytes < 0) ? wlen - 1 : wlen,
805 &written, NULL);
806 return;
807 }
808 }
809 /* Fall back on conio. */
810 if (nbytes < 0)
811 cputs (text);
812 else
813 cprintf ("%.*s", nbytes, text);
814 }
815
816 /* A replacement for nl_langinfo which does a more accurate job for
817 the console output codeset. Windows can use 3 different encodings
818 at the same time, and the Posix-compliant nl_langinfo simply
819 doesn't know enough to decide which one is needed when CODESET is
820 requested. */
821 #undef nl_langinfo
822 #include <langinfo.h>
823
824 char *
825 rpl_nl_langinfo (nl_item item)
826 {
827 if (item == CODESET)
828 {
829 static char buf[100];
830
831 /* We need all the help we can get from GNU libiconv, so we
832 request transliteration as well. */
833 sprintf (buf, "CP%u//TRANSLIT", GetConsoleOutputCP ());
834 return buf;
835 }
836 else
837 return nl_langinfo (item);
838 }
839
840 #ifndef HAVE_WCWIDTH
841 /* A replacement for wcwidth. The Gnulib version calls setlocale for
842 every character Info is about to display, which makes display of
843 large nodes annoyingly slow.
844
845 Note that the Gnulib version is still compiled and put into
846 libgnu.a, because the configure script doesn't know about this
847 replacement. But the linker will not pull the Gnulib version into
848 the binary, because it resolves the calls to this replacement
849 function. */
850 int
851 wcwidth (wchar_t wc)
852 {
853 return wc == 0 ? 0 : iswprint (wc) ? 1 : -1;
854 }
855 #endif
856
857 #endif /* _WIN32 */
858
859 /* Turn on reverse video. */
860 static void
861 pc_begin_inverse (void)
862 {
863 textattr (inv_attr);
864 }
865
866 /* Turn off reverse video. */
867 static void
868 pc_end_inverse (void)
869 {
870 textattr (norm_attr);
871 }
872
873 /* The implementation of the underlined text. The DOS/Windows console
874 doesn't support underlined text (until Win10), so we make it blue instead
875 (blue, because this face is used for hyperlinks). */
876 static void
877 pc_begin_underline (void)
878 {
879 if (xref_attr != COMMON_LVB_UNDERSCORE)
880 textattr (xref_attr);
881 else
882 underline ();
883 }
884
885 static void
886 pc_end_underline (void)
887 {
888 if (xref_attr != COMMON_LVB_UNDERSCORE)
889 textattr (norm_attr);
890 else
891 normvideo ();
892 }
893
894 /* Standout (a.k.a. "high video") text. */
895 static void
896 pc_begin_standout (void)
897 {
898 highvideo ();
899 }
900
901 static void
902 pc_end_standout (void)
903 {
904 normvideo ();
905 }
906
907 static void
908 pc_begin_blink (void)
909 {
910 blinkvideo ();
911 }
912
913 static void
914 pc_default_color (void)
915 {
916 textattr (norm_attr);
917 }
918
919 /* Info definitions of 8 colors (see terminal.h) are in an order
920 that's different from Windows/DOS console colors. This function
921 unscrambles the order, and also maps 8 standard ANSI colors to the
922 low-intensity shades of the 16 PC colors, so that "standout" works
923 by turning the intensity bit. */
924 static int
925 convert_color (int terminal_color)
926 {
927 /* The terminal.h order is:
928 black, red, green, yellow, blue, magenta, cyan, white. */
929 static int pc_color_map[] = {
930 0, 4, 2, 6, 1, 5, 3, 7
931 };
932 int intensity = terminal_color & (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
933 terminal_color &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
934
935 if (terminal_color >= 0
936 && terminal_color < sizeof(pc_color_map) / sizeof (pc_color_map[0]))
937 return pc_color_map[terminal_color] | intensity;
938 return 7; /* lightgray */
939 }
940
941 static void
942 pc_set_fg_color (int color)
943 {
944 textcolor (convert_color (color) | (norm_attr & FOREGROUND_INTENSITY));
945 }
946
947 static void
948 pc_set_bg_color (int color)
949 {
950 textbackground (convert_color (color) | (norm_attr & BACKGROUND_INTENSITY));
951 }
952
953 #ifdef MAX
954 #undef MAX
955 #endif
956 #define MAX(a,b) ((a) > (b) ? (a) : (b))
957 #ifdef MIN
958 #undef MIN
959 #endif
960 #define MIN(a,b) ((a) < (b) ? (a) : (b))
961
962 /* Move the cursor up one line. */
963 static void
964 pc_up_line (void)
965 {
966 int x, y;
967 ScreenGetCursor (&y, &x);
968 ScreenSetCursor (MAX (y-1, 0), x);
969 }
970
971 /* Move the cursor down one line. */
972 static void
973 pc_down_line (void)
974 {
975 int x, y;
976 ScreenGetCursor (&y, &x);
977 ScreenSetCursor (MIN (screenheight-1, y+1), x);
978 }
979
980 /* Clear the entire terminal screen. */
981 static void
982 pc_clear_screen (void)
983 {
984 ScreenClear ();
985 }
986
987 /* Clear from the current position of the cursor to the end of the line. */
988 static void
989 pc_clear_to_eol (void)
990 {
991 clreol (); /* perhaps to be replaced by a loop */
992 }
993
994 /* Set the global variables SCREENWIDTH and SCREENHEIGHT. */
995 static void
996 pc_get_screen_size(void)
997 {
998 /* Current screen dimensions are the default. */
999 if (!outside_info.screenheight) /* paranoia */
1000 gettextinfo (&outside_info);
1001 screenwidth = outside_info.screenwidth;
1002 screenheight = outside_info.screenheight;
1003
1004 /* Environment variable "LINES" overrides the default. */
1005 if (getenv ("LINES") != NULL)
1006 screenheight = atoi (getenv ("LINES"));
1007
1008 /* Environment variable "INFO_LINES" overrides "LINES". */
1009 if (getenv ("INFO_LINES") != NULL)
1010 screenheight = atoi (getenv ("INFO_LINES"));
1011 }
1012
1013 /* Move the cursor to the terminal location of X and Y. */
1014 static void
1015 pc_goto_xy (x, y)
1016 int x, y;
1017 {
1018 ScreenSetCursor (y, x); /* yes, pc.h says ScreenSetCursor (row, column) !! */
1019 }
1020
1021 /* Print STRING to the terminal at the current position. */
1022 static void
1023 pc_put_text (string)
1024 char *string;
1025 {
1026 if (speech_friendly)
1027 fputs (string, stdout);
1028 #ifdef __MINGW32__
1029 else if (hscreen == INVALID_HANDLE_VALUE)
1030 fputs (string, stdout);
1031 else if (output_cp == CP_UTF8 || output_cp == CP_UTF7)
1032 write_utf (output_cp, string, -1);
1033 #endif
1034 else
1035 cputs (string);
1036 }
1037
1038 /* Ring the terminal bell. The bell is rung visibly if the terminal is
1039 capable of doing that, and if terminal_use_visible_bell_p is non-zero. */
1040 static void
1041 pc_ring_bell(void)
1042 {
1043 if (terminal_has_visible_bell_p && terminal_use_visible_bell_p)
1044 ScreenVisualBell ();
1045 else
1046 {
1047 printf ("%c",'\a');
1048 fflush (stdout);
1049 }
1050 }
1051
1052 /* Print NCHARS from STRING to the terminal at the current position. */
1053 static void
1054 pc_write_chars (string, nchars)
1055 char *string;
1056 int nchars;
1057 {
1058 if (!nchars)
1059 return;
1060
1061 if (speech_friendly)
1062 printf ("%.*s", nchars, string);
1063 #ifdef __MINGW32__
1064 else if (hscreen == INVALID_HANDLE_VALUE)
1065 printf ("%.*s", nchars, string);
1066 else if (output_cp == CP_UTF8 || output_cp == CP_UTF7)
1067 write_utf (output_cp, string, nchars);
1068 #endif
1069 else
1070 cprintf ("%.*s", nchars, string);
1071 }
1072
1073 /* Scroll an area of the terminal from START to (and excluding) END,
1074 AMOUNT lines. If AMOUNT is negative, the lines are scrolled
1075 towards the top of the screen, else they are scrolled towards the
1076 bottom of the screen. The lines of the old region which do not
1077 overlap the new region are cleared, to mimic terminal operation. */
1078 static void
1079 pc_scroll_terminal (start, end, amount)
1080 int start, end, amount;
1081 {
1082 int line_to_clear = amount > 0 ? start : end + amount;
1083
1084 /* Move the text. Note that `movetext' expects 1-based coordinates. */
1085 movetext (1, start + 1, ScreenCols (), end, 1, start + amount + 1);
1086
1087 /* Now clear the lines which were left unoccupied. */
1088 if (amount < 0)
1089 amount = -amount;
1090 while (amount--)
1091 {
1092 ScreenSetCursor (line_to_clear++, 0);
1093 clreol ();
1094 }
1095 }
1096
1097 /* Put the screen in the video mode and colors which Info will use.
1098 Prepare to start using the terminal to read characters singly. */
1099 static void
1100 pc_prep_terminal (void)
1101 {
1102 int tty;
1103
1104 #ifdef _WIN32
1105 w32_info_prep ();
1106 #endif
1107
1108 /* Do not set screen height if we already have it, because
1109 doing so erases the screen. */
1110 if (screenheight != ScreenRows ())
1111 _set_screen_lines (screenheight);
1112
1113 /* Don't fail if they asked for screen dimensions that their
1114 hardware cannot support. */
1115 screenheight = ScreenRows ();
1116 screenwidth = ScreenCols ();
1117
1118 /* Try setting the colors user asked for. */
1119 textattr (norm_attr);
1120 ScreenClear ();
1121
1122 /* Switch console reads to binary mode. */
1123 tty = fileno (stdin);
1124 #ifdef __DJGPP__
1125 setmode (tty, O_BINARY);
1126 __djgpp_set_ctrl_c (1); /* re-enable SIGINT generation by Ctrl-C */
1127 #endif
1128 }
1129
1130 /* Restore the tty settings back to what they were before we started using
1131 this terminal. */
1132 static void
1133 pc_unprep_terminal (void)
1134 {
1135 int tty;
1136
1137 #ifdef _WIN32
1138 w32_info_unprep ();
1139 #endif
1140
1141 textattr (outside_info.normattr);
1142
1143 /* Do not set screen height if we already have it, because
1144 doing so erases the screen. */
1145 if (outside_info.screenheight != ScreenRows ())
1146 {
1147 _set_screen_lines (outside_info.screenheight);
1148 textmode (LASTMODE);
1149 }
1150 #ifdef __MSDOS__
1151 else
1152 pc_clear_to_eol (); /* for text attributes to really take effect */
1153 #endif
1154 #ifdef _WIN32
1155 if (hscreen != INVALID_HANDLE_VALUE)
1156 SetConsoleScreenBufferSize (hstdout, outside_info.bufsize);
1157 #endif
1158
1159 /* Switch back to text mode on stdin. */
1160 tty = fileno (stdin);
1161 #ifdef __DJGPP__
1162 setmode (tty, O_TEXT);
1163 #endif
1164 }
1165
1166 /* Initialize the terminal which is known as TERMINAL_NAME. If this
1167 terminal doesn't have cursor addressability, `terminal_is_dumb_p'
1168 becomes nonzero. The variables SCREENHEIGHT and SCREENWIDTH are set
1169 to the dimensions that this terminal actually has. Finally, the
1170 terminal screen is cleared. */
1171 static void
1172 pc_initialize_terminal (term_name)
1173 char *term_name;
1174 {
1175 char *info_colors;
1176
1177 if (!term_name)
1178 {
1179 term_name = getenv ("TERM");
1180 if (!term_name)
1181 #ifdef __MSDOS__
1182 term_name = "pc-dos"; /* ``what's in a name?'' */
1183 #endif
1184 #ifdef _WIN32
1185 term_name = "w32console";
1186 #endif
1187 }
1188
1189 /* Get current video information, to be restored later. */
1190 if (outside_info.screenwidth == 0)
1191 gettextinfo (&outside_info);
1192
1193 /* Current screen colors are the default. */
1194 norm_attr = outside_info.normattr;
1195 inv_attr = (((outside_info.normattr & 7) << 4) |
1196 ((outside_info.normattr & 0x7f) >> 4));
1197 #ifdef __MSDOS__
1198 xref_attr = CYAN;
1199 #endif
1200 #ifdef _WIN32
1201 xref_attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
1202 #endif
1203 xref_attr |= outside_info.normattr & 0xf0;
1204
1205 /* Does the user want non-default colors? */
1206 info_colors = getenv ("INFO_COLORS");
1207 if ((info_colors != (char *)0) && !speech_friendly)
1208 {
1209 /* Decode a color from a string descriptor.
1210 The descriptor string is a sequence of color specifiers separated
1211 by a non-numeric character. Each color specifier should represent
1212 a small integer which fits into an unsigned char, and can be given
1213 in any base supported by strtoul. Examples of valid descriptors:
1214
1215 "10 31"
1216 "0x13/0x45"
1217 "007.077"
1218
1219 The separator between two color specifiers can be any character which
1220 cannot be used in a printed representation of an integer number. */
1221 char *endp;
1222 unsigned long color_desc = strtoul (info_colors, &endp, 0);
1223
1224 if (color_desc <= UCHAR_MAX)
1225 {
1226 norm_attr = (unsigned char)color_desc;
1227 xref_attr = (xref_attr & 0x0f) | (norm_attr & 0xf0);
1228 color_desc = strtoul (endp + 1, &endp, 0);
1229 if (color_desc <= UCHAR_MAX)
1230 inv_attr = (unsigned char)color_desc;
1231 #ifdef _WIN32
1232 if (*endp == 'u')
1233 xref_attr = COMMON_LVB_UNDERSCORE;
1234 else
1235 #endif
1236 if (*endp != '\0')
1237 {
1238 color_desc = strtoul (endp + 1, &endp, 0);
1239 if (color_desc <= UCHAR_MAX)
1240 {
1241 #ifdef _WIN32
1242 if (*endp == 'u')
1243 color_desc |= COMMON_LVB_UNDERSCORE;
1244 xref_attr = (WORD)color_desc;
1245 #else
1246 xref_attr = (unsigned char)color_desc;
1247 #endif
1248 }
1249 }
1250 }
1251 }
1252
1253 /* We can scroll. */
1254 terminal_can_scroll = 1;
1255
1256 /* We know how to produce a visible bell, if somebody's looking... */
1257 if (!speech_friendly)
1258 terminal_has_visible_bell_p = 1;
1259
1260 /* We are *certainly* NOT dumb! */
1261 terminal_is_dumb_p = 0;
1262
1263 pc_get_screen_size ();
1264
1265 #ifdef __MINGW32__
1266 /* Record the screen output codepage. */
1267 output_cp = GetConsoleOutputCP ();
1268 #endif
1269
1270 #ifdef __MSDOS__
1271 /* Store the arrow keys. */
1272 term_ku = (char *)find_sequence (K_Up);
1273 term_kd = (char *)find_sequence (K_Down);
1274 term_kr = (char *)find_sequence (K_Right);
1275 term_kl = (char *)find_sequence (K_Left);
1276
1277 term_kP = (char *)find_sequence (K_PageUp);
1278 term_kN = (char *)find_sequence (K_PageDown);
1279
1280 term_kh = (char *)find_sequence (K_Home);
1281 term_ke = (char *)find_sequence (K_End);
1282 term_ki = (char *)find_sequence (K_Insert);
1283 term_kD = (char *)find_sequence (K_Delete);
1284 #elif defined _WIN32
1285 term_kh = "\033<";
1286 term_ke = "\033>";
1287 term_ki = "\033[L";
1288 #endif /* __MSDOS__ */
1289
1290 /* Set all the hooks to our PC-specific functions. */
1291 terminal_begin_inverse_hook = pc_begin_inverse;
1292 terminal_end_inverse_hook = pc_end_inverse;
1293 terminal_begin_standout_hook = pc_begin_standout;
1294 terminal_end_standout_hook = pc_end_standout;
1295 terminal_begin_underline_hook = pc_begin_underline;
1296 terminal_end_underline_hook = pc_end_underline;
1297 terminal_begin_bold_hook = pc_begin_standout;
1298 terminal_begin_blink_hook = pc_begin_blink;
1299 terminal_end_all_modes_hook = pc_default_color;
1300 terminal_default_colour_hook = pc_default_color;
1301 terminal_set_colour_hook = pc_set_fg_color;
1302 terminal_set_bgcolour_hook = pc_set_bg_color;
1303 terminal_prep_terminal_hook = pc_prep_terminal;
1304 terminal_unprep_terminal_hook = pc_unprep_terminal;
1305 terminal_up_line_hook = pc_up_line;
1306 terminal_down_line_hook = pc_down_line;
1307 terminal_clear_screen_hook = pc_clear_screen;
1308 terminal_clear_to_eol_hook = pc_clear_to_eol;
1309 terminal_get_screen_size_hook = pc_get_screen_size;
1310 terminal_goto_xy_hook = pc_goto_xy;
1311 terminal_put_text_hook = pc_put_text;
1312 terminal_ring_bell_hook = pc_ring_bell;
1313 terminal_write_chars_hook = pc_write_chars;
1314 terminal_scroll_terminal_hook = pc_scroll_terminal;
1315 }
1316
1317 /* **************************************************************** */
1318 /* */
1319 /* How to Read Characters From the PC Terminal */
1320 /* */
1321 /* **************************************************************** */
1322
1323 /* This will most certainly work ONLY with DJGPP. */
1324 #ifdef __DJGPP__
1325
1326 #include <errno.h>
1327 #include <sys/fsext.h>
1328 #include <dpmi.h>
1329
1330 /* Translation table for some special keys.
1331 Arrow keys which are standard on other keyboards are translated into
1332 standard ESC-sequences, in case somebody rebinds the simple keys
1333 (like C-f, C-b, C-n, etc.).
1334
1335 The strange "\033\061" prefix in some keys is a numeric argument of
1336 one, which means ``do the next command once''. It is here so that
1337 when the according PC key is pressed in the middle of an incremental
1338 search, Info doesn't see just an ASCII character like `n' or `B',
1339 and doesn't add it to the search string; instead, it will exit the
1340 incremental search and then perform the command. */
1341 static struct
1342 {
1343 int inkey;
1344 unsigned char const * const sequence;
1345 } DJGPP_keytab[] = { /* these are for moving between nodes... */
1346 {K_Control_PageDown, "\033\061n"},
1347 {K_Control_PageUp, "\033\061p"},
1348 {K_Control_Up, "\033\061u"},
1349 {K_Control_Down, "\033\061m"},
1350 {K_Control_Center, "\033\061l"},
1351
1352 {K_Home, "\033[H"}, /* ...and these are for moving IN a node */
1353 {K_End, "\033[F"}, /* they're Numeric-Keypad-Keys, so */
1354 {K_Left, "\033[D"}, /* NUMLOCK should be off !! */
1355 {K_Right, "\033[C"},
1356 {K_Down, "\033[B"},
1357 {K_Up, "\033[A"},
1358 {K_PageDown, "\033[G"},
1359 {K_PageUp, "\033[I"},
1360 {K_Control_Left, "\033b"},
1361 {K_Control_Right, "\033f"},
1362 {K_Control_Home, "\033<"},
1363 {K_Control_End, "\033>"},
1364
1365 {K_EHome, "\033[H"}, /* these are also for moving IN a node */
1366 {K_EEnd, "\033[F"}, /* they're the "extended" (Grey) keys */
1367 {K_ELeft, "\033[D"},
1368 {K_ERight, "\033[C"},
1369 {K_EDown, "\033[B"},
1370 {K_EUp, "\033[A"},
1371 {K_EPageDown, "\033[G"},
1372 {K_EPageUp, "\033[I"},
1373 {K_Control_ELeft, "\033b"},
1374 {K_Control_ERight, "\033f"},
1375 {K_Control_EHome, "\033<"},
1376 {K_Control_EEnd, "\033>"},
1377
1378 {K_BackTab, "\033\011"},
1379 {K_F1, "\10"}, /* YEAH, gimme that good old F-one-thing */
1380 {K_Delete, "\177"}, /* to make Kp-Del be DEL (0x7f) */
1381 {K_EDelete, "\177"}, /* to make Delete be DEL (0x7f) */
1382 {K_Insert, "\033[L"},
1383 {K_EInsert, "\033[L"},
1384
1385 /* These are here to map more Alt-X keys to ESC X sequences. */
1386 {K_Alt_Q, "\033q"},
1387 {K_Alt_W, "\033w"},
1388 {K_Alt_E, "\033e"},
1389 {K_Alt_R, "\033r"},
1390 {K_Alt_T, "\033t"},
1391 {K_Alt_Y, "\033y"},
1392 {K_Alt_U, "\033u"},
1393 {K_Alt_I, "\033i"},
1394 {K_Alt_O, "\033o"},
1395 {K_Alt_P, "\033p"},
1396 {K_Alt_LBracket, "\033["},
1397 {K_Alt_RBracket, "\033]"},
1398 {K_Alt_Return, "\033\015"},
1399 {K_Alt_A, "\033a"},
1400 {K_Alt_S, "\033s"},
1401 {K_Alt_D, "\033d"},
1402 {K_Alt_F, "\033f"},
1403 {K_Alt_G, "\033g"},
1404 {K_Alt_H, "\033h"},
1405 {K_Alt_J, "\033j"},
1406 {K_Alt_K, "\033k"},
1407 {K_Alt_L, "\033l"},
1408 {K_Alt_Semicolon, "\033;"},
1409 {K_Alt_Quote, "\033'"},
1410 {K_Alt_Backquote, "\033`"},
1411 {K_Alt_Backslash, "\033\\"},
1412 {K_Alt_Z, "\033z"},
1413 {K_Alt_X, "\033x"},
1414 {K_Alt_C, "\033c"},
1415 {K_Alt_V, "\033v"},
1416 {K_Alt_B, "\033b"},
1417 {K_Alt_N, "\033n"},
1418 {K_Alt_M, "\033m"},
1419 {K_Alt_Comma, "\033<"}, /* our reader cannot distinguish between */
1420 {K_Alt_Period, "\033>"}, /* Alt-. and Alt->, so we cheat a little */
1421 {K_Alt_Slash, "\033?"}, /* ditto, to get Alt-? */
1422 {K_Alt_Backspace, "\033\177"}, /* M-DEL, to delete word backwards */
1423 {K_Alt_1, "\033\061"},
1424 {K_Alt_2, "\033\062"},
1425 {K_Alt_3, "\033\063"},
1426 {K_Alt_4, "\033\064"},
1427 {K_Alt_5, "\033\065"},
1428 {K_Alt_6, "\033\066"},
1429 {K_Alt_7, "\033\067"},
1430 {K_Alt_8, "\033\070"},
1431 {K_Alt_9, "\033\071"},
1432 {K_Alt_0, "\033\060"},
1433 {K_Alt_Dash, "\033\055"},
1434 {K_Alt_EPageUp, "\033\033[I"},
1435 {K_Alt_EPageDown, "\033\033[G"},
1436 {K_Alt_Equals, "\033\075"},
1437 {K_Alt_EDelete, "\033\177"},
1438 {K_Alt_Tab, "\033\011"},
1439 {0, 0}
1440 };
1441
1442 /* Given a key, return the sequence of characters which
1443 our keyboard driver generates. */
1444 static unsigned const char *
1445 find_sequence (int key)
1446 {
1447 int i;
1448
1449 for (i = 0; DJGPP_keytab[i].inkey; i++)
1450 if (key == DJGPP_keytab[i].inkey)
1451 return DJGPP_keytab[i].sequence;
1452
1453 return NULL;
1454 }
1455
1456 /* Return zero if a key is pending in the
1457 keyboard buffer, non-zero otherwise. */
1458 static int
1459 kbd_buffer_empty (void)
1460 {
1461 __dpmi_regs r;
1462 int retval;
1463
1464 r.h.ah = 0x11; /* Get enhanced keyboard status */
1465 __dpmi_int (0x16, &r);
1466
1467 /* If the keyboard buffer is empty, the Zero Flag will be set. */
1468 return (r.x.flags & 0x40) == 0x40;
1469 }
1470
1471 /* The buffered characters pending to be read.
1472 Actually, Info usually reads a single character, but when we
1473 translate a key into a sequence of characters, we keep them here. */
1474 static unsigned char buffered[512];
1475
1476 /* Index of the next buffered character to be returned. */
1477 static int buf_idx;
1478
1479 /* Return the number of characters waiting to be read. */
1480 long
1481 pc_term_chars_avail (void)
1482 {
1483 if (buf_idx >= sizeof (buffered)) /* paranoia */
1484 {
1485 buf_idx = 0;
1486 buffered[buf_idx] = '\0';
1487 return 0;
1488 }
1489 else
1490 return strlen (buffered + buf_idx);
1491 }
1492
1493 /* Our special terminal keyboard reader. It will be called by
1494 low-level libc functions when the application calls `read' or
1495 the ANSI-standard stream-oriented read functions. If the
1496 caller wants to read the terminal, we redirect the call to
1497 the BIOS keyboard functions, since that lets us recognize more
1498 keys than DOS does. */
1499 static int
1500 keyboard_read (__FSEXT_Fnumber func, int *retval, va_list rest_args)
1501 {
1502 /* When we are called, REST_ARGS are: file_descriptor, buf, nbytes. */
1503 unsigned char *buf;
1504 size_t nbytes, nread = 0;
1505 int fd = va_arg (rest_args, int);
1506
1507 /* Is this call for us? */
1508 if (func != __FSEXT_read || !isatty (fd))
1509 return 0; /* and the usual DOS call will be issued */
1510
1511 buf = va_arg (rest_args, unsigned char *);
1512 nbytes = va_arg (rest_args, size_t);
1513
1514 if (!buf)
1515 {
1516 errno = EINVAL;
1517 *retval = -1;
1518 return 1;
1519 }
1520 if (!nbytes)
1521 {
1522 *retval = 0;
1523 return 1;
1524 }
1525
1526 /* Loop here until enough bytes has been read. */
1527 do
1528 {
1529 int key;
1530
1531 /* If any ``buffered characters'' are left, return as much
1532 of them as the caller wanted. */
1533 while (buffered[buf_idx] && nbytes)
1534 {
1535 *buf++ = buffered[buf_idx++];
1536 nread++;
1537 nbytes--;
1538 }
1539
1540 if (nbytes <= 0)
1541 break;
1542
1543 /* Wait for another key.
1544 We do that in a busy-waiting loop so we don't get parked
1545 inside a BIOS call, which will effectively disable signals.
1546 While we wait for them to type something, we repeatedly
1547 release the rest of our time slice, so that other programs
1548 in a multitasking environment, such as Windows, get more cycles. */
1549 while (kbd_buffer_empty ())
1550 __dpmi_yield ();
1551
1552 key = getxkey ();
1553
1554 /* Translate the key if necessary.
1555 Untranslated non-ASCII keys are silently ignored. */
1556 if ((key & 0x300) != 0)
1557 {
1558 unsigned char const * key_sequence = find_sequence (key);
1559
1560 if (key_sequence != NULL)
1561 {
1562 strcpy (buffered, key_sequence);
1563 buf_idx = 0;
1564 }
1565 }
1566 else if (key == K_Control_Z)
1567 raise (SIGUSR1); /* we don't have SIGTSTP, so simulate it */
1568 else if (key <= 0xff)
1569 {
1570 *buf++ = key;
1571 nbytes--;
1572 nread++;
1573 }
1574 }
1575 while (nbytes > 0);
1576
1577 *retval = nread;
1578 return 1; /* meaning that we handled the call */
1579 }
1580
1581 /* Install our keyboard handler.
1582 This is called by the startup code before `main'. */
1583 static void __attribute__((constructor))
1584 install_keyboard_handler (void)
1585 {
1586 __FSEXT_set_function (fileno (stdin), keyboard_read);
1587
1588 /* We need to set this single hook here; the rest
1589 will be set by pc_initialize_terminal when it is called. */
1590 terminal_initialize_terminal_hook = pc_initialize_terminal;
1591 }
1592
1593 #endif /* __DJGPP__ */
1594
1595 /* **************************************************************** */
1596 /* */
1597 /* Emulation of SIGTSTP on Ctrl-Z */
1598 /* */
1599 /* **************************************************************** */
1600
1601 #include <limits.h>
1602 #include "signals.h"
1603
1604 #ifndef PATH_MAX
1605 # define PATH_MAX 512
1606 #endif
1607
1608 /* Effectively disable signals which aren't defined
1609 (assuming no signal can ever be zero).
1610 SIGINT is ANSI, so we expect it to be always defined. */
1611 #ifndef SIGUSR1
1612 # define SIGUSR1 0
1613 #endif
1614 #ifndef SIGQUIT
1615 # define SIGQUIT 0
1616 #endif
1617
1618 int
1619 kill (pid_t pid, int sig)
1620 {
1621 static char interrupted_msg[] = "Interrupted\r\n";
1622 static char stopped_msg[] = "Stopped. Type 'exit RET' to return.\r\n";
1623 char cwd[PATH_MAX + 1];
1624
1625 if (pid == getpid ()
1626 || pid == 0
1627 || pid == -1
1628 || pid == -getpid ())
1629 {
1630 switch (sig)
1631 {
1632 void (*old_INT)(int), (*old_QUIT)(int);
1633
1634 case SIGINT:
1635 #ifdef __DJGPP__
1636 /* If SIGINT was generated by a readable key, we want to remove
1637 it from the PC keyboard buffer, so that DOS and other
1638 programs never see it. DJGPP signal-handling mechanism
1639 doesn't remove the INT key from the keyboard buffer. */
1640 if (!kbd_buffer_empty ())
1641 getxkey ();
1642 #endif
1643 pc_write_chars (interrupted_msg, sizeof (interrupted_msg) - 1);
1644 exit (EXIT_FAILURE);
1645 case SIGUSR1:
1646 /* Simulate SIGTSTP by invoking a subsidiary shell. */
1647 #ifndef _WIN32
1648 pc_goto_xy (0, outside_info.screenheight - 1);
1649 pc_clear_to_eol ();
1650 pc_write_chars (stopped_msg, sizeof (stopped_msg) - 1);
1651 #endif
1652
1653 /* The child shell can change the working directory, so
1654 we need to save and restore it, since it is global. */
1655 if (!getcwd (cwd, PATH_MAX)) /* should never happen */
1656 cwd[0] = '\0';
1657
1658 /* We don't want to get fatal signals while the subshell runs. */
1659 old_INT = signal (SIGINT, SIG_IGN);
1660 old_QUIT = signal (SIGQUIT, SIG_IGN);
1661 #ifdef _WIN32
1662 {
1663 const char *argv[2];
1664 const char *shell = NULL;
1665
1666 argv[0] = NULL;
1667 shell = getenv ("SHELL");
1668 if (!shell)
1669 {
1670 shell = getenv ("COMSPEC");
1671 if (!shell)
1672 return -1;
1673 argv[0] = " /k";
1674 }
1675 argv[1] = NULL;
1676 _spawnvp (_P_WAIT, shell, argv);
1677 }
1678 #else
1679 system ("");
1680 #endif
1681 if (*cwd)
1682 chdir (cwd);
1683 signal (SIGINT, old_INT);
1684 signal (SIGQUIT, old_QUIT);
1685 break;
1686 default:
1687 if (sig)
1688 raise (sig);
1689 break;
1690 }
1691 return 0;
1692 }
1693 else
1694 return -1;
1695 }
1696
1697 /* These should never be called, but they make the linker happy. */
1698
1699 int tputs (const char *a, int b, int (*c)(int))
1700 {
1701 perror ("tputs"); return 0; /* here and below, added dummy retvals */
1702 }
1703
1704 char* tgoto (const char *a, int b, int c)
1705 {
1706 perror ("tgoto"); return 0;
1707 }
1708
1709 int tgetnum (char*a)
1710 {
1711 perror ("tgetnum"); return 0;
1712 }
1713
1714 int tgetflag (char*a)
1715 {
1716 perror ("tgetflag"); return 0;
1717 }
1718
1719 char* tgetstr (char *a, char **b)
1720 {
1721 perror ("tgetstr"); return 0;
1722 }
1723
1724 int tgetent (char *a, const char *b)
1725 {
1726 perror ("tgetent"); return 0;
1727 }
1728
1729 int tcgetattr(int fildes, struct termios *termios_p)
1730 {
1731 perror ("tcgetattr"); return 0;
1732 }
1733
1734 int tcsetattr(int fd, int opt_actions, const struct termios *termios_p)
1735 {
1736 perror ("tcsetattr"); return 0;
1737 }