1 /* pcterm.c -- How to handle the PC terminal for Info under MS-DOS/MS-Windows.
2
3 Copyright 1998-2019 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
w32_info_prep(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
w32_info_unprep(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
w32_cleanup(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
w32_info_init(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
gettextinfo(struct text_info * ti)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
textattr(int attr)236 textattr (int attr)
237 {
238 if (hscreen != INVALID_HANDLE_VALUE)
239 SetConsoleTextAttribute (hscreen, attr);
240 }
241
242 void
textmode(int mode)243 textmode (int mode)
244 {
245 /* Nothing. */
246 }
247
248 void
highvideo(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
normvideo(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
blinkvideo(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
underline(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
textcolor(int color)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
textbackground(int color)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
ScreenGetCursor(int * row,int * col)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
ScreenSetCursor(int row,int col)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
ScreenClear(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
clreol(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
ScreenVisualBell(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
movetext(int left,int top,int right,int bottom,int destleft,int desttop)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
ScreenRows(void)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
ScreenCols(void)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
_set_screen_lines(int lines)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
w32_set_screen_dimensions(int cols,int rows)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
sleep(unsigned sec)509 sleep (unsigned sec)
510 {
511 Sleep (sec*1000);
512 return 0;
513 }
514
515 /* Keyboard input support. */
516
517 static int
w32_our_tty(int fd)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
w32keyseq(unsigned char ascii_ch,WORD vkey,DWORD ctl,unsigned char * seq)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
w32_kbd_read(unsigned char * inbuf,size_t n)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
w32_chars_avail(int fd)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
w32_read(int fd,void * buf,size_t n)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
write_utf(DWORD cp,const char * text,int nbytes)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 *
rpl_nl_langinfo(nl_item item)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
wcwidth(wchar_t wc)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
pc_begin_inverse(void)861 pc_begin_inverse (void)
862 {
863 textattr (inv_attr);
864 }
865
866 /* Turn off reverse video. */
867 static void
pc_end_inverse(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
pc_begin_underline(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
pc_end_underline(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
pc_begin_standout(void)896 pc_begin_standout (void)
897 {
898 highvideo ();
899 }
900
901 static void
pc_end_standout(void)902 pc_end_standout (void)
903 {
904 normvideo ();
905 }
906
907 static void
pc_begin_blink(void)908 pc_begin_blink (void)
909 {
910 blinkvideo ();
911 }
912
913 static void
pc_default_color(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
convert_color(int terminal_color)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
pc_set_fg_color(int color)942 pc_set_fg_color (int color)
943 {
944 textcolor (convert_color (color) | (norm_attr & FOREGROUND_INTENSITY));
945 }
946
947 static void
pc_set_bg_color(int color)948 pc_set_bg_color (int color)
949 {
950 textbackground (convert_color (color) | (norm_attr & BACKGROUND_INTENSITY));
951 }
952
953 /* Move the cursor up one line. */
954 static void
pc_up_line(void)955 pc_up_line (void)
956 {
957 int x, y;
958 ScreenGetCursor (&y, &x);
959 ScreenSetCursor (MAX (y-1, 0), x);
960 }
961
962 /* Move the cursor down one line. */
963 static void
pc_down_line(void)964 pc_down_line (void)
965 {
966 int x, y;
967 ScreenGetCursor (&y, &x);
968 ScreenSetCursor (MIN (screenheight-1, y+1), x);
969 }
970
971 /* Clear the entire terminal screen. */
972 static void
pc_clear_screen(void)973 pc_clear_screen (void)
974 {
975 ScreenClear ();
976 }
977
978 /* Clear from the current position of the cursor to the end of the line. */
979 static void
pc_clear_to_eol(void)980 pc_clear_to_eol (void)
981 {
982 clreol (); /* perhaps to be replaced by a loop */
983 }
984
985 /* Set the global variables SCREENWIDTH and SCREENHEIGHT. */
986 static void
pc_get_screen_size(void)987 pc_get_screen_size(void)
988 {
989 /* Current screen dimensions are the default. */
990 if (!outside_info.screenheight) /* paranoia */
991 gettextinfo (&outside_info);
992 screenwidth = outside_info.screenwidth;
993 screenheight = outside_info.screenheight;
994
995 /* Environment variable "LINES" overrides the default. */
996 if (getenv ("LINES") != NULL)
997 screenheight = atoi (getenv ("LINES"));
998
999 /* Environment variable "INFO_LINES" overrides "LINES". */
1000 if (getenv ("INFO_LINES") != NULL)
1001 screenheight = atoi (getenv ("INFO_LINES"));
1002 }
1003
1004 /* Move the cursor to the terminal location of X and Y. */
1005 static void
pc_goto_xy(x,y)1006 pc_goto_xy (x, y)
1007 int x, y;
1008 {
1009 ScreenSetCursor (y, x); /* yes, pc.h says ScreenSetCursor (row, column) !! */
1010 }
1011
1012 /* Print STRING to the terminal at the current position. */
1013 static void
pc_put_text(string)1014 pc_put_text (string)
1015 char *string;
1016 {
1017 if (speech_friendly)
1018 fputs (string, stdout);
1019 #ifdef __MINGW32__
1020 else if (hscreen == INVALID_HANDLE_VALUE)
1021 fputs (string, stdout);
1022 else if (output_cp == CP_UTF8 || output_cp == CP_UTF7)
1023 write_utf (output_cp, string, -1);
1024 #endif
1025 else
1026 cputs (string);
1027 }
1028
1029 /* Ring the terminal bell. The bell is rung visibly if the terminal is
1030 capable of doing that, and if terminal_use_visible_bell_p is non-zero. */
1031 static void
pc_ring_bell(void)1032 pc_ring_bell(void)
1033 {
1034 if (terminal_has_visible_bell_p && terminal_use_visible_bell_p)
1035 ScreenVisualBell ();
1036 else
1037 {
1038 printf ("%c",'\a');
1039 fflush (stdout);
1040 }
1041 }
1042
1043 /* Print NCHARS from STRING to the terminal at the current position. */
1044 static void
pc_write_chars(string,nchars)1045 pc_write_chars (string, nchars)
1046 char *string;
1047 int nchars;
1048 {
1049 if (!nchars)
1050 return;
1051
1052 if (speech_friendly)
1053 printf ("%.*s", nchars, string);
1054 #ifdef __MINGW32__
1055 else if (hscreen == INVALID_HANDLE_VALUE)
1056 printf ("%.*s", nchars, string);
1057 else if (output_cp == CP_UTF8 || output_cp == CP_UTF7)
1058 write_utf (output_cp, string, nchars);
1059 #endif
1060 else
1061 cprintf ("%.*s", nchars, string);
1062 }
1063
1064 /* Scroll an area of the terminal from START to (and excluding) END,
1065 AMOUNT lines. If AMOUNT is negative, the lines are scrolled
1066 towards the top of the screen, else they are scrolled towards the
1067 bottom of the screen. The lines of the old region which do not
1068 overlap the new region are cleared, to mimic terminal operation. */
1069 static void
pc_scroll_terminal(start,end,amount)1070 pc_scroll_terminal (start, end, amount)
1071 int start, end, amount;
1072 {
1073 int line_to_clear = amount > 0 ? start : end + amount;
1074
1075 /* Move the text. Note that `movetext' expects 1-based coordinates. */
1076 movetext (1, start + 1, ScreenCols (), end, 1, start + amount + 1);
1077
1078 /* Now clear the lines which were left unoccupied. */
1079 if (amount < 0)
1080 amount = -amount;
1081 while (amount--)
1082 {
1083 ScreenSetCursor (line_to_clear++, 0);
1084 clreol ();
1085 }
1086 }
1087
1088 /* Put the screen in the video mode and colors which Info will use.
1089 Prepare to start using the terminal to read characters singly. */
1090 static void
pc_prep_terminal(void)1091 pc_prep_terminal (void)
1092 {
1093 int tty;
1094
1095 #ifdef _WIN32
1096 w32_info_prep ();
1097 #endif
1098
1099 /* Do not set screen height if we already have it, because
1100 doing so erases the screen. */
1101 if (screenheight != ScreenRows ())
1102 _set_screen_lines (screenheight);
1103
1104 /* Don't fail if they asked for screen dimensions that their
1105 hardware cannot support. */
1106 screenheight = ScreenRows ();
1107 screenwidth = ScreenCols ();
1108
1109 /* Try setting the colors user asked for. */
1110 textattr (norm_attr);
1111 ScreenClear ();
1112
1113 /* Switch console reads to binary mode. */
1114 tty = fileno (stdin);
1115 #ifdef __DJGPP__
1116 setmode (tty, O_BINARY);
1117 __djgpp_set_ctrl_c (1); /* re-enable SIGINT generation by Ctrl-C */
1118 #endif
1119 }
1120
1121 /* Restore the tty settings back to what they were before we started using
1122 this terminal. */
1123 static void
pc_unprep_terminal(void)1124 pc_unprep_terminal (void)
1125 {
1126 int tty;
1127
1128 #ifdef _WIN32
1129 w32_info_unprep ();
1130 #endif
1131
1132 textattr (outside_info.normattr);
1133
1134 /* Do not set screen height if we already have it, because
1135 doing so erases the screen. */
1136 if (outside_info.screenheight != ScreenRows ())
1137 {
1138 _set_screen_lines (outside_info.screenheight);
1139 textmode (LASTMODE);
1140 }
1141 #ifdef __MSDOS__
1142 else
1143 pc_clear_to_eol (); /* for text attributes to really take effect */
1144 #endif
1145 #ifdef _WIN32
1146 if (hscreen != INVALID_HANDLE_VALUE)
1147 SetConsoleScreenBufferSize (hstdout, outside_info.bufsize);
1148 #endif
1149
1150 /* Switch back to text mode on stdin. */
1151 tty = fileno (stdin);
1152 #ifdef __DJGPP__
1153 setmode (tty, O_TEXT);
1154 #endif
1155 }
1156
1157 /* Initialize the terminal which is known as TERMINAL_NAME. If this
1158 terminal doesn't have cursor addressability, `terminal_is_dumb_p'
1159 becomes nonzero. The variables SCREENHEIGHT and SCREENWIDTH are set
1160 to the dimensions that this terminal actually has. Finally, the
1161 terminal screen is cleared. */
1162 static void
pc_initialize_terminal(term_name)1163 pc_initialize_terminal (term_name)
1164 char *term_name;
1165 {
1166 char *info_colors;
1167
1168 if (!term_name)
1169 {
1170 term_name = getenv ("TERM");
1171 if (!term_name)
1172 #ifdef __MSDOS__
1173 term_name = "pc-dos"; /* ``what's in a name?'' */
1174 #endif
1175 #ifdef _WIN32
1176 term_name = "w32console";
1177 #endif
1178 }
1179
1180 /* Get current video information, to be restored later. */
1181 if (outside_info.screenwidth == 0)
1182 gettextinfo (&outside_info);
1183
1184 /* Current screen colors are the default. */
1185 norm_attr = outside_info.normattr;
1186 inv_attr = (((outside_info.normattr & 7) << 4) |
1187 ((outside_info.normattr & 0x7f) >> 4));
1188 #ifdef __MSDOS__
1189 xref_attr = CYAN;
1190 #endif
1191 #ifdef _WIN32
1192 xref_attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
1193 #endif
1194 xref_attr |= outside_info.normattr & 0xf0;
1195
1196 /* Does the user want non-default colors? */
1197 info_colors = getenv ("INFO_COLORS");
1198 if ((info_colors != (char *)0) && !speech_friendly)
1199 {
1200 /* Decode a color from a string descriptor.
1201 The descriptor string is a sequence of color specifiers separated
1202 by a non-numeric character. Each color specifier should represent
1203 a small integer which fits into an unsigned char, and can be given
1204 in any base supported by strtoul. Examples of valid descriptors:
1205
1206 "10 31"
1207 "0x13/0x45"
1208 "007.077"
1209
1210 The separator between two color specifiers can be any character which
1211 cannot be used in a printed representation of an integer number. */
1212 char *endp;
1213 unsigned long color_desc = strtoul (info_colors, &endp, 0);
1214
1215 if (color_desc <= UCHAR_MAX)
1216 {
1217 norm_attr = (unsigned char)color_desc;
1218 xref_attr = (xref_attr & 0x0f) | (norm_attr & 0xf0);
1219 color_desc = strtoul (endp + 1, &endp, 0);
1220 if (color_desc <= UCHAR_MAX)
1221 inv_attr = (unsigned char)color_desc;
1222 #ifdef _WIN32
1223 if (*endp == 'u')
1224 xref_attr = COMMON_LVB_UNDERSCORE;
1225 else
1226 #endif
1227 if (*endp != '\0')
1228 {
1229 color_desc = strtoul (endp + 1, &endp, 0);
1230 if (color_desc <= UCHAR_MAX)
1231 {
1232 #ifdef _WIN32
1233 if (*endp == 'u')
1234 color_desc |= COMMON_LVB_UNDERSCORE;
1235 xref_attr = (WORD)color_desc;
1236 #else
1237 xref_attr = (unsigned char)color_desc;
1238 #endif
1239 }
1240 }
1241 }
1242 }
1243
1244 /* We can scroll. */
1245 terminal_can_scroll = 1;
1246
1247 /* We know how to produce a visible bell, if somebody's looking... */
1248 if (!speech_friendly)
1249 terminal_has_visible_bell_p = 1;
1250
1251 /* We are *certainly* NOT dumb! */
1252 terminal_is_dumb_p = 0;
1253
1254 pc_get_screen_size ();
1255
1256 #ifdef __MINGW32__
1257 /* Record the screen output codepage. */
1258 output_cp = GetConsoleOutputCP ();
1259 #endif
1260
1261 #ifdef __MSDOS__
1262 /* Store the arrow keys. */
1263 term_ku = (char *)find_sequence (K_Up);
1264 term_kd = (char *)find_sequence (K_Down);
1265 term_kr = (char *)find_sequence (K_Right);
1266 term_kl = (char *)find_sequence (K_Left);
1267
1268 term_kP = (char *)find_sequence (K_PageUp);
1269 term_kN = (char *)find_sequence (K_PageDown);
1270
1271 term_kh = (char *)find_sequence (K_Home);
1272 term_ke = (char *)find_sequence (K_End);
1273 term_ki = (char *)find_sequence (K_Insert);
1274 term_kD = (char *)find_sequence (K_Delete);
1275 #elif defined _WIN32
1276 term_kh = "\033<";
1277 term_ke = "\033>";
1278 term_ki = "\033[L";
1279 #endif /* __MSDOS__ */
1280
1281 /* Set all the hooks to our PC-specific functions. */
1282 terminal_begin_inverse_hook = pc_begin_inverse;
1283 terminal_end_inverse_hook = pc_end_inverse;
1284 terminal_begin_standout_hook = pc_begin_standout;
1285 terminal_end_standout_hook = pc_end_standout;
1286 terminal_begin_underline_hook = pc_begin_underline;
1287 terminal_end_underline_hook = pc_end_underline;
1288 terminal_begin_bold_hook = pc_begin_standout;
1289 terminal_begin_blink_hook = pc_begin_blink;
1290 terminal_end_all_modes_hook = pc_default_color;
1291 terminal_default_colour_hook = pc_default_color;
1292 terminal_set_colour_hook = pc_set_fg_color;
1293 terminal_set_bgcolour_hook = pc_set_bg_color;
1294 terminal_prep_terminal_hook = pc_prep_terminal;
1295 terminal_unprep_terminal_hook = pc_unprep_terminal;
1296 terminal_up_line_hook = pc_up_line;
1297 terminal_down_line_hook = pc_down_line;
1298 terminal_clear_screen_hook = pc_clear_screen;
1299 terminal_clear_to_eol_hook = pc_clear_to_eol;
1300 terminal_get_screen_size_hook = pc_get_screen_size;
1301 terminal_goto_xy_hook = pc_goto_xy;
1302 terminal_put_text_hook = pc_put_text;
1303 terminal_ring_bell_hook = pc_ring_bell;
1304 terminal_write_chars_hook = pc_write_chars;
1305 terminal_scroll_terminal_hook = pc_scroll_terminal;
1306 }
1307
1308 /* **************************************************************** */
1309 /* */
1310 /* How to Read Characters From the PC Terminal */
1311 /* */
1312 /* **************************************************************** */
1313
1314 /* This will most certainly work ONLY with DJGPP. */
1315 #ifdef __DJGPP__
1316
1317 #include <errno.h>
1318 #include <sys/fsext.h>
1319 #include <dpmi.h>
1320
1321 /* Translation table for some special keys.
1322 Arrow keys which are standard on other keyboards are translated into
1323 standard ESC-sequences, in case somebody rebinds the simple keys
1324 (like C-f, C-b, C-n, etc.).
1325
1326 The strange "\033\061" prefix in some keys is a numeric argument of
1327 one, which means ``do the next command once''. It is here so that
1328 when the according PC key is pressed in the middle of an incremental
1329 search, Info doesn't see just an ASCII character like `n' or `B',
1330 and doesn't add it to the search string; instead, it will exit the
1331 incremental search and then perform the command. */
1332 static struct
1333 {
1334 int inkey;
1335 unsigned char const * const sequence;
1336 } DJGPP_keytab[] = { /* these are for moving between nodes... */
1337 {K_Control_PageDown, "\033\061n"},
1338 {K_Control_PageUp, "\033\061p"},
1339 {K_Control_Up, "\033\061u"},
1340 {K_Control_Down, "\033\061m"},
1341 {K_Control_Center, "\033\061l"},
1342
1343 {K_Home, "\033[H"}, /* ...and these are for moving IN a node */
1344 {K_End, "\033[F"}, /* they're Numeric-Keypad-Keys, so */
1345 {K_Left, "\033[D"}, /* NUMLOCK should be off !! */
1346 {K_Right, "\033[C"},
1347 {K_Down, "\033[B"},
1348 {K_Up, "\033[A"},
1349 {K_PageDown, "\033[G"},
1350 {K_PageUp, "\033[I"},
1351 {K_Control_Left, "\033b"},
1352 {K_Control_Right, "\033f"},
1353 {K_Control_Home, "\033<"},
1354 {K_Control_End, "\033>"},
1355
1356 {K_EHome, "\033[H"}, /* these are also for moving IN a node */
1357 {K_EEnd, "\033[F"}, /* they're the "extended" (Grey) keys */
1358 {K_ELeft, "\033[D"},
1359 {K_ERight, "\033[C"},
1360 {K_EDown, "\033[B"},
1361 {K_EUp, "\033[A"},
1362 {K_EPageDown, "\033[G"},
1363 {K_EPageUp, "\033[I"},
1364 {K_Control_ELeft, "\033b"},
1365 {K_Control_ERight, "\033f"},
1366 {K_Control_EHome, "\033<"},
1367 {K_Control_EEnd, "\033>"},
1368
1369 {K_BackTab, "\033\011"},
1370 {K_F1, "\10"}, /* YEAH, gimme that good old F-one-thing */
1371 {K_Delete, "\177"}, /* to make Kp-Del be DEL (0x7f) */
1372 {K_EDelete, "\177"}, /* to make Delete be DEL (0x7f) */
1373 {K_Insert, "\033[L"},
1374 {K_EInsert, "\033[L"},
1375
1376 /* These are here to map more Alt-X keys to ESC X sequences. */
1377 {K_Alt_Q, "\033q"},
1378 {K_Alt_W, "\033w"},
1379 {K_Alt_E, "\033e"},
1380 {K_Alt_R, "\033r"},
1381 {K_Alt_T, "\033t"},
1382 {K_Alt_Y, "\033y"},
1383 {K_Alt_U, "\033u"},
1384 {K_Alt_I, "\033i"},
1385 {K_Alt_O, "\033o"},
1386 {K_Alt_P, "\033p"},
1387 {K_Alt_LBracket, "\033["},
1388 {K_Alt_RBracket, "\033]"},
1389 {K_Alt_Return, "\033\015"},
1390 {K_Alt_A, "\033a"},
1391 {K_Alt_S, "\033s"},
1392 {K_Alt_D, "\033d"},
1393 {K_Alt_F, "\033f"},
1394 {K_Alt_G, "\033g"},
1395 {K_Alt_H, "\033h"},
1396 {K_Alt_J, "\033j"},
1397 {K_Alt_K, "\033k"},
1398 {K_Alt_L, "\033l"},
1399 {K_Alt_Semicolon, "\033;"},
1400 {K_Alt_Quote, "\033'"},
1401 {K_Alt_Backquote, "\033`"},
1402 {K_Alt_Backslash, "\033\\"},
1403 {K_Alt_Z, "\033z"},
1404 {K_Alt_X, "\033x"},
1405 {K_Alt_C, "\033c"},
1406 {K_Alt_V, "\033v"},
1407 {K_Alt_B, "\033b"},
1408 {K_Alt_N, "\033n"},
1409 {K_Alt_M, "\033m"},
1410 {K_Alt_Comma, "\033<"}, /* our reader cannot distinguish between */
1411 {K_Alt_Period, "\033>"}, /* Alt-. and Alt->, so we cheat a little */
1412 {K_Alt_Slash, "\033?"}, /* ditto, to get Alt-? */
1413 {K_Alt_Backspace, "\033\177"}, /* M-DEL, to delete word backwards */
1414 {K_Alt_1, "\033\061"},
1415 {K_Alt_2, "\033\062"},
1416 {K_Alt_3, "\033\063"},
1417 {K_Alt_4, "\033\064"},
1418 {K_Alt_5, "\033\065"},
1419 {K_Alt_6, "\033\066"},
1420 {K_Alt_7, "\033\067"},
1421 {K_Alt_8, "\033\070"},
1422 {K_Alt_9, "\033\071"},
1423 {K_Alt_0, "\033\060"},
1424 {K_Alt_Dash, "\033\055"},
1425 {K_Alt_EPageUp, "\033\033[I"},
1426 {K_Alt_EPageDown, "\033\033[G"},
1427 {K_Alt_Equals, "\033\075"},
1428 {K_Alt_EDelete, "\033\177"},
1429 {K_Alt_Tab, "\033\011"},
1430 {0, 0}
1431 };
1432
1433 /* Given a key, return the sequence of characters which
1434 our keyboard driver generates. */
1435 static unsigned const char *
find_sequence(int key)1436 find_sequence (int key)
1437 {
1438 int i;
1439
1440 for (i = 0; DJGPP_keytab[i].inkey; i++)
1441 if (key == DJGPP_keytab[i].inkey)
1442 return DJGPP_keytab[i].sequence;
1443
1444 return NULL;
1445 }
1446
1447 /* Return zero if a key is pending in the
1448 keyboard buffer, non-zero otherwise. */
1449 static int
kbd_buffer_empty(void)1450 kbd_buffer_empty (void)
1451 {
1452 __dpmi_regs r;
1453 int retval;
1454
1455 r.h.ah = 0x11; /* Get enhanced keyboard status */
1456 __dpmi_int (0x16, &r);
1457
1458 /* If the keyboard buffer is empty, the Zero Flag will be set. */
1459 return (r.x.flags & 0x40) == 0x40;
1460 }
1461
1462 /* The buffered characters pending to be read.
1463 Actually, Info usually reads a single character, but when we
1464 translate a key into a sequence of characters, we keep them here. */
1465 static unsigned char buffered[512];
1466
1467 /* Index of the next buffered character to be returned. */
1468 static int buf_idx;
1469
1470 /* Return the number of characters waiting to be read. */
1471 long
pc_term_chars_avail(void)1472 pc_term_chars_avail (void)
1473 {
1474 if (buf_idx >= sizeof (buffered)) /* paranoia */
1475 {
1476 buf_idx = 0;
1477 buffered[buf_idx] = '\0';
1478 return 0;
1479 }
1480 else
1481 return strlen (buffered + buf_idx);
1482 }
1483
1484 /* Our special terminal keyboard reader. It will be called by
1485 low-level libc functions when the application calls `read' or
1486 the ANSI-standard stream-oriented read functions. If the
1487 caller wants to read the terminal, we redirect the call to
1488 the BIOS keyboard functions, since that lets us recognize more
1489 keys than DOS does. */
1490 static int
keyboard_read(__FSEXT_Fnumber func,int * retval,va_list rest_args)1491 keyboard_read (__FSEXT_Fnumber func, int *retval, va_list rest_args)
1492 {
1493 /* When we are called, REST_ARGS are: file_descriptor, buf, nbytes. */
1494 unsigned char *buf;
1495 size_t nbytes, nread = 0;
1496 int fd = va_arg (rest_args, int);
1497
1498 /* Is this call for us? */
1499 if (func != __FSEXT_read || !isatty (fd))
1500 return 0; /* and the usual DOS call will be issued */
1501
1502 buf = va_arg (rest_args, unsigned char *);
1503 nbytes = va_arg (rest_args, size_t);
1504
1505 if (!buf)
1506 {
1507 errno = EINVAL;
1508 *retval = -1;
1509 return 1;
1510 }
1511 if (!nbytes)
1512 {
1513 *retval = 0;
1514 return 1;
1515 }
1516
1517 /* Loop here until enough bytes has been read. */
1518 do
1519 {
1520 int key;
1521
1522 /* If any ``buffered characters'' are left, return as much
1523 of them as the caller wanted. */
1524 while (buffered[buf_idx] && nbytes)
1525 {
1526 *buf++ = buffered[buf_idx++];
1527 nread++;
1528 nbytes--;
1529 }
1530
1531 if (nbytes <= 0)
1532 break;
1533
1534 /* Wait for another key.
1535 We do that in a busy-waiting loop so we don't get parked
1536 inside a BIOS call, which will effectively disable signals.
1537 While we wait for them to type something, we repeatedly
1538 release the rest of our time slice, so that other programs
1539 in a multitasking environment, such as Windows, get more cycles. */
1540 while (kbd_buffer_empty ())
1541 __dpmi_yield ();
1542
1543 key = getxkey ();
1544
1545 /* Translate the key if necessary.
1546 Untranslated non-ASCII keys are silently ignored. */
1547 if ((key & 0x300) != 0)
1548 {
1549 unsigned char const * key_sequence = find_sequence (key);
1550
1551 if (key_sequence != NULL)
1552 {
1553 strcpy (buffered, key_sequence);
1554 buf_idx = 0;
1555 }
1556 }
1557 else if (key == K_Control_Z)
1558 raise (SIGUSR1); /* we don't have SIGTSTP, so simulate it */
1559 else if (key <= 0xff)
1560 {
1561 *buf++ = key;
1562 nbytes--;
1563 nread++;
1564 }
1565 }
1566 while (nbytes > 0);
1567
1568 *retval = nread;
1569 return 1; /* meaning that we handled the call */
1570 }
1571
1572 /* Install our keyboard handler.
1573 This is called by the startup code before `main'. */
1574 static void __attribute__((constructor))
install_keyboard_handler(void)1575 install_keyboard_handler (void)
1576 {
1577 __FSEXT_set_function (fileno (stdin), keyboard_read);
1578
1579 /* We need to set this single hook here; the rest
1580 will be set by pc_initialize_terminal when it is called. */
1581 terminal_initialize_terminal_hook = pc_initialize_terminal;
1582 }
1583
1584 #endif /* __DJGPP__ */
1585
1586 /* **************************************************************** */
1587 /* */
1588 /* Emulation of SIGTSTP on Ctrl-Z */
1589 /* */
1590 /* **************************************************************** */
1591
1592 #include <limits.h>
1593 #include "signals.h"
1594
1595 #ifndef PATH_MAX
1596 # define PATH_MAX 512
1597 #endif
1598
1599 /* Effectively disable signals which aren't defined
1600 (assuming no signal can ever be zero).
1601 SIGINT is ANSI, so we expect it to be always defined. */
1602 #ifndef SIGUSR1
1603 # define SIGUSR1 0
1604 #endif
1605 #ifndef SIGQUIT
1606 # define SIGQUIT 0
1607 #endif
1608
1609 int
kill(pid_t pid,int sig)1610 kill (pid_t pid, int sig)
1611 {
1612 static char interrupted_msg[] = "Interrupted\r\n";
1613 static char stopped_msg[] = "Stopped. Type 'exit RET' to return.\r\n";
1614 char cwd[PATH_MAX + 1];
1615
1616 if (pid == getpid ()
1617 || pid == 0
1618 || pid == -1
1619 || pid == -getpid ())
1620 {
1621 switch (sig)
1622 {
1623 void (*old_INT)(int), (*old_QUIT)(int);
1624
1625 case SIGINT:
1626 #ifdef __DJGPP__
1627 /* If SIGINT was generated by a readable key, we want to remove
1628 it from the PC keyboard buffer, so that DOS and other
1629 programs never see it. DJGPP signal-handling mechanism
1630 doesn't remove the INT key from the keyboard buffer. */
1631 if (!kbd_buffer_empty ())
1632 getxkey ();
1633 #endif
1634 pc_write_chars (interrupted_msg, sizeof (interrupted_msg) - 1);
1635 exit (EXIT_FAILURE);
1636 case SIGUSR1:
1637 /* Simulate SIGTSTP by invoking a subsidiary shell. */
1638 #ifndef _WIN32
1639 pc_goto_xy (0, outside_info.screenheight - 1);
1640 pc_clear_to_eol ();
1641 pc_write_chars (stopped_msg, sizeof (stopped_msg) - 1);
1642 #endif
1643
1644 /* The child shell can change the working directory, so
1645 we need to save and restore it, since it is global. */
1646 if (!getcwd (cwd, PATH_MAX)) /* should never happen */
1647 cwd[0] = '\0';
1648
1649 /* We don't want to get fatal signals while the subshell runs. */
1650 old_INT = signal (SIGINT, SIG_IGN);
1651 old_QUIT = signal (SIGQUIT, SIG_IGN);
1652 #ifdef _WIN32
1653 {
1654 const char *argv[2];
1655 const char *shell = NULL;
1656
1657 argv[0] = NULL;
1658 shell = getenv ("SHELL");
1659 if (!shell)
1660 {
1661 shell = getenv ("COMSPEC");
1662 if (!shell)
1663 return -1;
1664 argv[0] = " /k";
1665 }
1666 argv[1] = NULL;
1667 _spawnvp (_P_WAIT, shell, argv);
1668 }
1669 #else
1670 system ("");
1671 #endif
1672 if (*cwd)
1673 chdir (cwd);
1674 signal (SIGINT, old_INT);
1675 signal (SIGQUIT, old_QUIT);
1676 break;
1677 default:
1678 if (sig)
1679 raise (sig);
1680 break;
1681 }
1682 return 0;
1683 }
1684 else
1685 return -1;
1686 }
1687
1688 /* These should never be called, but they make the linker happy. */
1689
tputs(const char * a,int b,int (* c)(int))1690 int tputs (const char *a, int b, int (*c)(int))
1691 {
1692 perror ("tputs"); return 0; /* here and below, added dummy retvals */
1693 }
1694
tgoto(const char * a,int b,int c)1695 char* tgoto (const char *a, int b, int c)
1696 {
1697 perror ("tgoto"); return 0;
1698 }
1699
tgetnum(char * a)1700 int tgetnum (char*a)
1701 {
1702 perror ("tgetnum"); return 0;
1703 }
1704
tgetflag(char * a)1705 int tgetflag (char*a)
1706 {
1707 perror ("tgetflag"); return 0;
1708 }
1709
tgetstr(char * a,char ** b)1710 char* tgetstr (char *a, char **b)
1711 {
1712 perror ("tgetstr"); return 0;
1713 }
1714
tgetent(char * a,const char * b)1715 int tgetent (char *a, const char *b)
1716 {
1717 perror ("tgetent"); return 0;
1718 }
1719
tcgetattr(int fildes,struct termios * termios_p)1720 int tcgetattr(int fildes, struct termios *termios_p)
1721 {
1722 perror ("tcgetattr"); return 0;
1723 }
1724
tcsetattr(int fd,int opt_actions,const struct termios * termios_p)1725 int tcsetattr(int fd, int opt_actions, const struct termios *termios_p)
1726 {
1727 perror ("tcsetattr"); return 0;
1728 }
1729