1 /* MS-DOS specific C utilities.          -*- coding: cp850 -*-
2 
3 Copyright (C) 1993-1997, 1999-2021 Free Software Foundation, Inc.
4 
5 This file is part of GNU Emacs.
6 
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or (at
10 your option) any later version.
11 
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
19 
20 /* Contributed by Morten Welinder */
21 /* New display, keyboard, and mouse control by Kim F. Storm */
22 
23 /* Note: This file MUST use a unibyte encoding, to both display the
24    keys on the non-US keyboard layout as their respective labels, and
25    provide the correct byte values for the keyboard input to inject
26    into Emacs.  See 'struct dos_keyboard_map' below.  As long as there
27    are only European keyboard layouts here, we are OK with DOS
28    codepage 850 encoding.  */
29 
30 /* Note: some of the stuff here was taken from end of sysdep.c in demacs. */
31 
32 #include <config.h>
33 
34 #ifdef MSDOS
35 #include <setjmp.h>
36 #include "lisp.h"
37 #include <stdio.h>
38 #include <time.h>
39 #include <sys/param.h>
40 #include <sys/time.h>
41 /* gettime and settime in dos.h clash with their namesakes from
42    gnulib, so we move out of our way the prototypes in dos.h.  */
43 #define gettime dos_h_gettime_
44 #define settime dos_h_settime_
45 #include <dos.h>
46 #undef gettime
47 #undef settime
48 #include <errno.h>
49 #include <sys/stat.h>    /* for _fixpath */
50 #include <unistd.h>	 /* for chdir, dup, dup2, etc. */
51 #include <dir.h>	 /* for getdisk */
52 #pragma pack(0)		 /* dir.h does a pack(4), which isn't GCC's default */
53 #undef opendir
54 #include <dirent.h>	 /* for opendir */
55 #include <fcntl.h>
56 #include <io.h>		 /* for setmode */
57 #include <dpmi.h>	 /* for __dpmi_xxx stuff */
58 #include <sys/farptr.h>	 /* for _farsetsel, _farnspokeb */
59 #include <libc/dosio.h>  /* for _USE_LFN */
60 #include <conio.h>	 /* for cputs */
61 
62 #if (__DJGPP__ + (__DJGPP_MINOR__ > 3)) >= 3
63 #define SYS_ENVIRON _environ
64 #else
65 #define SYS_ENVIRON environ
66 #endif
67 
68 #include "msdos.h"
69 #include "systime.h"
70 #include "frame.h"
71 #include "termhooks.h"
72 #include "termchar.h"
73 #include "dispextern.h"
74 #include "dosfns.h"
75 #include "termopts.h"
76 #include "character.h"
77 #include "coding.h"
78 #include "disptab.h"
79 #include "window.h"
80 #include "menu.h"
81 #include "buffer.h"
82 #include "commands.h"
83 #include "blockinput.h"
84 #include "keyboard.h"
85 #include "intervals.h"
86 #include <go32.h>
87 #include <pc.h>
88 #include <ctype.h>
89 /* #include <process.h> */
90 /* Damn that local process.h!  Instead we can define P_WAIT and
91    spawnve ourselves.  */
92 #define P_WAIT 1
93 extern int spawnve (int, const char *, char *const [], char *const []);
94 
95 #ifndef _USE_LFN
96 #define _USE_LFN 0
97 #endif
98 
99 #ifndef _dos_ds
100 #define _dos_ds _go32_info_block.selector_for_linear_memory
101 #endif
102 
103 #include <signal.h>
104 #include "syssignal.h"
105 
106 #include "careadlinkat.h"
107 #include "allocator.h"
108 
109 #ifndef SYSTEM_MALLOC
110 
111 #ifdef GNU_MALLOC
112 
113 /* If other `malloc' than ours is used, force our `sbrk' behave like
114    Unix programs expect (resize memory blocks to keep them contiguous).
115    If `sbrk' from `ralloc.c' is NOT used, also zero-out sbrk'ed memory,
116    because that's what `gmalloc' expects to get.  */
117 #include <crt0.h>
118 
119 #ifdef REL_ALLOC
120 int _crt0_startup_flags = _CRT0_FLAG_UNIX_SBRK;
121 #else  /* not REL_ALLOC */
122 int _crt0_startup_flags = (_CRT0_FLAG_UNIX_SBRK | _CRT0_FLAG_FILL_SBRK_MEMORY);
123 #endif /* not REL_ALLOC */
124 #endif /* GNU_MALLOC */
125 
126 #endif /* not SYSTEM_MALLOC */
127 
128 /* Return the current timestamp in milliseconds since midnight.  */
129 static unsigned long
event_timestamp(void)130 event_timestamp (void)
131 {
132   struct timespec t;
133   unsigned long s;
134 
135   gettime (&t);
136   s = t.tv_sec;
137   s %= 86400;
138   s *= 1000;
139   s += t.tv_nsec * 1000000;
140 
141   return s;
142 }
143 
144 
145 /* ------------------------ Mouse control ---------------------------
146  *
147  * Coordinates are in screen positions and zero based.
148  * Mouse buttons are numbered from left to right and also zero based.
149  */
150 
151 /* This used to be in termhooks.h, but mainstream Emacs code no longer
152    uses it, and it was removed...  */
153 #define NUM_MOUSE_BUTTONS (5)
154 
155 int have_mouse;          /* 0: no, 1: enabled, -1: disabled */
156 static int mouse_visible;
157 
158 static int mouse_last_x;
159 static int mouse_last_y;
160 
161 static int mouse_button_translate[NUM_MOUSE_BUTTONS];
162 static int mouse_button_count;
163 
164 void
mouse_on(void)165 mouse_on (void)
166 {
167   union REGS regs;
168 
169   if (have_mouse > 0 && !mouse_visible)
170     {
171       struct tty_display_info *tty = CURTTY ();
172 
173       if (tty->termscript)
174 	fprintf (tty->termscript, "<M_ON>");
175       regs.x.ax = 0x0001;
176       int86 (0x33, &regs, &regs);
177       mouse_visible = 1;
178     }
179 }
180 
181 void
mouse_off(void)182 mouse_off (void)
183 {
184   union REGS regs;
185 
186   if (have_mouse > 0 && mouse_visible)
187     {
188       struct tty_display_info *tty = CURTTY ();
189 
190       if (tty->termscript)
191 	fprintf (tty->termscript, "<M_OFF>");
192       regs.x.ax = 0x0002;
193       int86 (0x33, &regs, &regs);
194       mouse_visible = 0;
195     }
196 }
197 
198 static void
mouse_setup_buttons(int n_buttons)199 mouse_setup_buttons (int n_buttons)
200 {
201   if (n_buttons == 3)
202     {
203       mouse_button_count = 3;
204       mouse_button_translate[0] = 0; /* Left */
205       mouse_button_translate[1] = 2; /* Middle */
206       mouse_button_translate[2] = 1; /* Right */
207     }
208   else	/* two, what else? */
209     {
210       mouse_button_count = 2;
211       mouse_button_translate[0] = 0;
212       mouse_button_translate[1] = 1;
213     }
214 }
215 
216 DEFUN ("msdos-set-mouse-buttons", Fmsdos_set_mouse_buttons, Smsdos_set_mouse_buttons,
217        1, 1, "NSet number of mouse buttons to: ",
218        doc: /* Set the number of mouse buttons to use by Emacs.
219 This is useful with mice that report the number of buttons inconsistently,
220 e.g., if the number of buttons is reported as 3, but Emacs only sees 2 of
221 them.  This happens with wheeled mice on Windows 9X, for example.  */)
222   (Lisp_Object nbuttons)
223 {
224   int n;
225 
226   CHECK_FIXNUM (nbuttons);
227   n = XFIXNUM (nbuttons);
228   if (n < 2 || n > 3)
229     xsignal2 (Qargs_out_of_range,
230 	      build_string ("only 2 or 3 mouse buttons are supported"),
231 	      nbuttons);
232   mouse_setup_buttons (n);
233   return Qnil;
234 }
235 
236 static void
mouse_get_xy(int * x,int * y)237 mouse_get_xy (int *x, int *y)
238 {
239   union REGS regs;
240 
241   regs.x.ax = 0x0003;
242   int86 (0x33, &regs, &regs);
243   *x = regs.x.cx / 8;
244   *y = regs.x.dx / 8;
245 }
246 
247 void
mouse_moveto(int x,int y)248 mouse_moveto (int x, int y)
249 {
250   union REGS regs;
251   struct tty_display_info *tty = CURTTY ();
252 
253   if (tty->termscript)
254     fprintf (tty->termscript, "<M_XY=%dx%d>", x, y);
255   regs.x.ax = 0x0004;
256   mouse_last_x = regs.x.cx = x * 8;
257   mouse_last_y = regs.x.dx = y * 8;
258   int86 (0x33, &regs, &regs);
259 }
260 
261 static int
mouse_pressed(int b,int * xp,int * yp)262 mouse_pressed (int b, int *xp, int *yp)
263 {
264   union REGS regs;
265 
266   if (b >= mouse_button_count)
267     return 0;
268   regs.x.ax = 0x0005;
269   regs.x.bx = mouse_button_translate[b];
270   int86 (0x33, &regs, &regs);
271   if (regs.x.bx)
272     *xp = regs.x.cx / 8, *yp = regs.x.dx / 8;
273   return (regs.x.bx != 0);
274 }
275 
276 static int
mouse_released(int b,int * xp,int * yp)277 mouse_released (int b, int *xp, int *yp)
278 {
279   union REGS regs;
280 
281   if (b >= mouse_button_count)
282     return 0;
283   regs.x.ax = 0x0006;
284   regs.x.bx = mouse_button_translate[b];
285   int86 (0x33, &regs, &regs);
286   if (regs.x.bx)
287     *xp = regs.x.cx / 8, *yp = regs.x.dx / 8;
288   return (regs.x.bx != 0);
289 }
290 
291 static int
mouse_button_depressed(int b,int * xp,int * yp)292 mouse_button_depressed (int b, int *xp, int *yp)
293 {
294   union REGS regs;
295 
296   if (b >= mouse_button_count)
297     return 0;
298   regs.x.ax = 0x0003;
299   int86 (0x33, &regs, &regs);
300   if ((regs.x.bx & (1 << mouse_button_translate[b])) != 0)
301     {
302       *xp = regs.x.cx / 8;
303       *yp = regs.x.dx / 8;
304       return 1;
305     }
306   return 0;
307 }
308 
309 void
mouse_get_pos(struct frame ** f,int insist,Lisp_Object * bar_window,enum scroll_bar_part * part,Lisp_Object * x,Lisp_Object * y,Time * time)310 mouse_get_pos (struct frame **f, int insist, Lisp_Object *bar_window,
311 	       enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
312 	       Time *time)
313 {
314   int ix, iy;
315   Lisp_Object frame, tail;
316 
317   /* Clear the mouse-moved flag for every frame on this display.  */
318   FOR_EACH_FRAME (tail, frame)
319     XFRAME (frame)->mouse_moved = 0;
320 
321   *f = SELECTED_FRAME ();
322   *bar_window = Qnil;
323   mouse_get_xy (&ix, &iy);
324   *time = event_timestamp ();
325   *x = make_fixnum (mouse_last_x = ix);
326   *y = make_fixnum (mouse_last_y = iy);
327 }
328 
329 static void
mouse_check_moved(void)330 mouse_check_moved (void)
331 {
332   int x, y;
333 
334   mouse_get_xy (&x, &y);
335   SELECTED_FRAME ()->mouse_moved |= (x != mouse_last_x || y != mouse_last_y);
336   mouse_last_x = x;
337   mouse_last_y = y;
338 }
339 
340 /* Force the mouse driver to ``forget'' about any button clicks until
341    now.  */
342 static void
mouse_clear_clicks(void)343 mouse_clear_clicks (void)
344 {
345   int b;
346 
347   for (b = 0; b < mouse_button_count; b++)
348     {
349       int dummy_x, dummy_y;
350 
351       (void) mouse_pressed (b, &dummy_x, &dummy_y);
352       (void) mouse_released (b, &dummy_x, &dummy_y);
353     }
354 }
355 
356 void
mouse_init(void)357 mouse_init (void)
358 {
359   union REGS regs;
360   struct tty_display_info *tty = CURTTY ();
361 
362   if (tty->termscript)
363     fprintf (tty->termscript, "<M_INIT>");
364 
365   regs.x.ax = 0x0021;
366   int86 (0x33, &regs, &regs);
367 
368   /* Reset the mouse last press/release info.  It seems that Windows
369      doesn't do that automatically when function 21h is called, which
370      causes Emacs to ``remember'' the click that switched focus to the
371      window just before Emacs was started from that window.  */
372   mouse_clear_clicks ();
373 
374   regs.x.ax = 0x0007;
375   regs.x.cx = 0;
376   regs.x.dx = 8 * (ScreenCols () - 1);
377   int86 (0x33, &regs, &regs);
378 
379   regs.x.ax = 0x0008;
380   regs.x.cx = 0;
381   regs.x.dx = 8 * (ScreenRows () - 1);
382   int86 (0x33, &regs, &regs);
383 
384   mouse_moveto (0, 0);
385   mouse_visible = 0;
386 }
387 
388 /* ------------------------- Screen control ----------------------
389  *
390  */
391 
392 static int internal_terminal = 0;
393 
394 #ifndef HAVE_X_WINDOWS
395 extern unsigned char ScreenAttrib;
396 static int screen_face;
397 
398 static int screen_size_X;
399 static int screen_size_Y;
400 static int screen_size;
401 
402 static int current_pos_X;
403 static int current_pos_Y;
404 static int new_pos_X;
405 static int new_pos_Y;
406 
407 static void *startup_screen_buffer;
408 static int startup_screen_size_X;
409 static int startup_screen_size_Y;
410 static int startup_pos_X;
411 static int startup_pos_Y;
412 static unsigned char startup_screen_attrib;
413 
414 static clock_t startup_time;
415 
416 static int term_setup_done;
417 
418 static unsigned short outside_cursor;
419 
420 /* The only display since MS-DOS does not support multiple ones.  */
421 struct tty_display_info the_only_display_info;
422 
423 /* The only tty_output, since MS-DOS supports only 1 display.  */
424 struct tty_output the_only_tty_output;
425 
426 /* Support for DOS/V (allows Japanese characters to be displayed on
427    standard, non-Japanese, ATs).  Only supported for DJGPP v2 and later.  */
428 
429 /* Holds the address of the text-mode screen buffer.  */
430 static unsigned long screen_old_address = 0;
431 /* Segment and offset of the virtual screen.  If 0, DOS/V is NOT loaded.  */
432 static unsigned short screen_virtual_segment = 0;
433 static unsigned short screen_virtual_offset = 0;
434 
435 /* The screen colors of the current frame, which serve as the default
436    colors for newly-created frames.  */
437 static int initial_screen_colors[2];
438 
439 /* Update the screen from a part of relocated DOS/V screen buffer which
440    begins at OFFSET and includes COUNT characters.  */
441 static void
dosv_refresh_virtual_screen(int offset,int count)442 dosv_refresh_virtual_screen (int offset, int count)
443 {
444   __dpmi_regs regs;
445 
446   if (offset < 0 || count < 0)	/* paranoia; invalid values crash DOS/V */
447     return;
448 
449   regs.h.ah = 0xff;	/* update relocated screen */
450   regs.x.es = screen_virtual_segment;
451   regs.x.di = screen_virtual_offset + offset;
452   regs.x.cx = count;
453   __dpmi_int (0x10, &regs);
454 }
455 
456 static void
dos_direct_output(int y,int x,char * buf,int len)457 dos_direct_output (int y, int x, char *buf, int len)
458 {
459   int t0 = 2 * (x + y * screen_size_X);
460   int t = t0 + (int) ScreenPrimary;
461   int l0 = len;
462 
463   /* This is faster.  */
464   for (_farsetsel (_dos_ds); --len >= 0; t += 2, buf++)
465     _farnspokeb (t, *buf);
466 
467   if (screen_virtual_segment)
468     dosv_refresh_virtual_screen (t0, l0);
469 }
470 #endif
471 
472 #ifndef HAVE_X_WINDOWS
473 
474 static int blink_bit = -1;	/* the state of the blink bit at startup */
475 
476 /* Enable bright background colors.  */
477 static void
bright_bg(void)478 bright_bg (void)
479 {
480   union REGS regs;
481 
482   /* Remember the original state of the blink/bright-background bit.
483      It is stored at 0040:0065h in the BIOS data area.  */
484   if (blink_bit == -1)
485     blink_bit = (_farpeekb (_dos_ds, 0x465) & 0x20) == 0x20;
486 
487   regs.h.bl = 0;
488   regs.x.ax = 0x1003;
489   int86 (0x10, &regs, &regs);
490 }
491 
492 /* Disable bright background colors (and enable blinking) if we found
493    the video system in that state at startup.  */
494 static void
maybe_enable_blinking(void)495 maybe_enable_blinking (void)
496 {
497   if (blink_bit == 1)
498     {
499       union REGS regs;
500 
501       regs.h.bl = 1;
502       regs.x.ax = 0x1003;
503       int86 (0x10, &regs, &regs);
504     }
505 }
506 
507 /* Return non-zero if the system has a VGA adapter.  */
508 static int
vga_installed(void)509 vga_installed (void)
510 {
511   union REGS regs;
512 
513   regs.x.ax = 0x1a00;
514   int86 (0x10, &regs, &regs);
515   if (regs.h.al == 0x1a && regs.h.bl > 5 && regs.h.bl < 13)
516     return 1;
517   return 0;
518 }
519 
520 /* Set the screen dimensions so that it can show no less than
521    ROWS x COLS frame.  */
522 
523 void
dos_set_window_size(int * rows,int * cols)524 dos_set_window_size (int *rows, int *cols)
525 {
526   char video_name[30];
527   union REGS regs;
528   Lisp_Object video_mode;
529   int video_mode_value, have_vga = 0;
530   int current_rows = ScreenRows (), current_cols = ScreenCols ();
531 
532   if (*rows == current_rows && *cols == current_cols)
533     return;
534 
535   mouse_off ();
536   have_vga = vga_installed ();
537 
538   /* If the user specified a special video mode for these dimensions,
539      use that mode.  */
540   video_mode
541     = Fsymbol_value (Fintern_soft (make_formatted_string
542 				   (video_name, "screen-dimensions-%dx%d",
543 				    *rows, *cols), Qnil));
544 
545   if (FIXNUMP (video_mode)
546       && (video_mode_value = XFIXNUM (video_mode)) > 0)
547     {
548       regs.x.ax = video_mode_value;
549       int86 (0x10, &regs, &regs);
550 
551       if (have_mouse)
552 	{
553 	  /* Must hardware-reset the mouse, or else it won't update
554 	     its notion of screen dimensions for some non-standard
555 	     video modes.  This is *painfully* slow...  */
556 	  regs.x.ax = 0;
557 	  int86 (0x33, &regs, &regs);
558 	}
559     }
560 
561   /* Find one of the dimensions supported by standard EGA/VGA
562      which gives us at least the required dimensions.  */
563   else
564     {
565       static struct {
566 	int rows, need_vga;
567       }	std_dimension[] = {
568 	  {25, 0},
569 	  {28, 1},
570 	  {35, 0},
571 	  {40, 1},
572 	  {43, 0},
573 	  {50, 1}
574       };
575       int i = 0;
576 
577       while (i < ARRAYELTS (std_dimension))
578 	{
579 	 if (std_dimension[i].need_vga <= have_vga
580 	     && std_dimension[i].rows >= *rows)
581 	   {
582 	     if (std_dimension[i].rows != current_rows
583 		 || *cols != current_cols)
584 	       _set_screen_lines (std_dimension[i].rows);
585 	     break;
586 	   }
587 	 i++;
588 	}
589     }
590 
591 
592   if (have_mouse)
593     {
594       mouse_init ();
595       mouse_on ();
596     }
597 
598   /* Tell the caller what dimensions have been REALLY set.  */
599   *rows = ScreenRows ();
600   *cols = ScreenCols ();
601 
602   /* Update Emacs' notion of screen dimensions.  */
603   screen_size_X = *cols;
604   screen_size_Y = *rows;
605   screen_size = *cols * *rows;
606 
607   /* If the dimensions changed, the mouse highlight info is invalid.  */
608   if (current_rows != *rows || current_cols != *cols)
609     {
610       struct frame *f = SELECTED_FRAME ();
611       Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
612       Lisp_Object window = hlinfo->mouse_face_window;
613 
614       if (! NILP (window) && XFRAME (XWINDOW (window)->frame) == f)
615 	reset_mouse_highlight (hlinfo);
616     }
617 
618   /* Enable bright background colors.  */
619   bright_bg ();
620 
621   /* FIXME: I'm not sure the above will run at all on DOS/V.  But let's
622      be defensive anyway.  */
623   if (screen_virtual_segment)
624     dosv_refresh_virtual_screen (0, *cols * *rows);
625 }
626 
627 /* If we write a character in the position where the mouse is,
628    the mouse cursor may need to be refreshed.  */
629 
630 static void
mouse_off_maybe(void)631 mouse_off_maybe (void)
632 {
633   int x, y;
634 
635   if (!mouse_visible)
636     return;
637 
638   mouse_get_xy (&x, &y);
639   if (y != new_pos_Y || x < new_pos_X)
640     return;
641 
642   mouse_off ();
643 }
644 
645 #define DEFAULT_CURSOR_START (-1)
646 #define DEFAULT_CURSOR_WIDTH (-1)
647 #define BOX_CURSOR_WIDTH     (-32)
648 
649 /* Set cursor to begin at scan line START_LINE in the character cell
650    and extend for WIDTH scan lines.  Scan lines are counted from top
651    of the character cell, starting from zero.  */
652 static void
msdos_set_cursor_shape(struct frame * f,int start_line,int width)653 msdos_set_cursor_shape (struct frame *f, int start_line, int width)
654 {
655   unsigned desired_cursor;
656   __dpmi_regs regs;
657   int max_line, top_line, bot_line;
658   struct tty_display_info *tty = FRAME_TTY (f);
659 
660   /* Avoid the costly BIOS call if F isn't the currently selected
661      frame.  Allow for NULL as unconditionally meaning the selected
662      frame.  */
663   if (f && f != SELECTED_FRAME ())
664     return;
665 
666   if (tty->termscript)
667     fprintf (tty->termscript, "\nCURSOR SHAPE=(%d,%d)", start_line, width);
668 
669   /* The character cell size in scan lines is stored at 40:85 in the
670      BIOS data area.  */
671   max_line = _farpeekw (_dos_ds, 0x485) - 1;
672   switch (max_line)
673     {
674       default:	/* this relies on CGA cursor emulation being ON! */
675       case 7:
676 	bot_line = 7;
677 	break;
678       case 9:
679 	bot_line = 9;
680 	break;
681       case 13:
682 	bot_line = 12;
683 	break;
684       case 15:
685 	bot_line = 14;
686 	break;
687     }
688 
689   if (width < 0)
690     {
691       if (width == BOX_CURSOR_WIDTH)
692 	{
693 	  top_line = 0;
694 	  bot_line = max_line;
695 	}
696       else if (start_line != DEFAULT_CURSOR_START)
697 	{
698 	  top_line = start_line;
699 	  bot_line = top_line - width - 1;
700 	}
701       else if (width != DEFAULT_CURSOR_WIDTH)
702 	{
703 	  top_line = 0;
704 	  bot_line = -1 - width;
705 	}
706       else
707 	top_line = bot_line + 1;
708     }
709   else if (width == 0)
710     {
711       /* [31, 0] seems to DTRT for all screen sizes.  */
712       top_line = 31;
713       bot_line = 0;
714     }
715   else	/* WIDTH is positive */
716     {
717       if (start_line != DEFAULT_CURSOR_START)
718 	bot_line = start_line;
719       top_line = bot_line - (width - 1);
720     }
721 
722   /* If the current cursor shape is already what they want, we are
723      history here.  */
724   desired_cursor = ((top_line & 0x1f) << 8) | (bot_line & 0x1f);
725   if (desired_cursor == _farpeekw (_dos_ds, 0x460))
726     return;
727 
728   regs.h.ah = 1;
729   regs.x.cx = desired_cursor;
730   __dpmi_int (0x10, &regs);
731 }
732 
733 static void
IT_set_cursor_type(struct frame * f,Lisp_Object cursor_type)734 IT_set_cursor_type (struct frame *f, Lisp_Object cursor_type)
735 {
736   if (EQ (cursor_type, Qbar) || EQ (cursor_type, Qhbar))
737     {
738       /* Just BAR means the normal EGA/VGA cursor.  */
739       msdos_set_cursor_shape (f, DEFAULT_CURSOR_START, DEFAULT_CURSOR_WIDTH);
740     }
741   else if (CONSP (cursor_type)
742 	   && (EQ (XCAR (cursor_type), Qbar)
743 	       || EQ (XCAR (cursor_type), Qhbar)))
744     {
745       Lisp_Object bar_parms = XCDR (cursor_type);
746       int width;
747 
748       if (FIXNUMP (bar_parms))
749 	{
750 	  /* Feature: negative WIDTH means cursor at the top
751 	     of the character cell, zero means invisible cursor.  */
752 	  width = XFIXNUM (bar_parms);
753 	  msdos_set_cursor_shape (f, width >= 0 ? DEFAULT_CURSOR_START : 0,
754 				  width);
755 	}
756       else if (CONSP (bar_parms)
757 	       && FIXNUMP (XCAR (bar_parms))
758 	       && FIXNUMP (XCDR (bar_parms)))
759 	{
760 	  int start_line = XFIXNUM (XCDR (bar_parms));
761 
762 	  width = XFIXNUM (XCAR (bar_parms));
763 	  msdos_set_cursor_shape (f, start_line, width);
764 	}
765     }
766   else
767     {
768       /* Treat anything unknown as "box cursor".  This includes nil, so
769 	 that a frame which doesn't specify a cursor type gets a box,
770 	 which is the default in Emacs.  */
771       msdos_set_cursor_shape (f, 0, BOX_CURSOR_WIDTH);
772     }
773 }
774 
775 static void
IT_ring_bell(struct frame * f)776 IT_ring_bell (struct frame *f)
777 {
778   if (visible_bell)
779     {
780       mouse_off ();
781       ScreenVisualBell ();
782     }
783   else
784     {
785       union REGS inregs, outregs;
786       inregs.h.ah = 2;
787       inregs.h.dl = 7;
788       intdos (&inregs, &outregs);
789     }
790 }
791 
792 /* Given a face id FACE, extract the face parameters to be used for
793    display until the face changes.  The face parameters (actually, its
794    color) are used to construct the video attribute byte for each
795    glyph during the construction of the buffer that is then blitted to
796    the video RAM.  */
797 static void
IT_set_face(int face)798 IT_set_face (int face)
799 {
800   struct frame *sf = SELECTED_FRAME ();
801   struct face *fp  = FACE_FROM_ID_OR_NULL (sf, face);
802   struct face *dfp = FACE_FROM_ID_OR_NULL (sf, DEFAULT_FACE_ID);
803   unsigned long fg, bg, dflt_fg, dflt_bg;
804   struct tty_display_info *tty = FRAME_TTY (sf);
805 
806   if (!fp)
807     {
808       fp = dfp;
809       /* The default face for the frame should always be realized and
810 	 cached.  */
811       if (!fp)
812 	emacs_abort ();
813     }
814   screen_face = face;
815   fg = fp->foreground;
816   bg = fp->background;
817   dflt_fg = dfp->foreground;
818   dflt_bg = dfp->background;
819 
820   /* Don't use invalid colors.  In particular, FACE_TTY_DEFAULT_* colors
821      mean use the colors of the default face.  Note that we assume all
822      16 colors to be available for the background, since Emacs switches
823      on this mode (and loses the blinking attribute) at startup.  */
824   if (fg == FACE_TTY_DEFAULT_COLOR || fg == FACE_TTY_DEFAULT_FG_COLOR)
825     fg = FRAME_FOREGROUND_PIXEL (sf);
826   else if (fg == FACE_TTY_DEFAULT_BG_COLOR)
827     fg = FRAME_BACKGROUND_PIXEL (sf);
828   if (bg == FACE_TTY_DEFAULT_COLOR || bg == FACE_TTY_DEFAULT_BG_COLOR)
829     bg = FRAME_BACKGROUND_PIXEL (sf);
830   else if (bg == FACE_TTY_DEFAULT_FG_COLOR)
831     bg = FRAME_FOREGROUND_PIXEL (sf);
832 
833   /* Make sure highlighted lines really stand out, come what may.  */
834   if (fp->tty_reverse_p && (fg == dflt_fg && bg == dflt_bg))
835     {
836       unsigned long tem = fg;
837 
838       fg = bg;
839       bg = tem;
840     }
841   /* If the user requested inverse video, obey.  */
842   if (inverse_video)
843     {
844       unsigned long tem2 = fg;
845 
846       fg = bg;
847       bg = tem2;
848     }
849   if (tty->termscript)
850     fprintf (tty->termscript, "<FACE %d: %lu/%lu[FG:%lu/BG:%lu]>", face,
851 	     fp->foreground, fp->background, fg, bg);
852   if (fg >= 0 && fg < 16)
853     {
854       ScreenAttrib &= 0xf0;
855       ScreenAttrib |= fg;
856     }
857   if (bg >= 0 && bg < 16)
858     {
859       ScreenAttrib &= 0x0f;
860       ScreenAttrib |= ((bg & 0x0f) << 4);
861     }
862 }
863 
864 /* According to RBIL (INTERRUP.A, V-1000), 160 is the maximum possible
865    width of a DOS display in any known text mode.  We multiply by 2 to
866    accommodate the screen attribute byte.  */
867 #define MAX_SCREEN_BUF 160*2
868 
869 extern unsigned char *encode_terminal_code (struct glyph *, int,
870 					    struct coding_system *);
871 
872 static void
IT_write_glyphs(struct frame * f,struct glyph * str,int str_len)873 IT_write_glyphs (struct frame *f, struct glyph *str, int str_len)
874 {
875   unsigned char screen_buf[MAX_SCREEN_BUF], *screen_bp, *bp;
876   int offset = 2 * (new_pos_X + screen_size_X * new_pos_Y);
877   register int sl = str_len;
878   struct tty_display_info *tty = FRAME_TTY (f);
879   struct frame *sf;
880   unsigned char *conversion_buffer;
881 
882   /* If terminal_coding does any conversion, use it, otherwise use
883      safe_terminal_coding.  We can't use CODING_REQUIRE_ENCODING here
884      because it always returns 1 if terminal_coding.src_multibyte is 1.  */
885   struct coding_system *coding = FRAME_TERMINAL_CODING (f);
886 
887   if (!(coding->common_flags & CODING_REQUIRE_ENCODING_MASK))
888     coding = &safe_terminal_coding;
889 
890   if (str_len <= 0) return;
891 
892   sf = SELECTED_FRAME ();
893 
894   /* Since faces get cached and uncached behind our back, we can't
895      rely on their indices in the cache being consistent across
896      invocations.  So always reset the screen face to the default
897      face of the frame, before writing glyphs, and let the glyphs
898      set the right face if it's different from the default.  */
899   IT_set_face (DEFAULT_FACE_ID);
900 
901   /* The mode bit CODING_MODE_LAST_BLOCK should be set to 1 only at
902      the tail.  */
903   coding->mode &= ~CODING_MODE_LAST_BLOCK;
904   screen_bp = &screen_buf[0];
905   while (sl > 0)
906     {
907       int cf;
908       int n;
909 
910       /* If the face of this glyph is different from the current
911 	 screen face, update the screen attribute byte.  */
912       cf = str->face_id;
913       if (cf != screen_face)
914 	IT_set_face (cf);	/* handles invalid faces gracefully */
915 
916       /* Identify a run of glyphs with the same face.  */
917       for (n = 1; n < sl; ++n)
918 	if (str[n].face_id != cf)
919 	  break;
920 
921       if (n >= sl)
922 	/* This is the last glyph.  */
923 	coding->mode |= CODING_MODE_LAST_BLOCK;
924 
925       conversion_buffer = encode_terminal_code (str, n, coding);
926       if (coding->produced > 0)
927 	{
928 	  /* Copy the encoded bytes to the screen buffer.  */
929 	  for (bp = conversion_buffer; coding->produced--; bp++)
930 	    {
931 	      /* Paranoia: discard bytes that would overrun the end of
932 		 the screen buffer.  */
933 	      if (screen_bp - screen_buf <= MAX_SCREEN_BUF - 2)
934 		{
935 		  *screen_bp++ = (unsigned char)*bp;
936 		  *screen_bp++ = ScreenAttrib;
937 		}
938 	      if (tty->termscript)
939 		fputc (*bp, tty->termscript);
940 	    }
941 	}
942       /* Update STR and its remaining length.  */
943       str += n;
944       sl -= n;
945     }
946 
947   /* Dump whatever we have in the screen buffer.  */
948   mouse_off_maybe ();
949   dosmemput (screen_buf, screen_bp - screen_buf, (int)ScreenPrimary + offset);
950   if (screen_virtual_segment)
951     dosv_refresh_virtual_screen (offset, (screen_bp - screen_buf) / 2);
952   new_pos_X += (screen_bp - screen_buf) / 2;
953 }
954 
955 /************************************************************************
956 			  Mouse Highlight (and friends..)
957  ************************************************************************/
958 
959 static int mouse_preempted = 0;	/* non-zero when XMenu gobbles mouse events */
960 
961 int
popup_activated(void)962 popup_activated (void)
963 {
964   return mouse_preempted;
965 }
966 
967 /* Draw TEXT_AREA glyphs between START and END of glyph row ROW on
968    window W.  X is relative to TEXT_AREA in W.  HL is a face override
969    for drawing the glyphs.  */
970 void
tty_draw_row_with_mouse_face(struct window * w,struct glyph_row * row,int start_hpos,int end_hpos,enum draw_glyphs_face hl)971 tty_draw_row_with_mouse_face (struct window *w, struct glyph_row *row,
972 			      int start_hpos, int end_hpos,
973 			      enum draw_glyphs_face hl)
974 {
975   struct frame *f = XFRAME (WINDOW_FRAME (w));
976   struct tty_display_info *tty = FRAME_TTY (f);
977   Mouse_HLInfo *hlinfo = &tty->mouse_highlight;
978 
979   if (hl == DRAW_MOUSE_FACE)
980     {
981       int vpos = row->y + WINDOW_TOP_EDGE_Y (w);
982       int kstart = start_hpos + WINDOW_LEFT_EDGE_X (w);
983       int nglyphs = end_hpos - start_hpos;
984       int offset = ScreenPrimary + 2*(vpos*screen_size_X + kstart) + 1;
985       int start_offset = offset;
986 
987       if (tty->termscript)
988 	fprintf (tty->termscript, "\n<MH+ %d-%d:%d>",
989 		 kstart, kstart + nglyphs - 1, vpos);
990 
991       mouse_off ();
992       IT_set_face (hlinfo->mouse_face_face_id);
993       /* Since we are going to change only the _colors_ of already
994 	 displayed text, there's no need to go through all the pain of
995 	 generating and encoding the text from the glyphs.  Instead,
996 	 we simply poke the attribute byte of each affected position
997 	 in video memory with the colors computed by IT_set_face!  */
998       _farsetsel (_dos_ds);
999       while (nglyphs--)
1000 	{
1001 	  _farnspokeb (offset, ScreenAttrib);
1002 	  offset += 2;
1003 	}
1004       if (screen_virtual_segment)
1005 	dosv_refresh_virtual_screen (start_offset, end_hpos - start_hpos);
1006       mouse_on ();
1007     }
1008   else if (hl == DRAW_NORMAL_TEXT)
1009     {
1010       /* We are removing a previously-drawn mouse highlight.  The
1011 	 safest way to do so is to redraw the glyphs anew, since all
1012 	 kinds of faces and display tables could have changed behind
1013 	 our back.  */
1014       int nglyphs = end_hpos - start_hpos;
1015       int save_x = new_pos_X, save_y = new_pos_Y;
1016 
1017       if (end_hpos >= row->used[TEXT_AREA])
1018 	nglyphs = row->used[TEXT_AREA] - start_hpos;
1019 
1020       /* IT_write_glyphs writes at cursor position, so we need to
1021 	 temporarily move cursor coordinates to the beginning of
1022 	 the highlight region.  */
1023       new_pos_X = start_hpos + WINDOW_LEFT_EDGE_X (w);
1024       new_pos_Y = row->y + WINDOW_TOP_EDGE_Y (w);
1025 
1026       if (tty->termscript)
1027 	fprintf (tty->termscript, "<MH- %d-%d:%d>",
1028 		 new_pos_X, new_pos_X + nglyphs - 1, new_pos_Y);
1029       IT_write_glyphs (f, row->glyphs[TEXT_AREA] + start_hpos, nglyphs);
1030       if (tty->termscript)
1031 	fputs ("\n", tty->termscript);
1032       new_pos_X = save_x;
1033       new_pos_Y = save_y;
1034     }
1035 }
1036 
1037 static void
IT_clear_end_of_line(struct frame * f,int first_unused)1038 IT_clear_end_of_line (struct frame *f, int first_unused)
1039 {
1040   char *spaces, *sp;
1041   int i, j, offset = 2 * (new_pos_X + screen_size_X * new_pos_Y);
1042   struct tty_display_info *tty = FRAME_TTY (f);
1043 
1044   if (new_pos_X >= first_unused || fatal_error_in_progress)
1045     return;
1046 
1047   IT_set_face (0);
1048   i = (j = first_unused - new_pos_X) * 2;
1049   if (tty->termscript)
1050     fprintf (tty->termscript, "<CLR:EOL[%d..%d)>", new_pos_X, first_unused);
1051   spaces = sp = alloca (i);
1052 
1053   while (--j >= 0)
1054     {
1055       *sp++ = ' ';
1056       *sp++ = ScreenAttrib;
1057     }
1058 
1059   mouse_off_maybe ();
1060   dosmemput (spaces, i, (int)ScreenPrimary + offset);
1061   if (screen_virtual_segment)
1062     dosv_refresh_virtual_screen (offset, i / 2);
1063 
1064   /* clear_end_of_line_raw on term.c leaves the cursor at first_unused.
1065      Let's follow their lead, in case someone relies on this.  */
1066   new_pos_X = first_unused;
1067 }
1068 
1069 static void
IT_clear_screen(struct frame * f)1070 IT_clear_screen (struct frame *f)
1071 {
1072   struct tty_display_info *tty = FRAME_TTY (f);
1073 
1074   if (tty->termscript)
1075     fprintf (tty->termscript, "<CLR:SCR>");
1076   /* We are sometimes called (from clear_garbaged_frames) when a new
1077      frame is being created, but its faces are not yet realized.  In
1078      such a case we cannot call IT_set_face, since it will fail to find
1079      any valid faces and will abort.  Instead, use the initial screen
1080      colors; that should mimic what a Unix tty does, which simply clears
1081      the screen with whatever default colors are in use.  */
1082   if (FACE_FROM_ID_OR_NULL (SELECTED_FRAME (), DEFAULT_FACE_ID) == NULL)
1083     ScreenAttrib = (initial_screen_colors[0] << 4) | initial_screen_colors[1];
1084   else
1085     IT_set_face (0);
1086   mouse_off ();
1087   ScreenClear ();
1088   if (screen_virtual_segment)
1089     dosv_refresh_virtual_screen (0, screen_size);
1090   new_pos_X = new_pos_Y = 0;
1091 }
1092 
1093 static void
IT_clear_to_end(struct frame * f)1094 IT_clear_to_end (struct frame *f)
1095 {
1096   struct tty_display_info *tty = FRAME_TTY (f);
1097 
1098   if (tty->termscript)
1099     fprintf (tty->termscript, "<CLR:EOS>");
1100 
1101   while (new_pos_Y < screen_size_Y) {
1102     new_pos_X = 0;
1103     IT_clear_end_of_line (f, screen_size_X);
1104     new_pos_Y++;
1105   }
1106 }
1107 
1108 static void
IT_cursor_to(struct frame * f,int y,int x)1109 IT_cursor_to (struct frame *f, int y, int x)
1110 {
1111   struct tty_display_info *tty = FRAME_TTY (f);
1112 
1113   if (tty->termscript)
1114     fprintf (tty->termscript, "\n<XY=%dx%d>", x, y);
1115   new_pos_X = x;
1116   new_pos_Y = y;
1117 }
1118 
1119 static int cursor_cleared;
1120 
1121 static void
IT_display_cursor(int on)1122 IT_display_cursor (int on)
1123 {
1124   struct tty_display_info *tty = CURTTY ();
1125 
1126   if (on && cursor_cleared)
1127     {
1128       ScreenSetCursor (current_pos_Y, current_pos_X);
1129       cursor_cleared = 0;
1130       if (tty->termscript)
1131 	fprintf (tty->termscript, "\nCURSOR ON (%dx%d)",
1132 		 current_pos_Y, current_pos_X);
1133     }
1134   else if (!on && !cursor_cleared)
1135     {
1136       ScreenSetCursor (-1, -1);
1137       cursor_cleared = 1;
1138       if (tty->termscript)
1139 	fprintf (tty->termscript, "\nCURSOR OFF (%dx%d)",
1140 		 current_pos_Y, current_pos_X);
1141     }
1142 }
1143 
1144 /* Emacs calls cursor-movement functions a lot when it updates the
1145    display (probably a legacy of old terminals where you cannot
1146    update a screen line without first moving the cursor there).
1147    However, cursor movement is expensive on MSDOS (it calls a slow
1148    BIOS function and requires 2 mode switches), while actual screen
1149    updates access the video memory directly and don't depend on
1150    cursor position.  To avoid slowing down the redisplay, we cheat:
1151    all functions that move the cursor only set internal variables
1152    which record the cursor position, whereas the cursor is only
1153    moved to its final position whenever screen update is complete.
1154 
1155    `IT_cmgoto' is called from the keyboard reading loop and when the
1156    frame update is complete.  This means that we are ready for user
1157    input, so we update the cursor position to show where the point is,
1158    and also make the mouse pointer visible.
1159 
1160    Special treatment is required when the cursor is in the echo area,
1161    to put the cursor at the end of the text displayed there.  */
1162 
1163 static void
IT_cmgoto(struct frame * f)1164 IT_cmgoto (struct frame *f)
1165 {
1166   /* Only set the cursor to where it should be if the display is
1167      already in sync with the window contents.  */
1168   int update_cursor_pos = 1; /* MODIFF == unchanged_modified; */
1169   struct tty_display_info *tty = FRAME_TTY (f);
1170 
1171   /* FIXME: This needs to be rewritten for the new redisplay, or
1172      removed.  */
1173 #if 0
1174   static int previous_pos_X = -1;
1175 
1176   update_cursor_pos = 1;	/* temporary!!! */
1177 
1178   /* If the display is in sync, forget any previous knowledge about
1179      cursor position.  This is primarily for unexpected events like
1180      C-g in the minibuffer.  */
1181   if (update_cursor_pos && previous_pos_X >= 0)
1182     previous_pos_X = -1;
1183   /* If we are in the echo area, put the cursor at the
1184      end of the echo area message.  */
1185   if (!update_cursor_pos
1186       && WINDOW_TOP_EDGE_LINE (XWINDOW (FRAME_MINIBUF_WINDOW (f))) <= new_pos_Y)
1187     {
1188       int tem_X = current_pos_X, dummy;
1189 
1190       if (echo_area_glyphs)
1191 	{
1192 	  tem_X = echo_area_glyphs_length;
1193 	  /* Save current cursor position, to be restored after the
1194 	     echo area message is erased.  Only remember one level
1195 	     of previous cursor position.  */
1196 	  if (previous_pos_X == -1)
1197 	    ScreenGetCursor (&dummy, &previous_pos_X);
1198 	}
1199       else if (previous_pos_X >= 0)
1200 	{
1201 	  /* We wind up here after the echo area message is erased.
1202 	     Restore the cursor position we remembered above.  */
1203 	  tem_X = previous_pos_X;
1204 	  previous_pos_X = -1;
1205 	}
1206 
1207       if (current_pos_X != tem_X)
1208 	{
1209 	  new_pos_X = tem_X;
1210 	  update_cursor_pos = 1;
1211 	}
1212     }
1213 #endif
1214 
1215   if (update_cursor_pos
1216       && (current_pos_X != new_pos_X || current_pos_Y != new_pos_Y))
1217     {
1218       ScreenSetCursor (current_pos_Y = new_pos_Y, current_pos_X = new_pos_X);
1219       if (tty->termscript)
1220 	fprintf (tty->termscript, "\n<CURSOR:%dx%d>", current_pos_X, current_pos_Y);
1221     }
1222 
1223   /* Maybe cursor is invisible, so make it visible.  */
1224   IT_display_cursor (1);
1225 
1226   /* Mouse pointer should be always visible if we are waiting for
1227      keyboard input.  */
1228   if (!mouse_visible)
1229     mouse_on ();
1230 }
1231 
1232 static void
IT_update_begin(struct frame * f)1233 IT_update_begin (struct frame *f)
1234 {
1235   struct tty_display_info *display_info = FRAME_DISPLAY_INFO (f);
1236   Mouse_HLInfo *hlinfo = &display_info->mouse_highlight;
1237   struct frame *mouse_face_frame = hlinfo->mouse_face_mouse_frame;
1238 
1239   if (display_info->termscript)
1240     fprintf (display_info->termscript, "\n\n<UPDATE_BEGIN");
1241 
1242   block_input ();
1243 
1244   if (f && f == mouse_face_frame)
1245     {
1246       /* Don't do highlighting for mouse motion during the update.  */
1247       hlinfo->mouse_face_defer = 1;
1248 
1249       /* If F needs to be redrawn, simply forget about any prior mouse
1250 	 highlighting.  */
1251       if (FRAME_GARBAGED_P (f))
1252 	hlinfo->mouse_face_window = Qnil;
1253 
1254       /* Can we tell that this update does not affect the window
1255 	 where the mouse highlight is?  If so, no need to turn off.
1256 	 Likewise, don't do anything if none of the enabled rows
1257 	 contains glyphs highlighted in mouse face.  */
1258       if (!NILP (hlinfo->mouse_face_window)
1259 	  && WINDOWP (hlinfo->mouse_face_window))
1260 	{
1261 	  struct window *w = XWINDOW (hlinfo->mouse_face_window);
1262 	  int i;
1263 
1264 	  /* If the mouse highlight is in the window that was deleted
1265 	     (e.g., if it was popped by completion), clear highlight
1266 	     unconditionally.  */
1267 	  if (NILP (w->contents))
1268 	    hlinfo->mouse_face_window = Qnil;
1269 	  else
1270 	    {
1271 	      for (i = 0; i < w->desired_matrix->nrows; ++i)
1272 		if (MATRIX_ROW_ENABLED_P (w->desired_matrix, i)
1273 		    && MATRIX_ROW (w->current_matrix, i)->mouse_face_p)
1274 		  break;
1275 	    }
1276 
1277 	  if (NILP (w->contents) || i < w->desired_matrix->nrows)
1278 	    clear_mouse_face (hlinfo);
1279 	}
1280     }
1281   else if (mouse_face_frame && !FRAME_LIVE_P (mouse_face_frame))
1282     /* If the frame with mouse highlight was deleted, invalidate the
1283        highlight info.  */
1284     reset_mouse_highlight (hlinfo);
1285 
1286   unblock_input ();
1287 }
1288 
1289 static void
IT_update_end(struct frame * f)1290 IT_update_end (struct frame *f)
1291 {
1292   struct tty_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
1293 
1294   if (dpyinfo->termscript)
1295     fprintf (dpyinfo->termscript, "\n<UPDATE_END\n");
1296   dpyinfo->mouse_highlight.mouse_face_defer = 0;
1297 }
1298 
1299 static void
IT_frame_up_to_date(struct frame * f)1300 IT_frame_up_to_date (struct frame *f)
1301 {
1302   Lisp_Object new_cursor, frame_desired_cursor;
1303   struct window *sw;
1304 
1305   FRAME_MOUSE_UPDATE (f);
1306 
1307   /* Set the cursor type to whatever they wanted.  In a minibuffer
1308      window, we want the cursor to appear only if we are reading input
1309      from this window, and we want the cursor to be taken from the
1310      frame parameters.  For the selected window, we use either its
1311      buffer-local value or the value from the frame parameters if the
1312      buffer doesn't define its local value for the cursor type.  */
1313   sw = XWINDOW (f->selected_window);
1314   frame_desired_cursor = Fcdr (Fassq (Qcursor_type, f->param_alist));
1315   if (cursor_in_echo_area
1316       && FRAME_HAS_MINIBUF_P (f)
1317       && EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window)
1318       && sw == XWINDOW (echo_area_window))
1319     new_cursor = frame_desired_cursor;
1320   else
1321     {
1322       struct buffer *b = XBUFFER (sw->contents);
1323 
1324       if (EQ (BVAR (b,cursor_type), Qt))
1325 	new_cursor = frame_desired_cursor;
1326       else if (NILP (BVAR (b, cursor_type))) /* nil means no cursor */
1327 	new_cursor = Fcons (Qbar, make_fixnum (0));
1328       else
1329 	new_cursor = BVAR (b, cursor_type);
1330     }
1331 
1332   IT_set_cursor_type (f, new_cursor);
1333 
1334   IT_cmgoto (f);  /* position cursor when update is done */
1335 }
1336 
1337 /* Copy LEN glyphs displayed on a single line whose vertical position
1338    is YPOS, beginning at horizontal position XFROM to horizontal
1339    position XTO, by moving blocks in the video memory.  Used by
1340    functions that insert and delete glyphs.  */
1341 static void
IT_copy_glyphs(int xfrom,int xto,size_t len,int ypos)1342 IT_copy_glyphs (int xfrom, int xto, size_t len, int ypos)
1343 {
1344   /* The offsets of source and destination relative to the
1345      conventional memory selector.  */
1346   int from = 2 * (xfrom + screen_size_X * ypos) + ScreenPrimary;
1347   int to = 2 * (xto + screen_size_X * ypos) + ScreenPrimary;
1348 
1349   if (from == to || len <= 0)
1350     return;
1351 
1352   _farsetsel (_dos_ds);
1353 
1354   /* The source and destination might overlap, so we need to move
1355      glyphs non-destructively.  */
1356   if (from > to)
1357     {
1358       for ( ; len; from += 2, to += 2, len--)
1359 	_farnspokew (to, _farnspeekw (from));
1360     }
1361   else
1362     {
1363       from += (len - 1) * 2;
1364       to += (len - 1) * 2;
1365       for ( ; len; from -= 2, to -= 2, len--)
1366 	_farnspokew (to, _farnspeekw (from));
1367     }
1368   if (screen_virtual_segment)
1369     dosv_refresh_virtual_screen (ypos * screen_size_X * 2, screen_size_X);
1370 }
1371 
1372 /* Insert and delete glyphs.  */
1373 static void
IT_insert_glyphs(struct frame * f,struct glyph * start,int len)1374 IT_insert_glyphs (struct frame *f, struct glyph *start, int len)
1375 {
1376   int shift_by_width = screen_size_X - (new_pos_X + len);
1377 
1378   /* Shift right the glyphs from the nominal cursor position to the
1379      end of this line.  */
1380   IT_copy_glyphs (new_pos_X, new_pos_X + len, shift_by_width, new_pos_Y);
1381 
1382   /* Now write the glyphs to be inserted.  */
1383   IT_write_glyphs (f, start, len);
1384 }
1385 
1386 static void
IT_delete_glyphs(struct frame * f,int n)1387 IT_delete_glyphs (struct frame *f, int n)
1388 {
1389   emacs_abort ();
1390 }
1391 
1392 /* This was copied from xfaces.c  */
1393 
1394 /* IT_set_terminal_modes is called when emacs is started,
1395    resumed, and whenever the screen is redrawn!  */
1396 
1397 static void
IT_set_terminal_modes(struct terminal * term)1398 IT_set_terminal_modes (struct terminal *term)
1399 {
1400   struct tty_display_info *tty;
1401 
1402   /* If called with initial terminal, it's too early to do anything
1403      useful.  */
1404   if (term->type == output_initial)
1405     return;
1406 
1407   tty = term->display_info.tty;
1408 
1409   if (tty->termscript)
1410     fprintf (tty->termscript, "\n<SET_TERM>");
1411 
1412   screen_size_X = ScreenCols ();
1413   screen_size_Y = ScreenRows ();
1414   screen_size = screen_size_X * screen_size_Y;
1415 
1416   new_pos_X = new_pos_Y = 0;
1417   current_pos_X = current_pos_Y = -1;
1418 
1419   if (term_setup_done)
1420     return;
1421   term_setup_done = 1;
1422 
1423   startup_screen_size_X = screen_size_X;
1424   startup_screen_size_Y = screen_size_Y;
1425   startup_screen_attrib = ScreenAttrib;
1426 
1427   /* Is DOS/V (or any other RSIS software which relocates
1428      the screen) installed?  */
1429   {
1430     unsigned short es_value;
1431     __dpmi_regs regs;
1432 
1433     regs.h.ah = 0xfe;	/* get relocated screen address */
1434     if (ScreenPrimary == 0xb0000UL || ScreenPrimary == 0xb8000UL)
1435       regs.x.es = (ScreenPrimary >> 4) & 0xffff;
1436     else if (screen_old_address) /* already switched to Japanese mode once */
1437       regs.x.es = (screen_old_address >> 4) & 0xffff;
1438     else
1439       regs.x.es = ScreenMode () == 7 ? 0xb000 : 0xb800;
1440     regs.x.di = 0;
1441     es_value = regs.x.es;
1442     __dpmi_int (0x10, &regs);
1443 
1444     if (regs.x.es != es_value)
1445       {
1446 	/* screen_old_address is only set if ScreenPrimary does NOT
1447 	   already point to the relocated buffer address returned by
1448 	   the Int 10h/AX=FEh call above.  DJGPP v2.02 and later sets
1449 	   ScreenPrimary to that address at startup under DOS/V.  */
1450 	if (regs.x.es != ((ScreenPrimary >> 4) & 0xffff))
1451 	  screen_old_address = ScreenPrimary;
1452 	screen_virtual_segment = regs.x.es;
1453 	screen_virtual_offset  = regs.x.di;
1454 	ScreenPrimary = (screen_virtual_segment << 4) + screen_virtual_offset;
1455       }
1456   }
1457 
1458   ScreenGetCursor (&startup_pos_Y, &startup_pos_X);
1459   ScreenRetrieve (startup_screen_buffer = xmalloc (screen_size * 2));
1460 
1461   bright_bg ();
1462 }
1463 
1464 /* IT_reset_terminal_modes is called when emacs is
1465    suspended or killed.  */
1466 
1467 static void
IT_reset_terminal_modes(struct terminal * term)1468 IT_reset_terminal_modes (struct terminal *term)
1469 {
1470   int display_row_start = (int) ScreenPrimary;
1471   int saved_row_len     = startup_screen_size_X * 2;
1472   int update_row_len    = ScreenCols () * 2, current_rows = ScreenRows ();
1473   int to_next_row       = update_row_len;
1474   unsigned char *saved_row = startup_screen_buffer;
1475   int cursor_pos_X = ScreenCols () - 1, cursor_pos_Y = ScreenRows () - 1;
1476   struct tty_display_info *tty = term->display_info.tty;
1477 
1478   if (tty->termscript)
1479     fprintf (tty->termscript, "\n<RESET_TERM>");
1480 
1481   if (!term_setup_done)
1482     return;
1483 
1484   mouse_off ();
1485 
1486   /* Leave the video system in the same state as we found it,
1487      as far as the blink/bright-background bit is concerned.  */
1488   maybe_enable_blinking ();
1489 
1490   /* We have a situation here.
1491      We cannot just do ScreenUpdate(startup_screen_buffer) because
1492      the luser could have changed screen dimensions inside Emacs
1493      and failed (or didn't want) to restore them before killing
1494      Emacs.  ScreenUpdate() uses the *current* screen dimensions and
1495      thus will happily use memory outside what was allocated for
1496      `startup_screen_buffer'.
1497      Thus we only restore as much as the current screen dimensions
1498      can hold, and clear the rest (if the saved screen is smaller than
1499      the current) with the color attribute saved at startup.  The cursor
1500      is also restored within the visible dimensions.  */
1501 
1502   ScreenAttrib = startup_screen_attrib;
1503 
1504   /* Don't restore the screen if we are exiting less than 2 seconds
1505      after startup: we might be crashing, and the screen might show
1506      some vital clues to what's wrong.  */
1507   if (clock () - startup_time >= 2*CLOCKS_PER_SEC)
1508     {
1509       ScreenClear ();
1510       if (screen_virtual_segment)
1511 	dosv_refresh_virtual_screen (0, screen_size);
1512 
1513       if (update_row_len > saved_row_len)
1514 	update_row_len = saved_row_len;
1515       if (current_rows > startup_screen_size_Y)
1516 	current_rows = startup_screen_size_Y;
1517 
1518       if (tty->termscript)
1519 	fprintf (tty->termscript, "<SCREEN RESTORED (dimensions=%dx%d)>\n",
1520 		 update_row_len / 2, current_rows);
1521 
1522       while (current_rows--)
1523 	{
1524 	  dosmemput (saved_row, update_row_len, display_row_start);
1525 	  if (screen_virtual_segment)
1526 	    dosv_refresh_virtual_screen (display_row_start - ScreenPrimary,
1527 					 update_row_len / 2);
1528 	  saved_row         += saved_row_len;
1529 	  display_row_start += to_next_row;
1530 	}
1531     }
1532   if (startup_pos_X < cursor_pos_X)
1533     cursor_pos_X = startup_pos_X;
1534   if (startup_pos_Y < cursor_pos_Y)
1535     cursor_pos_Y = startup_pos_Y;
1536 
1537   ScreenSetCursor (cursor_pos_Y, cursor_pos_X);
1538   xfree (startup_screen_buffer);
1539   startup_screen_buffer = NULL;
1540 
1541   term_setup_done = 0;
1542 }
1543 
1544 /* Remember the screen colors of the current frame, to serve as the
1545    default colors for newly-created frames.  */
1546 DEFUN ("msdos-remember-default-colors", Fmsdos_remember_default_colors,
1547        Smsdos_remember_default_colors, 1, 1, 0,
1548        doc: /* Remember the screen colors of the current frame.  */)
1549   (Lisp_Object frame)
1550 {
1551   struct frame *f;
1552 
1553   CHECK_FRAME (frame);
1554   f = XFRAME (frame);
1555 
1556   /* This function is called after applying default-frame-alist to the
1557      initial frame.  At that time, if reverse-colors option was
1558      specified in default-frame-alist, it was already applied, and
1559      frame colors are reversed.  */
1560   initial_screen_colors[0] = FRAME_FOREGROUND_PIXEL (f);
1561   initial_screen_colors[1] = FRAME_BACKGROUND_PIXEL (f);
1562 
1563   return Qnil;
1564 }
1565 
1566 void
IT_set_frame_parameters(struct frame * f,Lisp_Object alist)1567 IT_set_frame_parameters (struct frame *f, Lisp_Object alist)
1568 {
1569   Lisp_Object tail;
1570   int i, j, length = XFIXNUM (Flength (alist));
1571   Lisp_Object *parms
1572     = (Lisp_Object *) alloca (length * word_size);
1573   Lisp_Object *values
1574     = (Lisp_Object *) alloca (length * word_size);
1575   /* Do we have to reverse the foreground and background colors?  */
1576   int reverse = EQ (Fcdr (Fassq (Qreverse, f->param_alist)), Qt);
1577   int redraw = 0, fg_set = 0, bg_set = 0;
1578   unsigned long orig_fg, orig_bg;
1579   struct tty_display_info *tty = FRAME_TTY (f);
1580 
1581   /* If we are creating a new frame, begin with the original screen colors
1582      used for the initial frame.  */
1583   if (!f->default_face_done_p
1584       && initial_screen_colors[0] != -1 && initial_screen_colors[1] != -1)
1585     {
1586       FRAME_FOREGROUND_PIXEL (f) = initial_screen_colors[0];
1587       FRAME_BACKGROUND_PIXEL (f) = initial_screen_colors[1];
1588       init_frame_faces (f);
1589       f->default_face_done_p = 1;
1590     }
1591   orig_fg = reverse ? FRAME_BACKGROUND_PIXEL (f) : FRAME_FOREGROUND_PIXEL (f);
1592   orig_bg = reverse ? FRAME_FOREGROUND_PIXEL (f) : FRAME_BACKGROUND_PIXEL (f);
1593 
1594   /* Extract parm names and values into those vectors.  */
1595   i = 0;
1596   for (tail = alist; CONSP (tail); tail = XCDR (tail))
1597     {
1598       Lisp_Object elt = XCAR (tail);
1599       parms[i] = Fcar (elt);
1600       CHECK_SYMBOL (parms[i]);
1601       values[i] = Fcdr (elt);
1602       i++;
1603     }
1604 
1605   j = i;
1606 
1607   for (i = 0; i < j; i++)
1608     {
1609       Lisp_Object prop, val;
1610 
1611       prop = parms[i];
1612       val  = values[i];
1613 
1614       if (EQ (prop, Qreverse))
1615 	reverse = EQ (val, Qt);
1616     }
1617 
1618   if (tty->termscript && reverse)
1619     fprintf (tty->termscript, "<INVERSE-VIDEO>\n");
1620 
1621   /* Now process the alist elements in reverse of specified order.  */
1622   for (i--; i >= 0; i--)
1623     {
1624       Lisp_Object prop, val;
1625 
1626       prop = parms[i];
1627       val  = values[i];
1628 
1629       if (EQ (prop, Qforeground_color))
1630 	{
1631 	  unsigned long new_color = load_color (f, NULL, val, reverse
1632 						? LFACE_BACKGROUND_INDEX
1633 						: LFACE_FOREGROUND_INDEX);
1634 	  if (new_color !=  FACE_TTY_DEFAULT_COLOR
1635 	      && new_color != FACE_TTY_DEFAULT_FG_COLOR
1636 	      && new_color != FACE_TTY_DEFAULT_BG_COLOR)
1637 	    {
1638 	      if (!reverse)
1639 		{
1640 		  FRAME_FOREGROUND_PIXEL (f) = new_color;
1641 		  /* Make sure the foreground of the default face for
1642 		     this frame is changed as well.  */
1643 		  update_face_from_frame_parameter (f, Qforeground_color, val);
1644 		  fg_set = 1;
1645 		  if (tty->termscript)
1646 		    fprintf (tty->termscript, "<FGCOLOR %lu>\n", new_color);
1647 		}
1648 	      else
1649 		{
1650 		  FRAME_BACKGROUND_PIXEL (f) = new_color;
1651 		  update_face_from_frame_parameter (f, Qbackground_color, val);
1652 		  bg_set = 1;
1653 		  if (tty->termscript)
1654 		    fprintf (tty->termscript, "<BGCOLOR %lu>\n", new_color);
1655 		}
1656 	      redraw = 1;
1657 	    }
1658 	}
1659       else if (EQ (prop, Qbackground_color))
1660 	{
1661 	  unsigned long new_color = load_color (f, NULL, val, reverse
1662 						? LFACE_FOREGROUND_INDEX
1663 						: LFACE_BACKGROUND_INDEX);
1664 	  if (new_color != FACE_TTY_DEFAULT_COLOR
1665 	      && new_color != FACE_TTY_DEFAULT_FG_COLOR
1666 	      && new_color != FACE_TTY_DEFAULT_BG_COLOR)
1667 	    {
1668 	      if (!reverse)
1669 		{
1670 		  FRAME_BACKGROUND_PIXEL (f) = new_color;
1671 		  /* Make sure the background of the default face for
1672 		     this frame is changed as well.  */
1673 		  bg_set = 1;
1674 		  update_face_from_frame_parameter (f, Qbackground_color, val);
1675 		  if (tty->termscript)
1676 		    fprintf (tty->termscript, "<BGCOLOR %lu>\n", new_color);
1677 		}
1678 	      else
1679 		{
1680 		  FRAME_FOREGROUND_PIXEL (f) = new_color;
1681 		  fg_set = 1;
1682 		  update_face_from_frame_parameter (f, Qforeground_color, val);
1683 		  if (tty->termscript)
1684 		    fprintf (tty->termscript, "<FGCOLOR %lu>\n", new_color);
1685 		}
1686 	      redraw = 1;
1687 	    }
1688 	}
1689       else if (EQ (prop, Qtitle))
1690 	{
1691 	  x_set_title (f, val);
1692 	  if (tty->termscript)
1693 	    fprintf (tty->termscript, "<TITLE: %s>\n", SDATA (val));
1694 	}
1695       else if (EQ (prop, Qcursor_type))
1696 	{
1697 	  IT_set_cursor_type (f, val);
1698 	  if (tty->termscript)
1699 	    fprintf (tty->termscript, "<CTYPE: %s>\n",
1700 		     EQ (val, Qbar)
1701 		     || EQ (val, Qhbar)
1702 		     || (CONSP (val) && (EQ (XCAR (val), Qbar)
1703 					 || EQ (XCAR (val), Qhbar)))
1704 		     ? "bar" : "box");
1705 	}
1706       else if (EQ (prop, Qtty_type))
1707 	{
1708 	  internal_terminal_init ();
1709 	  if (tty->termscript)
1710 	    fprintf (tty->termscript, "<TERM_INIT done, TTY_TYPE: %.*s>\n",
1711 		     SBYTES (val), SDATA (val));
1712 	}
1713       store_frame_param (f, prop, val);
1714     }
1715 
1716   /* If they specified "reverse", but not the colors, we need to swap
1717      the current frame colors.  */
1718   if (reverse)
1719     {
1720       if (!fg_set)
1721 	{
1722 	  FRAME_FOREGROUND_PIXEL (f) = orig_bg;
1723 	  update_face_from_frame_parameter (f, Qforeground_color,
1724 					    tty_color_name (f, orig_bg));
1725 	  redraw = 1;
1726 	}
1727       if (!bg_set)
1728 	{
1729 	  FRAME_BACKGROUND_PIXEL (f) = orig_fg;
1730 	  update_face_from_frame_parameter (f, Qbackground_color,
1731 					    tty_color_name (f, orig_fg));
1732 	  redraw = 1;
1733 	}
1734     }
1735 
1736   if (redraw)
1737     {
1738       face_change = true;	/* forces xdisp.c to recompute basic faces */
1739       if (f == SELECTED_FRAME ())
1740 	redraw_frame (f);
1741     }
1742 }
1743 
1744 extern void init_frame_faces (struct frame *);
1745 
1746 #endif /* !HAVE_X_WINDOWS */
1747 
1748 
1749 /* Do we need the internal terminal?  */
1750 
1751 void
internal_terminal_init(void)1752 internal_terminal_init (void)
1753 {
1754   static int init_needed = 1;
1755   char *term = getenv ("TERM"), *colors;
1756   struct frame *sf = SELECTED_FRAME ();
1757   struct tty_display_info *tty;
1758 
1759 #ifdef HAVE_X_WINDOWS
1760   if (!inhibit_window_system)
1761     return;
1762 #endif
1763 
1764   /* If this is the initial terminal, we are done here.  */
1765   if (sf->output_method == output_initial)
1766     return;
1767 
1768   internal_terminal
1769     = (!noninteractive) && term && !strcmp (term, "internal");
1770 
1771 #ifndef HAVE_X_WINDOWS
1772   if (!internal_terminal || inhibit_window_system)
1773     {
1774       sf->output_method = output_termcap;
1775       return;
1776     }
1777 
1778   tty = FRAME_TTY (sf);
1779   kset_window_system (current_kboard, Qpc);
1780   sf->output_method = output_msdos_raw;
1781   if (init_needed)
1782     {
1783       if (!tty->termscript && getenv ("EMACSTEST"))
1784 	tty->termscript = fopen (getenv ("EMACSTEST"), "wt");
1785       if (tty->termscript)
1786 	{
1787 	  time_t now = time (NULL);
1788 	  struct tm *tnow = localtime (&now);
1789 	  char tbuf[100];
1790 
1791 	  strftime (tbuf, sizeof (tbuf) - 1, "%a %b %e %Y %H:%M:%S %Z", tnow);
1792 	  fprintf (tty->termscript, "\nEmacs session started at %s\n", tbuf);
1793 	  fprintf (tty->termscript,   "=====================\n\n");
1794 	}
1795 
1796       Vinitial_window_system = Qpc;
1797       Vwindow_system_version = make_fixnum (27); /* RE Emacs version */
1798       tty->terminal->type = output_msdos_raw;
1799 
1800       /* If Emacs was dumped on DOS/V machine, forget the stale VRAM
1801 	 address.  */
1802       screen_old_address = 0;
1803 
1804       /* Forget the stale screen colors as well.  */
1805       initial_screen_colors[0] = initial_screen_colors[1] = -1;
1806 
1807       FRAME_BACKGROUND_PIXEL (SELECTED_FRAME ()) = 7; /* White */
1808       FRAME_FOREGROUND_PIXEL (SELECTED_FRAME ()) = 0; /* Black */
1809       bright_bg ();
1810       colors = getenv ("EMACSCOLORS");
1811       if (colors && strlen (colors) >= 2)
1812 	{
1813 	  /* The colors use 4 bits each (we enable bright background).  */
1814 	  if (isdigit (colors[0]))
1815 	    colors[0] -= '0';
1816 	  else if (isxdigit (colors[0]))
1817 	    colors[0] -= (isupper (colors[0]) ? 'A' : 'a') - 10;
1818 	  if (colors[0] >= 0 && colors[0] < 16)
1819 	    FRAME_FOREGROUND_PIXEL (SELECTED_FRAME ()) = colors[0];
1820 	  if (isdigit (colors[1]))
1821 	    colors[1] -= '0';
1822 	  else if (isxdigit (colors[1]))
1823 	    colors[1] -= (isupper (colors[1]) ? 'A' : 'a') - 10;
1824 	  if (colors[1] >= 0 && colors[1] < 16)
1825 	    FRAME_BACKGROUND_PIXEL (SELECTED_FRAME ()) = colors[1];
1826 	}
1827 
1828       reset_mouse_highlight (&the_only_display_info.mouse_highlight);
1829 
1830       if (have_mouse)	/* detected in dos_ttraw, which see */
1831 	{
1832 	  have_mouse = 1;	/* enable mouse */
1833 	  mouse_visible = 0;
1834 	  mouse_setup_buttons (mouse_button_count);
1835 	  tty->terminal->mouse_position_hook = &mouse_get_pos;
1836 	  mouse_init ();
1837 	}
1838 
1839       if (tty->termscript && screen_size)
1840 	fprintf (tty->termscript, "<SCREEN SAVED (dimensions=%dx%d)>\n",
1841 		 screen_size_X, screen_size_Y);
1842 
1843       init_frame_faces (sf);
1844       init_needed = 0;
1845     }
1846 #endif
1847 }
1848 
1849 void
initialize_msdos_display(struct terminal * term)1850 initialize_msdos_display (struct terminal *term)
1851 {
1852   term->rif = 0;		/* we don't support window-based display */
1853   term->cursor_to_hook = term->raw_cursor_to_hook = IT_cursor_to;
1854   term->clear_to_end_hook = IT_clear_to_end;
1855   term->clear_frame_hook = IT_clear_screen;
1856   term->clear_end_of_line_hook = IT_clear_end_of_line;
1857   term->ins_del_lines_hook = 0;
1858   term->insert_glyphs_hook = IT_insert_glyphs;
1859   term->write_glyphs_hook = IT_write_glyphs;
1860   term->delete_glyphs_hook = IT_delete_glyphs;
1861   term->ring_bell_hook = IT_ring_bell;
1862   term->reset_terminal_modes_hook = IT_reset_terminal_modes;
1863   term->set_terminal_modes_hook = IT_set_terminal_modes;
1864   term->set_terminal_window_hook = NULL;
1865   term->update_begin_hook = IT_update_begin;
1866   term->update_end_hook = IT_update_end;
1867   term->frame_up_to_date_hook = IT_frame_up_to_date;
1868   term->mouse_position_hook = 0; /* set later by dos_ttraw */
1869   term->menu_show_hook = x_menu_show;
1870   term->frame_rehighlight_hook = 0;
1871   term->frame_raise_lower_hook = 0;
1872   term->set_vertical_scroll_bar_hook = 0;
1873   term->condemn_scroll_bars_hook = 0;
1874   term->redeem_scroll_bar_hook = 0;
1875   term->judge_scroll_bars_hook = 0;
1876   term->read_socket_hook = &tty_read_avail_input; /* from keyboard.c */
1877 }
1878 
1879 int
dos_get_saved_screen(char ** screen,int * rows,int * cols)1880 dos_get_saved_screen (char **screen, int *rows, int *cols)
1881 {
1882 #ifndef HAVE_X_WINDOWS
1883   *screen = startup_screen_buffer;
1884   *cols = startup_screen_size_X;
1885   *rows = startup_screen_size_Y;
1886   return *screen != (char *)0;
1887 #else
1888   return 0;
1889 #endif
1890 }
1891 
1892 
1893 /* ----------------------- Keyboard control ----------------------
1894  *
1895  * Keymaps reflect the following keyboard layout:
1896  *
1897  *    0  1  2  3  4  5  6  7  8  9  10 11 12  BS
1898  *    TAB 15 16 17 18 19 20 21 22 23 24 25 26 (41)
1899  *    CLOK 30 31 32 33 34 35 36 37 38 39 40 (41) RET
1900  *    SH () 45 46 47 48 49 50 51 52 53 54  SHIFT
1901  *                    SPACE
1902  */
1903 
1904 #define Ignore	0x0000
1905 #define Normal	0x0000	/* normal key - alt changes scan-code */
1906 #define FctKey	0x1000	/* func key if c == 0, else c */
1907 #define Special	0x2000	/* func key even if c != 0 */
1908 #define ModFct	0x3000	/* special if mod-keys, else 'c' */
1909 #define Map	0x4000	/* alt scan-code, map to unshift/shift key */
1910 #define KeyPad	0x5000	/* map to insert/kp-0 depending on c == 0xe0 */
1911 #define Grey	0x6000	/* Grey keypad key */
1912 
1913 #define Alt	0x0100	/* alt scan-code */
1914 #define Ctrl	0x0200	/* ctrl scan-code */
1915 #define Shift	0x0400	/* shift scan-code */
1916 
1917 static int extended_kbd; /* 101 (102) keyboard present. */
1918 
1919 struct kbd_translate {
1920   unsigned char  sc;
1921   unsigned char  ch;
1922   unsigned short code;
1923 };
1924 
1925 struct dos_keyboard_map
1926 {
1927   char *unshifted;
1928   char *shifted;
1929   char *alt_gr;
1930   struct kbd_translate *translate_table;
1931 };
1932 
1933 
1934 static struct dos_keyboard_map us_keyboard = {
1935 /* 0         1         2         3         4         5      */
1936 /* 01234567890123456789012345678901234567890 123 45678901234 */
1937   "`1234567890-=  qwertyuiop[]   asdfghjkl;'\\  \\zxcvbnm,./  ",
1938 /* 0123456789012345678901234567890123456789 012345678901234 */
1939   "~!@#$%^&*()_+  QWERTYUIOP{}   ASDFGHJKL:\"|  |ZXCVBNM<>?  ",
1940   0,				/* no Alt-Gr key */
1941   0				/* no translate table */
1942 };
1943 
1944 static struct dos_keyboard_map fr_keyboard = {
1945 /* 0         1         2         3         4         5      */
1946 /* 012 3456789012345678901234567890123456789012345678901234 */
1947   "�&�\"'(-�_��)=  azertyuiop^$   qsdfghjklm�*  <wxcvbn,;:!  ",
1948 /* 0123456789012345678901234567890123456789012345678901234 */
1949   " 1234567890�+  AZERTYUIOP��   QSDFGHJKLM%�  >WXCVBN?./�  ",
1950 /* 01234567 89012345678901234567890123456789012345678901234 */
1951   "  ~#{[|`\\^@]}             �                              ",
1952   0				/* no translate table */
1953 };
1954 
1955 /*
1956  * Italian keyboard support, country code 39.
1957  * '<' 56:3c*0000
1958  * '>' 56:3e*0000
1959  * added also {,},` as, respectively, AltGr-8, AltGr-9, AltGr-'
1960  * Donated by Stefano Brozzi <brozzis@mag00.cedi.unipr.it>
1961  */
1962 
1963 static struct kbd_translate it_kbd_translate_table[] = {
1964   { 0x56, 0x3c, Normal | 13 },
1965   { 0x56, 0x3e, Normal | 27 },
1966   { 0, 0, 0 }
1967 };
1968 static struct dos_keyboard_map it_keyboard = {
1969 /* 0          1         2         3         4         5     */
1970 /* 0 123456789012345678901234567890123456789012345678901234 */
1971   "\\1234567890'�< qwertyuiop�+>  asdfghjkl���  <zxcvbnm,.-  ",
1972 /* 01 23456789012345678901234567890123456789012345678901234 */
1973   "|!\"�$%&/()=?^> QWERTYUIOP�*   ASDFGHJKL���  >ZXCVBNM;:_  ",
1974 /* 0123456789012345678901234567890123456789012345678901234 */
1975   "        {}~`             []             @#               ",
1976   it_kbd_translate_table
1977 };
1978 
1979 static struct dos_keyboard_map dk_keyboard = {
1980 /* 0         1         2         3         4         5      */
1981 /* 0123456789012345678901234567890123456789012345678901234 */
1982   "�1234567890+|  qwertyuiop�~   asdfghjkl��'  <zxcvbnm,.-  ",
1983 /* 01 23456789012345678901234567890123456789012345678901234 */
1984   "�!\"#$%&/()=?`  QWERTYUIOP�^   ASDFGHJKL��*  >ZXCVBNM;:_  ",
1985 /* 0123456789012345678901234567890123456789012345678901234 */
1986   "  @�$  {[]} |                                             ",
1987   0				/* no translate table */
1988 };
1989 
1990 static struct kbd_translate jp_kbd_translate_table[] = {
1991   { 0x73, 0x5c, Normal | 0 },
1992   { 0x73, 0x5f, Normal | 0 },
1993   { 0x73, 0x1c, Map | 0 },
1994   { 0x7d, 0x5c, Normal | 13 },
1995   { 0x7d, 0x7c, Normal | 13 },
1996   { 0x7d, 0x1c, Map | 13 },
1997   { 0, 0, 0 }
1998 };
1999 static struct dos_keyboard_map jp_keyboard = {
2000 /*  0         1          2         3         4         5     */
2001 /*  0123456789012 345678901234567890123456789012345678901234 */
2002   "\\1234567890-^\\ qwertyuiop@[   asdfghjkl;:]   zxcvbnm,./  ",
2003 /*  01 23456789012345678901234567890123456789012345678901234 */
2004    "_!\"#$%&'()~=~| QWERTYUIOP`{   ASDFGHJKL+*}   ZXCVBNM<>?  ",
2005   0,				/* no Alt-Gr key */
2006   jp_kbd_translate_table
2007 };
2008 
2009 static struct keyboard_layout_list
2010 {
2011   int country_code;
2012   struct dos_keyboard_map *keyboard_map;
2013 } keyboard_layout_list[] =
2014 {
2015   { 1, &us_keyboard },
2016   { 33, &fr_keyboard },
2017   { 39, &it_keyboard },
2018   { 45, &dk_keyboard },
2019   { 81, &jp_keyboard }
2020 };
2021 
2022 static struct dos_keyboard_map *keyboard;
2023 static int keyboard_map_all;
2024 static int international_keyboard;
2025 
2026 int
dos_set_keyboard(int code,int always)2027 dos_set_keyboard (int code, int always)
2028 {
2029   int i;
2030   _go32_dpmi_registers regs;
2031 
2032   /* See if Keyb.Com is installed (for international keyboard support).
2033      Note: calling Int 2Fh via int86 wedges the DOS box on some versions
2034      of Windows 9X!  So don't do that!  */
2035   regs.x.ax = 0xad80;
2036   regs.x.ss = regs.x.sp = regs.x.flags = 0;
2037   _go32_dpmi_simulate_int (0x2f, &regs);
2038   if (regs.h.al == 0xff)
2039     international_keyboard = 1;
2040 
2041   /* Initialize to US settings, for countries that don't have their own.  */
2042   keyboard = keyboard_layout_list[0].keyboard_map;
2043   keyboard_map_all = always;
2044   dos_keyboard_layout = 1;
2045 
2046   for (i = 0; i < (sizeof (keyboard_layout_list)/sizeof (struct keyboard_layout_list)); i++)
2047     if (code == keyboard_layout_list[i].country_code)
2048       {
2049 	keyboard = keyboard_layout_list[i].keyboard_map;
2050 	keyboard_map_all = always;
2051 	dos_keyboard_layout = code;
2052 	return 1;
2053       }
2054   return 0;
2055 }
2056 
2057 static struct
2058 {
2059   unsigned char char_code;	/* normal code	*/
2060   unsigned char meta_code;	/* M- code	*/
2061   unsigned char keypad_code;	/* keypad code	*/
2062   unsigned char editkey_code;	/* edit key	*/
2063 } keypad_translate_map[] = {
2064   { '0',  '0',  0xb0, /* kp-0 */		0x63 /* insert */ },
2065   { '1',  '1',  0xb1, /* kp-1 */		0x57 /* end */    },
2066   { '2',  '2',  0xb2, /* kp-2 */		0x54 /* down */   },
2067   { '3',  '3',  0xb3, /* kp-3 */		0x56 /* next */   },
2068   { '4',  '4',  0xb4, /* kp-4 */		0x51 /* left */   },
2069   { '5',  '5',  0xb5, /* kp-5 */		0xb5 /* kp-5 */   },
2070   { '6',  '6',  0xb6, /* kp-6 */		0x53 /* right */  },
2071   { '7',  '7',  0xb7, /* kp-7 */		0x50 /* home */   },
2072   { '8',  '8',  0xb8, /* kp-8 */		0x52 /* up */     },
2073   { '9',  '9',  0xb9, /* kp-9 */		0x55 /* prior */  },
2074   { '.',  '-',  0xae, /* kp-decimal */		0xff  /* delete */}
2075 };
2076 
2077 static struct
2078 {
2079   unsigned char char_code;	/* normal code	*/
2080   unsigned char keypad_code;	/* keypad code	*/
2081 } grey_key_translate_map[] = {
2082   { '/',  0xaf /* kp-decimal */  },
2083   { '*',  0xaa /* kp-multiply */ },
2084   { '-',  0xad /* kp-subtract */ },
2085   { '+',  0xab /* kp-add */      },
2086   { '\r', 0x8d  /* kp-enter */   }
2087 };
2088 
2089 static unsigned short
2090 ibmpc_translate_map[] =
2091 {
2092   /* --------------- 00 to 0f --------------- */
2093   Normal | 0xff,	/* Ctrl Break + Alt-NNN */
2094   Alt | ModFct | 0x1b,		/* Escape */
2095   Normal | 1,			/* '1' */
2096   Normal | 2,			/* '2' */
2097   Normal | 3,			/* '3' */
2098   Normal | 4,			/* '4' */
2099   Normal | 5,			/* '5' */
2100   Normal | 6,			/* '6' */
2101   Normal | 7,			/* '7' */
2102   Normal | 8,			/* '8' */
2103   Normal | 9,			/* '9' */
2104   Normal | 10,			/* '0' */
2105   Normal | 11,			/* '-' */
2106   Normal | 12,			/* '=' */
2107   Special | 0x08,		/* Backspace */
2108   ModFct | 0x74,		/* Tab/Backtab */
2109 
2110   /* --------------- 10 to 1f --------------- */
2111   Map | 15,			/* 'q' */
2112   Map | 16,			/* 'w' */
2113   Map | 17,			/* 'e' */
2114   Map | 18,			/* 'r' */
2115   Map | 19,			/* 't' */
2116   Map | 20,			/* 'y' */
2117   Map | 21,			/* 'u' */
2118   Map | 22,			/* 'i' */
2119   Map | 23,			/* 'o' */
2120   Map | 24,			/* 'p' */
2121   Map | 25,			/* '[' */
2122   Map | 26,			/* ']' */
2123   ModFct | 0x0d,		/* Return */
2124   Ignore,			/* Ctrl */
2125   Map | 30,			/* 'a' */
2126   Map | 31,			/* 's' */
2127 
2128   /* --------------- 20 to 2f --------------- */
2129   Map | 32,			/* 'd' */
2130   Map | 33,			/* 'f' */
2131   Map | 34,			/* 'g' */
2132   Map | 35,			/* 'h' */
2133   Map | 36,			/* 'j' */
2134   Map | 37,			/* 'k' */
2135   Map | 38,			/* 'l' */
2136   Map | 39,			/* ';' */
2137   Map | 40,			/* '\'' */
2138   Map |  0,			/* '`' */
2139   Ignore,			/* Left shift */
2140   Map | 41,			/* '\\' */
2141   Map | 45,			/* 'z' */
2142   Map | 46,			/* 'x' */
2143   Map | 47,			/* 'c' */
2144   Map | 48,			/* 'v' */
2145 
2146   /* --------------- 30 to 3f --------------- */
2147   Map | 49,			/* 'b' */
2148   Map | 50,			/* 'n' */
2149   Map | 51,			/* 'm' */
2150   Map | 52,			/* ',' */
2151   Map | 53,			/* '.' */
2152   Map | 54,			/* '/' */
2153   Ignore,			/* Right shift */
2154   Grey | 1,			/* Grey * */
2155   Ignore,			/* Alt */
2156   Normal | 55,			/* ' ' */
2157   Ignore,			/* Caps Lock */
2158   FctKey | 0xbe,		/* F1 */
2159   FctKey | 0xbf,		/* F2 */
2160   FctKey | 0xc0,		/* F3 */
2161   FctKey | 0xc1,		/* F4 */
2162   FctKey | 0xc2,		/* F5 */
2163 
2164   /* --------------- 40 to 4f --------------- */
2165   FctKey | 0xc3,		/* F6 */
2166   FctKey | 0xc4,		/* F7 */
2167   FctKey | 0xc5,		/* F8 */
2168   FctKey | 0xc6,		/* F9 */
2169   FctKey | 0xc7,		/* F10 */
2170   Ignore,			/* Num Lock */
2171   Ignore,			/* Scroll Lock */
2172   KeyPad | 7,			/* Home */
2173   KeyPad | 8,			/* Up */
2174   KeyPad | 9,			/* Page Up */
2175   Grey | 2,			/* Grey - */
2176   KeyPad | 4,			/* Left */
2177   KeyPad | 5,			/* Keypad 5 */
2178   KeyPad | 6,			/* Right */
2179   Grey | 3,			/* Grey + */
2180   KeyPad | 1,			/* End */
2181 
2182   /* --------------- 50 to 5f --------------- */
2183   KeyPad | 2,			/* Down */
2184   KeyPad | 3,			/* Page Down */
2185   KeyPad | 0,			/* Insert */
2186   KeyPad | 10,			/* Delete */
2187   Shift | FctKey | 0xbe,	/* (Shift) F1 */
2188   Shift | FctKey | 0xbf,	/* (Shift) F2 */
2189   Shift | FctKey | 0xc0,	/* (Shift) F3 */
2190   Shift | FctKey | 0xc1,	/* (Shift) F4 */
2191   Shift | FctKey | 0xc2,	/* (Shift) F5 */
2192   Shift | FctKey | 0xc3,	/* (Shift) F6 */
2193   Shift | FctKey | 0xc4,	/* (Shift) F7 */
2194   Shift | FctKey | 0xc5,	/* (Shift) F8 */
2195   Shift | FctKey | 0xc6,	/* (Shift) F9 */
2196   Shift | FctKey | 0xc7,	/* (Shift) F10 */
2197   Ctrl | FctKey | 0xbe,		/* (Ctrl) F1 */
2198   Ctrl | FctKey | 0xbf,		/* (Ctrl) F2 */
2199 
2200   /* --------------- 60 to 6f --------------- */
2201   Ctrl | FctKey | 0xc0,		/* (Ctrl) F3 */
2202   Ctrl | FctKey | 0xc1,		/* (Ctrl) F4 */
2203   Ctrl | FctKey | 0xc2,		/* (Ctrl) F5 */
2204   Ctrl | FctKey | 0xc3,		/* (Ctrl) F6 */
2205   Ctrl | FctKey | 0xc4,		/* (Ctrl) F7 */
2206   Ctrl | FctKey | 0xc5,		/* (Ctrl) F8 */
2207   Ctrl | FctKey | 0xc6,		/* (Ctrl) F9 */
2208   Ctrl | FctKey | 0xc7,		/* (Ctrl) F10 */
2209   Alt | FctKey | 0xbe,		/* (Alt) F1 */
2210   Alt | FctKey | 0xbf,		/* (Alt) F2 */
2211   Alt | FctKey | 0xc0,		/* (Alt) F3 */
2212   Alt | FctKey | 0xc1,		/* (Alt) F4 */
2213   Alt | FctKey | 0xc2,		/* (Alt) F5 */
2214   Alt | FctKey | 0xc3,		/* (Alt) F6 */
2215   Alt | FctKey | 0xc4,		/* (Alt) F7 */
2216   Alt | FctKey | 0xc5,		/* (Alt) F8 */
2217 
2218   /* --------------- 70 to 7f --------------- */
2219   Alt | FctKey | 0xc6,		/* (Alt) F9 */
2220   Alt | FctKey | 0xc7,		/* (Alt) F10 */
2221   Ctrl | FctKey | 0x6d,		/* (Ctrl) Sys Rq */
2222   Ctrl | KeyPad | 4,		/* (Ctrl) Left */
2223   Ctrl | KeyPad | 6,		/* (Ctrl) Right */
2224   Ctrl | KeyPad | 1,		/* (Ctrl) End */
2225   Ctrl | KeyPad | 3,		/* (Ctrl) Page Down */
2226   Ctrl | KeyPad | 7,		/* (Ctrl) Home */
2227   Alt | Map | 1,		/* '1' */
2228   Alt | Map | 2,		/* '2' */
2229   Alt | Map | 3,		/* '3' */
2230   Alt | Map | 4,		/* '4' */
2231   Alt | Map | 5,		/* '5' */
2232   Alt | Map | 6,		/* '6' */
2233   Alt | Map | 7,		/* '7' */
2234   Alt | Map | 8,		/* '8' */
2235 
2236   /* --------------- 80 to 8f --------------- */
2237   Alt | Map | 9,		/* '9' */
2238   Alt | Map | 10,		/* '0' */
2239   Alt | Map | 11,		/* '-' */
2240   Alt | Map | 12,		/* '=' */
2241   Ctrl | KeyPad | 9,		/* (Ctrl) Page Up */
2242   FctKey | 0xc8,		/* F11 */
2243   FctKey | 0xc9,		/* F12 */
2244   Shift | FctKey | 0xc8,	/* (Shift) F11 */
2245   Shift | FctKey | 0xc9,	/* (Shift) F12 */
2246   Ctrl | FctKey | 0xc8,		/* (Ctrl) F11 */
2247   Ctrl | FctKey | 0xc9,		/* (Ctrl) F12 */
2248   Alt | FctKey | 0xc8,		/* (Alt) F11 */
2249   Alt | FctKey | 0xc9,		/* (Alt) F12 */
2250   Ctrl | KeyPad | 8,		/* (Ctrl) Up */
2251   Ctrl | Grey | 2,		/* (Ctrl) Grey - */
2252   Ctrl | KeyPad | 5,		/* (Ctrl) Keypad 5 */
2253 
2254   /* --------------- 90 to 9f --------------- */
2255   Ctrl | Grey | 3,		/* (Ctrl) Grey + */
2256   Ctrl | KeyPad | 2,		/* (Ctrl) Down */
2257   Ctrl | KeyPad | 0,		/* (Ctrl) Insert */
2258   Ctrl | KeyPad | 10,		/* (Ctrl) Delete */
2259   Ctrl | FctKey | 0x09,		/* (Ctrl) Tab */
2260   Ctrl | Grey | 0,		/* (Ctrl) Grey / */
2261   Ctrl | Grey | 1,		/* (Ctrl) Grey * */
2262   Alt | FctKey | 0x50,		/* (Alt) Home */
2263   Alt | FctKey | 0x52,		/* (Alt) Up */
2264   Alt | FctKey | 0x55,		/* (Alt) Page Up */
2265   Ignore,			/* NO KEY */
2266   Alt | FctKey | 0x51,		/* (Alt) Left */
2267   Ignore,			/* NO KEY */
2268   Alt | FctKey | 0x53,		/* (Alt) Right */
2269   Ignore,			/* NO KEY */
2270   Alt | FctKey | 0x57,		/* (Alt) End */
2271 
2272   /* --------------- a0 to af --------------- */
2273   Alt | KeyPad | 2,		/* (Alt) Down */
2274   Alt | KeyPad | 3,		/* (Alt) Page Down */
2275   Alt | KeyPad | 0,		/* (Alt) Insert */
2276   Alt | KeyPad | 10,		/* (Alt) Delete */
2277   Alt | Grey | 0,		/* (Alt) Grey / */
2278   Alt | FctKey | 0x09,		/* (Alt) Tab */
2279   Alt | Grey | 4		/* (Alt) Keypad Enter */
2280 };
2281 
2282 /* These bit-positions corresponds to values returned by BIOS */
2283 #define SHIFT_P		0x0003	/* two bits! */
2284 #define CTRL_P		0x0004
2285 #define ALT_P		0x0008
2286 #define SCRLOCK_P	0x0010
2287 #define NUMLOCK_P	0x0020
2288 #define CAPSLOCK_P	0x0040
2289 #define ALT_GR_P	0x0800
2290 #define SUPER_P		0x4000	/* pseudo */
2291 #define HYPER_P		0x8000	/* pseudo */
2292 
2293 static int
dos_get_modifiers(int * keymask)2294 dos_get_modifiers (int *keymask)
2295 {
2296   union REGS regs;
2297   int mask, modifiers = 0;
2298 
2299   /* Calculate modifier bits */
2300   regs.h.ah = extended_kbd ? 0x12 : 0x02;
2301   int86 (0x16, &regs, &regs);
2302 
2303   if (!extended_kbd)
2304     {
2305       mask = regs.h.al & (SHIFT_P | CTRL_P | ALT_P |
2306 			  SCRLOCK_P | NUMLOCK_P | CAPSLOCK_P);
2307     }
2308   else
2309     {
2310       mask = regs.h.al & (SHIFT_P |
2311 			  SCRLOCK_P | NUMLOCK_P | CAPSLOCK_P);
2312 
2313       /* Do not break international keyboard support.   */
2314       /* When Keyb.Com is loaded, the right Alt key is  */
2315       /* used for accessing characters like { and } 	  */
2316       if (regs.h.ah & 2)		/* Left ALT pressed ? */
2317 	mask |= ALT_P;
2318 
2319       if ((regs.h.ah & 8) != 0)		/* Right ALT pressed ? */
2320 	{
2321 	  mask |= ALT_GR_P;
2322 	  if (dos_hyper_key == 1)
2323 	    {
2324 	      mask |= HYPER_P;
2325 	      modifiers |= hyper_modifier;
2326 	    }
2327 	  else if (dos_super_key == 1)
2328 	    {
2329 	      mask |= SUPER_P;
2330 	      modifiers |= super_modifier;
2331 	    }
2332 	  else if (!international_keyboard)
2333 	    {
2334 	      /* If Keyb.Com is NOT installed, let Right Alt behave
2335 		 like the Left Alt.  */
2336 	      mask &= ~ALT_GR_P;
2337 	      mask |= ALT_P;
2338 	    }
2339 	}
2340 
2341       if (regs.h.ah & 1)		/* Left CTRL pressed ? */
2342 	mask |= CTRL_P;
2343 
2344       if (regs.h.ah & 4)	 	/* Right CTRL pressed ? */
2345 	{
2346 	  if (dos_hyper_key == 2)
2347 	    {
2348 	      mask |= HYPER_P;
2349 	      modifiers |= hyper_modifier;
2350 	    }
2351 	  else if (dos_super_key == 2)
2352 	    {
2353 	      mask |= SUPER_P;
2354 	      modifiers |= super_modifier;
2355 	    }
2356 	  else
2357 	    mask |= CTRL_P;
2358 	}
2359     }
2360 
2361   if (mask & SHIFT_P)
2362     modifiers |= shift_modifier;
2363   if (mask & CTRL_P)
2364     modifiers |= ctrl_modifier;
2365   if (mask & ALT_P)
2366     modifiers |= meta_modifier;
2367 
2368   if (keymask)
2369     *keymask = mask;
2370   return modifiers;
2371 }
2372 
2373 #define NUM_RECENT_DOSKEYS (100)
2374 int recent_doskeys_index;	/* Index for storing next element into recent_doskeys */
2375 int total_doskeys;		/* Total number of elements stored into recent_doskeys */
2376 Lisp_Object recent_doskeys;	/* A vector, holding the last 100 keystrokes */
2377 
2378 DEFUN ("recent-doskeys", Frecent_doskeys, Srecent_doskeys, 0, 0, 0,
2379        doc: /* Return vector of last 100 keyboard input values seen in dos_rawgetc.
2380 Each input key receives two values in this vector: first the ASCII code,
2381 and then the scan code.  */)
2382   (void)
2383 {
2384   Lisp_Object val, *keys = XVECTOR (recent_doskeys)->contents;
2385 
2386   if (total_doskeys < NUM_RECENT_DOSKEYS)
2387     return Fvector (total_doskeys, keys);
2388   else
2389     {
2390       val = Fvector (NUM_RECENT_DOSKEYS, keys);
2391       vcopy (val, 0, keys + recent_doskeys_index,
2392 	     NUM_RECENT_DOSKEYS - recent_doskeys_index);
2393       vcopy (val, NUM_RECENT_DOSKEYS - recent_doskeys_index,
2394 	     keys, recent_doskeys_index);
2395       return val;
2396     }
2397 }
2398 
2399 /* Get a char from keyboard.  Function keys are put into the event queue.  */
2400 static int
dos_rawgetc(void)2401 dos_rawgetc (void)
2402 {
2403   struct input_event event;
2404   union REGS regs;
2405   Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (SELECTED_FRAME ());
2406   EVENT_INIT (event);
2407 
2408 #ifndef HAVE_X_WINDOWS
2409   /* Maybe put the cursor where it should be.  */
2410   IT_cmgoto (SELECTED_FRAME ());
2411 #endif
2412 
2413   /* The following condition is equivalent to `kbhit ()', except that
2414      it uses the bios to do its job.  This pleases DESQview/X.  */
2415   while ((regs.h.ah = extended_kbd ? 0x11 : 0x01),
2416 	 int86 (0x16, &regs, &regs),
2417 	 (regs.x.flags & 0x40) == 0)
2418     {
2419       union REGS regs;
2420       register unsigned char c;
2421       int modifiers, sc, code = -1, mask, kp_mode;
2422 
2423       regs.h.ah = extended_kbd ? 0x10 : 0x00;
2424       int86 (0x16, &regs, &regs);
2425       c = regs.h.al;
2426       sc = regs.h.ah;
2427 
2428       total_doskeys += 2;
2429       ASET (recent_doskeys, recent_doskeys_index, make_fixnum (c));
2430       recent_doskeys_index++;
2431       if (recent_doskeys_index == NUM_RECENT_DOSKEYS)
2432 	recent_doskeys_index = 0;
2433       ASET (recent_doskeys, recent_doskeys_index, make_fixnum (sc));
2434       recent_doskeys_index++;
2435       if (recent_doskeys_index == NUM_RECENT_DOSKEYS)
2436 	recent_doskeys_index = 0;
2437 
2438       modifiers = dos_get_modifiers (&mask);
2439 
2440 #ifndef HAVE_X_WINDOWS
2441       if (!NILP (Vdos_display_scancodes))
2442 	{
2443 	  char buf[11];
2444 	  sprintf (buf, "%02x:%02x*%04x",
2445 		   (unsigned) (sc&0xff), (unsigned) c, mask);
2446 	  dos_direct_output (screen_size_Y - 2, screen_size_X - 12, buf, 10);
2447 	}
2448 #endif
2449 
2450       if (sc == 0xe0)
2451 	{
2452 	  switch (c)
2453 	    {
2454 	    case 10:		/* Ctrl Grey Enter */
2455 	      code = Ctrl | Grey | 4;
2456 	      break;
2457 	    case 13:		/* Grey Enter */
2458 	      code = Grey | 4;
2459 	      break;
2460 	    case '/':		/* Grey / */
2461 	      code = Grey | 0;
2462 	      break;
2463 	    default:
2464 	      continue;
2465 	    };
2466 	  c = 0;
2467 	}
2468       else
2469 	{
2470 	  /* Try the keyboard-private translation table first.  */
2471 	  if (keyboard->translate_table)
2472 	    {
2473 	      struct kbd_translate *p = keyboard->translate_table;
2474 
2475 	      while (p->sc)
2476 		{
2477 		  if (p->sc == sc && p->ch == c)
2478 		    {
2479 		      code = p->code;
2480 		      break;
2481 		    }
2482 		  p++;
2483 		}
2484 	    }
2485 	  /* If the private table didn't translate it, use the general
2486              one.  */
2487 	  if (code == -1)
2488 	    {
2489 	      if (sc >= (sizeof (ibmpc_translate_map) / sizeof (short)))
2490 		continue;
2491 	      if ((code = ibmpc_translate_map[sc]) == Ignore)
2492 		continue;
2493 	    }
2494 	}
2495 
2496       if (c == 0)
2497 	{
2498         /* We only look at the keyboard Ctrl/Shift/Alt keys when
2499            Emacs is ready to read a key.  Therefore, if they press
2500            `Alt-x' when Emacs is busy, by the time we get to
2501            `dos_get_modifiers', they might have already released the
2502            Alt key, and Emacs gets just `x', which is BAD.
2503            However, for keys with the `Map' property set, the ASCII
2504            code returns zero only if Alt is pressed.  So, when we DON'T
2505            have to support international_keyboard, we don't have to
2506            distinguish between the left and  right Alt keys, and we
2507            can set the META modifier for any keys with the `Map'
2508            property if they return zero ASCII code (c = 0).  */
2509         if ( (code & Alt)
2510              || ( (code & 0xf000) == Map && !international_keyboard))
2511 	    modifiers |= meta_modifier;
2512 	  if (code & Ctrl)
2513 	    modifiers |= ctrl_modifier;
2514 	  if (code & Shift)
2515 	    modifiers |= shift_modifier;
2516 	}
2517 
2518       switch (code & 0xf000)
2519 	{
2520 	case ModFct:
2521 	  if (c && !(mask & (SHIFT_P | ALT_P | CTRL_P | HYPER_P | SUPER_P)))
2522 	    return c;
2523 	  c = 0;		/* Special */
2524 
2525 	case FctKey:
2526 	  if (c != 0)
2527 	    return c;
2528 
2529 	case Special:
2530 	  code |= 0xff00;
2531 	  break;
2532 
2533 	case Normal:
2534 	  if (sc == 0)
2535 	    {
2536 	      if (c == 0)	/* ctrl-break */
2537 		continue;
2538 	      return c;		/* ALT-nnn */
2539 	    }
2540 	  if (!keyboard_map_all)
2541 	    {
2542 	      if (c != ' ')
2543 		return c;
2544 	      code = c;
2545 	      break;
2546 	    }
2547 
2548 	case Map:
2549 	  if (c && !(mask & ALT_P) && !((mask & SHIFT_P) && (mask & CTRL_P)))
2550 	    if (!keyboard_map_all)
2551 	      return c;
2552 
2553 	  code &= 0xff;
2554 	  if (mask & ALT_P && code <= 10 && code > 0 && dos_keypad_mode & 0x200)
2555 	    mask |= SHIFT_P;	/* ALT-1 => M-! etc. */
2556 
2557 	  if (mask & SHIFT_P)
2558 	    {
2559 	      code = keyboard->shifted[code];
2560 	      mask -= SHIFT_P;
2561 	      modifiers &= ~shift_modifier;
2562 	    }
2563 	  else
2564 	    if ((mask & ALT_GR_P) && keyboard->alt_gr && keyboard->alt_gr[code] != ' ')
2565 	      code = keyboard->alt_gr[code];
2566 	    else
2567 	      code = keyboard->unshifted[code];
2568 	  break;
2569 
2570 	case KeyPad:
2571 	  code &= 0xff;
2572 	  if (c == 0xe0)	/* edit key */
2573 	    kp_mode = 3;
2574 	  else
2575 	    if ((mask & (NUMLOCK_P|CTRL_P|SHIFT_P|ALT_P)) == NUMLOCK_P) /* numlock on */
2576 	      kp_mode = dos_keypad_mode & 0x03;
2577 	    else
2578 	      kp_mode = (dos_keypad_mode >> 4) & 0x03;
2579 
2580 	  switch (kp_mode)
2581 	    {
2582 	    case 0:
2583 	      if (code == 10 && dos_decimal_point)
2584 		return dos_decimal_point;
2585 	      return keypad_translate_map[code].char_code;
2586 
2587 	    case 1:
2588 	      code = 0xff00 | keypad_translate_map[code].keypad_code;
2589 	      break;
2590 
2591 	    case 2:
2592 	      code = keypad_translate_map[code].meta_code;
2593 	      modifiers = meta_modifier;
2594 	      break;
2595 
2596 	    case 3:
2597 	      code = 0xff00 | keypad_translate_map[code].editkey_code;
2598 	      break;
2599 	    }
2600 	  break;
2601 
2602 	case Grey:
2603 	  code &= 0xff;
2604 	  kp_mode = ((mask & (NUMLOCK_P|CTRL_P|SHIFT_P|ALT_P)) == NUMLOCK_P) ? 0x04 : 0x40;
2605 	  if (dos_keypad_mode & kp_mode)
2606 	    code = 0xff00 | grey_key_translate_map[code].keypad_code;
2607 	  else
2608 	    code = grey_key_translate_map[code].char_code;
2609 	  break;
2610 	}
2611 
2612       if (code == 0)
2613 	continue;
2614 
2615       if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight))
2616 	{
2617 	  clear_mouse_face (hlinfo);
2618 	  hlinfo->mouse_face_hidden = 1;
2619 	}
2620 
2621       if (code >= 0x100)
2622 	event.kind = NON_ASCII_KEYSTROKE_EVENT;
2623       else
2624 	event.kind = ASCII_KEYSTROKE_EVENT;
2625       event.code = code;
2626       event.modifiers =	modifiers;
2627       event.frame_or_window = selected_frame;
2628       event.arg = Qnil;
2629       event.timestamp = event_timestamp ();
2630       kbd_buffer_store_event (&event);
2631     }
2632 
2633   if (have_mouse > 0 && !mouse_preempted)
2634     {
2635       int but, press, x, y, ok;
2636       int mouse_prev_x = mouse_last_x, mouse_prev_y = mouse_last_y;
2637       Lisp_Object mouse_window = Qnil;
2638 
2639       /* Check for mouse movement *before* buttons.  */
2640       mouse_check_moved ();
2641 
2642       /* If the mouse moved from the spot of its last sighting, we
2643          might need to update mouse highlight.  */
2644       if (mouse_last_x != mouse_prev_x || mouse_last_y != mouse_prev_y)
2645 	{
2646 	  if (hlinfo->mouse_face_hidden)
2647 	    {
2648 	      hlinfo->mouse_face_hidden = 0;
2649 	      clear_mouse_face (hlinfo);
2650 	    }
2651 
2652 	  /* Generate SELECT_WINDOW_EVENTs when needed.  */
2653 	  if (!NILP (Vmouse_autoselect_window))
2654 	    {
2655 	      static Lisp_Object last_mouse_window;
2656 
2657 	      mouse_window = window_from_coordinates
2658 		(SELECTED_FRAME (), mouse_last_x, mouse_last_y, 0, 0, 0);
2659 	      /* A window will be selected only when it is not
2660 		 selected now, and the last mouse movement event was
2661 		 not in it.  A minibuffer window will be selected iff
2662 		 it is active.  */
2663 	      if (WINDOWP (mouse_window)
2664 		  && !EQ (mouse_window, last_mouse_window)
2665 		  && !EQ (mouse_window, selected_window))
2666 		{
2667 		  event.kind = SELECT_WINDOW_EVENT;
2668 		  event.frame_or_window = mouse_window;
2669 		  event.arg = Qnil;
2670 		  event.timestamp = event_timestamp ();
2671 		  kbd_buffer_store_event (&event);
2672 		}
2673 	      /* Remember the last window where we saw the mouse.  */
2674 	      last_mouse_window = mouse_window;
2675 	    }
2676 
2677 	  previous_help_echo_string = help_echo_string;
2678 	  help_echo_string = help_echo_object = help_echo_window = Qnil;
2679 	  help_echo_pos = -1;
2680 	  note_mouse_highlight (SELECTED_FRAME (), mouse_last_x, mouse_last_y);
2681 	  /* If the contents of the global variable help_echo has
2682 	     changed, generate a HELP_EVENT.  */
2683 	  if (!NILP (help_echo_string) || !NILP (previous_help_echo_string))
2684 	    gen_help_event (help_echo_string, selected_frame, help_echo_window,
2685 			    help_echo_object, help_echo_pos);
2686 	}
2687 
2688       for (but = 0; but < NUM_MOUSE_BUTTONS; but++)
2689 	for (press = 0; press < 2; press++)
2690 	  {
2691 	    int button_num = but;
2692 
2693 	    if (press)
2694 	      ok = mouse_pressed (but, &x, &y);
2695 	    else
2696 	      ok = mouse_released (but, &x, &y);
2697 	    if (ok)
2698 	      {
2699 		/* Allow a simultaneous press/release of Mouse-1 and
2700 		   Mouse-2 to simulate Mouse-3 on two-button mice.  */
2701 		if (mouse_button_count == 2 && but < 2)
2702 		  {
2703 		    int x2, y2;	/* don't clobber original coordinates */
2704 
2705 		    /* If only one button is pressed, wait 100 msec and
2706 		       check again.  This way, Speedy Gonzales isn't
2707 		       punished, while the slow get their chance.  */
2708 		    if ((press && mouse_pressed (1-but, &x2, &y2))
2709 			|| (!press && mouse_released (1-but, &x2, &y2)))
2710 		      button_num = 2;
2711 		    else
2712 		      {
2713 			delay (100);
2714 			if ((press && mouse_pressed (1-but, &x2, &y2))
2715 			    || (!press && mouse_released (1-but, &x2, &y2)))
2716 			  button_num = 2;
2717 		      }
2718 		  }
2719 
2720 		event.kind = MOUSE_CLICK_EVENT;
2721 		event.code = button_num;
2722 		event.modifiers = dos_get_modifiers (0)
2723 		  | (press ? down_modifier : up_modifier);
2724 		event.x = make_fixnum (x);
2725 		event.y = make_fixnum (y);
2726 		event.frame_or_window = selected_frame;
2727 		event.arg = Qnil;
2728 		event.timestamp = event_timestamp ();
2729 		kbd_buffer_store_event (&event);
2730 	      }
2731 	  }
2732     }
2733 
2734   return -1;
2735 }
2736 
2737 static int prev_get_char = -1;
2738 
2739 /* Return 1 if a key is ready to be read without suspending execution.  */
2740 int
dos_keysns(void)2741 dos_keysns (void)
2742 {
2743   if (prev_get_char != -1)
2744     return 1;
2745   else
2746     return ((prev_get_char = dos_rawgetc ()) != -1);
2747 }
2748 
2749 /* Read a key.  Return -1 if no key is ready.  */
2750 int
dos_keyread(void)2751 dos_keyread (void)
2752 {
2753   if (prev_get_char != -1)
2754     {
2755       int c = prev_get_char;
2756       prev_get_char = -1;
2757       return c;
2758     }
2759   else
2760     return dos_rawgetc ();
2761 }
2762 
2763 #ifndef HAVE_X_WINDOWS
2764 
2765 /* Simulation of X's menus.  Nothing too fancy here -- just make it work
2766    for now.
2767 
2768    Actually, I don't know the meaning of all the parameters of the functions
2769    here -- I only know how they are called by xmenu.c.  I could of course
2770    grab the nearest Xlib manual (down the hall, second-to-last door on the
2771    left), but I don't think it's worth the effort.  */
2772 
2773 /* These hold text of the current and the previous menu help messages.  */
2774 static const char *menu_help_message, *prev_menu_help_message;
2775 /* Pane number and item number of the menu item which generated the
2776    last menu help message.  */
2777 static int menu_help_paneno, menu_help_itemno;
2778 
2779 static XMenu *
IT_menu_create(void)2780 IT_menu_create (void)
2781 {
2782   XMenu *menu;
2783 
2784   menu = xmalloc (sizeof (XMenu));
2785   menu->allocated = menu->count = menu->panecount = menu->width = 0;
2786   return menu;
2787 }
2788 
2789 /* Allocate some (more) memory for MENU ensuring that there is room for one
2790    for item.  */
2791 
2792 static void
IT_menu_make_room(XMenu * menu)2793 IT_menu_make_room (XMenu *menu)
2794 {
2795   if (menu->allocated == 0)
2796     {
2797       int count = menu->allocated = 10;
2798       menu->text = xmalloc (count * sizeof (char *));
2799       menu->submenu = xmalloc (count * sizeof (XMenu *));
2800       menu->panenumber = xmalloc (count * sizeof (int));
2801       menu->help_text = xmalloc (count * sizeof (char *));
2802     }
2803   else if (menu->allocated == menu->count)
2804     {
2805       int count = menu->allocated = menu->allocated + 10;
2806       menu->text
2807 	= (char **) xrealloc (menu->text, count * sizeof (char *));
2808       menu->submenu
2809 	= (XMenu **) xrealloc (menu->submenu, count * sizeof (XMenu *));
2810       menu->panenumber
2811 	= (int *) xrealloc (menu->panenumber, count * sizeof (int));
2812       menu->help_text
2813 	= (const char **) xrealloc (menu->help_text, count * sizeof (char *));
2814     }
2815 }
2816 
2817 /* Search the given menu structure for a given pane number.  */
2818 
2819 static XMenu *
IT_menu_search_pane(XMenu * menu,int pane)2820 IT_menu_search_pane (XMenu *menu, int pane)
2821 {
2822   int i;
2823   XMenu *try;
2824 
2825   for (i = 0; i < menu->count; i++)
2826     if (menu->submenu[i])
2827       {
2828 	if (pane == menu->panenumber[i])
2829 	  return menu->submenu[i];
2830 	if ((try = IT_menu_search_pane (menu->submenu[i], pane)))
2831 	  return try;
2832       }
2833   return (XMenu *) 0;
2834 }
2835 
2836 /* Determine how much screen space a given menu needs.  */
2837 
2838 static void
IT_menu_calc_size(XMenu * menu,int * width,int * height)2839 IT_menu_calc_size (XMenu *menu, int *width, int *height)
2840 {
2841   int i, h2, w2, maxsubwidth, maxheight;
2842 
2843   maxsubwidth = 0;
2844   maxheight = menu->count;
2845   for (i = 0; i < menu->count; i++)
2846     {
2847       if (menu->submenu[i])
2848 	{
2849 	  IT_menu_calc_size (menu->submenu[i], &w2, &h2);
2850 	  if (w2 > maxsubwidth) maxsubwidth = w2;
2851 	  if (i + h2 > maxheight) maxheight = i + h2;
2852 	}
2853     }
2854   *width = menu->width + maxsubwidth;
2855   *height = maxheight;
2856 }
2857 
2858 /* Display MENU at (X,Y) using FACES.  */
2859 
2860 #define BUILD_CHAR_GLYPH(GLYPH, CODE, FACE_ID, PADDING_P)  \
2861   do							   \
2862     {							   \
2863       (GLYPH).type = CHAR_GLYPH;			   \
2864       SET_CHAR_GLYPH ((GLYPH), CODE, FACE_ID, PADDING_P);  \
2865       (GLYPH).charpos = -1;				   \
2866     }							   \
2867   while (0)
2868 
2869 static void
IT_menu_display(XMenu * menu,int y,int x,int pn,int * faces,int disp_help)2870 IT_menu_display (XMenu *menu, int y, int x, int pn, int *faces, int disp_help)
2871 {
2872   int i, j, face, width,  mx, my, enabled, mousehere, row, col;
2873   struct glyph *text, *p;
2874   const unsigned char *q;
2875   struct frame *sf = SELECTED_FRAME ();
2876 
2877   menu_help_message = NULL;
2878 
2879   width = menu->width;
2880   /* We multiply width by 2 to account for possible control characters.
2881      FIXME: cater to non-ASCII characters in menus.  */
2882   text = xmalloc ((width * 2 + 2) * sizeof (struct glyph));
2883   ScreenGetCursor (&row, &col);
2884   mouse_get_xy (&mx, &my);
2885   IT_update_begin (sf);
2886   for (i = 0; i < menu->count; i++)
2887     {
2888       int max_width = width + 2;
2889 
2890       IT_cursor_to (sf, y + i, x);
2891       enabled
2892 	= (!menu->submenu[i] && menu->panenumber[i]) || (menu->submenu[i]);
2893       mousehere = (y + i == my && x <= mx && mx < x + max_width);
2894       face = faces[enabled + mousehere * 2];
2895       /* The following if clause means that we display the menu help
2896 	 strings even if the menu item is currently disabled.  */
2897       if (disp_help && enabled + mousehere * 2 >= 2)
2898 	{
2899 	  menu_help_message = menu->help_text[i];
2900 	  menu_help_paneno = pn - 1;
2901 	  menu_help_itemno = i;
2902 	}
2903       p = text;
2904       BUILD_CHAR_GLYPH (*p, ' ', face, 0);
2905       p++;
2906       for (j = 0, q = menu->text[i]; *q; j++)
2907 	{
2908 	  unsigned c = STRING_CHAR_ADVANCE (q);
2909 
2910 	  if (c > 26)
2911 	    {
2912 	      BUILD_CHAR_GLYPH (*p, c, face, 0);
2913 	      p++;
2914 	    }
2915 	  else	/* make '^x' */
2916 	    {
2917 	      BUILD_CHAR_GLYPH (*p, '^', face, 0);
2918 	      p++;
2919 	      j++;
2920 	      BUILD_CHAR_GLYPH (*p, c + 64, face, 0);
2921 	      p++;
2922 	    }
2923 	}
2924       /* Don't let the menu text overflow into the next screen row.  */
2925       if (x + max_width > screen_size_X)
2926 	{
2927 	  max_width = screen_size_X - x;
2928 	  text[max_width - 1].u.ch = '$'; /* indicate it's truncated */
2929 	}
2930       for (; j < max_width - 2; j++, p++)
2931 	BUILD_CHAR_GLYPH (*p, ' ', face, 0);
2932 
2933       /* 16 is the character code of a character that on DOS terminal
2934 	 produces a nice-looking right-pointing arrow glyph.  */
2935       BUILD_CHAR_GLYPH (*p, menu->submenu[i] ? 16 : ' ', face, 0);
2936       p++;
2937       IT_write_glyphs (sf, text, max_width);
2938     }
2939   IT_update_end (sf);
2940   IT_cursor_to (sf, row, col);
2941   xfree (text);
2942 }
2943 
2944 /* --------------------------- X Menu emulation ---------------------- */
2945 
2946 /* Create a brand new menu structure.  */
2947 
2948 XMenu *
XMenuCreate(Display * foo1,Window foo2,char * foo3)2949 XMenuCreate (Display *foo1, Window foo2, char *foo3)
2950 {
2951   return IT_menu_create ();
2952 }
2953 
2954 /* Create a new pane and place it on the outer-most level.  It is not
2955    clear that it should be placed out there, but I don't know what else
2956    to do.  */
2957 
2958 int
XMenuAddPane(Display * foo,XMenu * menu,const char * txt,int enable)2959 XMenuAddPane (Display *foo, XMenu *menu, const char *txt, int enable)
2960 {
2961   int len;
2962   const char *p;
2963 
2964   if (!enable)
2965     emacs_abort ();
2966 
2967   IT_menu_make_room (menu);
2968   menu->submenu[menu->count] = IT_menu_create ();
2969   menu->text[menu->count] = (char *)txt;
2970   menu->panenumber[menu->count] = ++menu->panecount;
2971   menu->help_text[menu->count] = NULL;
2972   menu->count++;
2973 
2974   /* Adjust length for possible control characters (which will
2975      be written as ^x).  */
2976   for (len = strlen (txt), p = txt; *p; p++)
2977     if (*p < 27)
2978       len++;
2979 
2980   if (len > menu->width)
2981     menu->width = len;
2982 
2983   return menu->panecount;
2984 }
2985 
2986 /* Create a new item in a menu pane.  */
2987 
2988 int
XMenuAddSelection(Display * bar,XMenu * menu,int pane,int foo,char * txt,int enable,char const * help_text)2989 XMenuAddSelection (Display *bar, XMenu *menu, int pane,
2990 		   int foo, char *txt, int enable, char const *help_text)
2991 {
2992   int len;
2993   char *p;
2994 
2995   if (pane)
2996     if (!(menu = IT_menu_search_pane (menu, pane)))
2997       return XM_FAILURE;
2998   IT_menu_make_room (menu);
2999   menu->submenu[menu->count] = (XMenu *) 0;
3000   menu->text[menu->count] = txt;
3001   menu->panenumber[menu->count] = enable;
3002   menu->help_text[menu->count] = help_text;
3003   menu->count++;
3004 
3005   /* Adjust length for possible control characters (which will
3006      be written as ^x).  */
3007   for (len = strlen (txt), p = txt; *p; p++)
3008     if (*p < 27)
3009       len++;
3010 
3011   if (len > menu->width)
3012     menu->width = len;
3013 
3014   return XM_SUCCESS;
3015 }
3016 
3017 /* Decide where the menu would be placed if requested at (X,Y).  */
3018 
3019 void
XMenuLocate(Display * foo0,XMenu * menu,int foo1,int foo2,int x,int y,int * ulx,int * uly,int * width,int * height)3020 XMenuLocate (Display *foo0, XMenu *menu, int foo1, int foo2, int x, int y,
3021 	     int *ulx, int *uly, int *width, int *height)
3022 {
3023   IT_menu_calc_size (menu, width, height);
3024   *ulx = x + 1;
3025   *uly = y;
3026   *width += 2;
3027 }
3028 
3029 struct IT_menu_state
3030 {
3031   void *screen_behind;
3032   XMenu *menu;
3033   int pane;
3034   int x, y;
3035 };
3036 
3037 
3038 /* Display menu, wait for user's response, and return that response.  */
3039 
3040 int
XMenuActivate(Display * foo,XMenu * menu,int * pane,int * selidx,int x0,int y0,unsigned ButtonMask,char ** txt,void (* help_callback)(char const *,int,int))3041 XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx,
3042 	       int x0, int y0, unsigned ButtonMask, char **txt,
3043 	       void (*help_callback)(char const *, int, int))
3044 {
3045   struct IT_menu_state *state;
3046   int statecount, x, y, i, b, screensize, leave, result, onepane;
3047   int title_faces[4];		/* face to display the menu title */
3048   int faces[4], buffers_num_deleted = 0;
3049   struct frame *sf = SELECTED_FRAME ();
3050   Lisp_Object saved_echo_area_message, selectface;
3051 
3052   /* Just in case we got here without a mouse present...  */
3053   if (have_mouse <= 0)
3054     return XM_IA_SELECT;
3055   /* Don't allow non-positive x0 and y0, lest the menu will wrap
3056      around the display.  */
3057   if (x0 <= 0)
3058     x0 = 1;
3059   if (y0 <= 0)
3060     y0 = 1;
3061 
3062   /* We will process all the mouse events directly, so we had
3063      better prevent dos_rawgetc from stealing them from us.  */
3064   mouse_preempted++;
3065 
3066   state = alloca (menu->panecount * sizeof (struct IT_menu_state));
3067   screensize = screen_size * 2;
3068   faces[0]
3069     = lookup_derived_face (NULL, sf, intern ("msdos-menu-passive-face"),
3070 			   DEFAULT_FACE_ID, 1);
3071   faces[1]
3072     = lookup_derived_face (NULL, sf, intern ("msdos-menu-active-face"),
3073 			   DEFAULT_FACE_ID, 1);
3074   selectface = intern ("msdos-menu-select-face");
3075   faces[2] = lookup_derived_face (NULL, sf, selectface,
3076 				  faces[0], 1);
3077   faces[3] = lookup_derived_face (NULL, sf, selectface,
3078 				  faces[1], 1);
3079 
3080   /* Make sure the menu title is always displayed with
3081      `msdos-menu-active-face', no matter where the mouse pointer is.  */
3082   for (i = 0; i < 4; i++)
3083     title_faces[i] = faces[3];
3084 
3085   statecount = 1;
3086 
3087   /* Don't let the title for the "Buffers" popup menu include a
3088      digit (which is ugly).
3089 
3090      This is a terrible kludge, but I think the "Buffers" case is
3091      the only one where the title includes a number, so it doesn't
3092      seem to be necessary to make this more general.  */
3093   if (strncmp (menu->text[0], "Buffers 1", 9) == 0)
3094     {
3095       menu->text[0][7] = '\0';
3096       buffers_num_deleted = 1;
3097     }
3098 
3099   /* We need to save the current echo area message, so that we could
3100      restore it below, before we exit.  See the commentary below,
3101      before the call to message_with_string.  */
3102   saved_echo_area_message = Fcurrent_message ();
3103   state[0].menu = menu;
3104   mouse_off ();
3105   ScreenRetrieve (state[0].screen_behind = xmalloc (screensize));
3106 
3107   /* Turn off the cursor.  Otherwise it shows through the menu
3108      panes, which is ugly.  */
3109   IT_display_cursor (0);
3110 
3111   /* Display the menu title.  */
3112   IT_menu_display (menu, y0 - 1, x0 - 1, 1, title_faces, 0);
3113   if (buffers_num_deleted)
3114     menu->text[0][7] = ' ';
3115   if ((onepane = menu->count == 1 && menu->submenu[0]))
3116     {
3117       menu->width = menu->submenu[0]->width;
3118       state[0].menu = menu->submenu[0];
3119     }
3120   else
3121     {
3122       state[0].menu = menu;
3123     }
3124   state[0].x = x0 - 1;
3125   state[0].y = y0;
3126   state[0].pane = onepane;
3127 
3128   mouse_last_x = -1;  /* A hack that forces display.  */
3129   leave = 0;
3130   while (!leave)
3131     {
3132       if (!mouse_visible) mouse_on ();
3133       mouse_check_moved ();
3134       if (sf->mouse_moved)
3135 	{
3136 	  sf->mouse_moved = 0;
3137 	  result = XM_IA_SELECT;
3138 	  mouse_get_xy (&x, &y);
3139 	  for (i = 0; i < statecount; i++)
3140 	    if (state[i].x <= x && x < state[i].x + state[i].menu->width + 2)
3141 	      {
3142 		int dy = y - state[i].y;
3143 		if (0 <= dy && dy < state[i].menu->count)
3144 		  {
3145 		    if (!state[i].menu->submenu[dy])
3146 		      {
3147 			if (state[i].menu->panenumber[dy])
3148 			  result = XM_SUCCESS;
3149 			else
3150 			  result = XM_IA_SELECT;
3151 		      }
3152 		    *pane = state[i].pane - 1;
3153 		    *selidx = dy;
3154 		    /* We hit some part of a menu, so drop extra menus that
3155 		       have been opened.  That does not include an open and
3156 		       active submenu.  */
3157 		    if (i != statecount - 2
3158 			|| state[i].menu->submenu[dy] != state[i+1].menu)
3159 		      while (i != statecount - 1)
3160 			{
3161 			  statecount--;
3162 			  mouse_off ();
3163 			  ScreenUpdate (state[statecount].screen_behind);
3164 			  if (screen_virtual_segment)
3165 			    dosv_refresh_virtual_screen (0, screen_size);
3166 			  xfree (state[statecount].screen_behind);
3167 			}
3168 		    if (i == statecount - 1 && state[i].menu->submenu[dy])
3169 		      {
3170 			IT_menu_display (state[i].menu,
3171 					 state[i].y,
3172 					 state[i].x,
3173 					 state[i].pane,
3174 					 faces, 1);
3175 			state[statecount].menu = state[i].menu->submenu[dy];
3176 			state[statecount].pane = state[i].menu->panenumber[dy];
3177 			mouse_off ();
3178 			ScreenRetrieve (state[statecount].screen_behind
3179 					= xmalloc (screensize));
3180 			state[statecount].x
3181 			  = state[i].x + state[i].menu->width + 2;
3182 			state[statecount].y = y;
3183 			statecount++;
3184 		      }
3185 		  }
3186 	      }
3187 	  IT_menu_display (state[statecount - 1].menu,
3188 			   state[statecount - 1].y,
3189 			   state[statecount - 1].x,
3190 			   state[statecount - 1].pane,
3191 			   faces, 1);
3192 	}
3193       else
3194 	{
3195 	  if ((menu_help_message || prev_menu_help_message)
3196 	      && menu_help_message != prev_menu_help_message)
3197 	    {
3198 	      help_callback (menu_help_message,
3199 			     menu_help_paneno, menu_help_itemno);
3200 	      IT_display_cursor (0);
3201 	      prev_menu_help_message = menu_help_message;
3202 	    }
3203 	  /* We are busy-waiting for the mouse to move, so let's be nice
3204 	     to other Windows applications by releasing our time slice.  */
3205 	  __dpmi_yield ();
3206 	}
3207       for (b = 0; b < mouse_button_count && !leave; b++)
3208 	{
3209 	  /* Only leave if user both pressed and released the mouse, and in
3210 	     that order.  This avoids popping down the menu pane unless
3211 	     the user is really done with it.  */
3212 	  if (mouse_pressed (b, &x, &y))
3213 	    {
3214 	      while (mouse_button_depressed (b, &x, &y))
3215 		__dpmi_yield ();
3216 	      leave = 1;
3217 	    }
3218 	  (void) mouse_released (b, &x, &y);
3219 	}
3220     }
3221 
3222   mouse_off ();
3223   ScreenUpdate (state[0].screen_behind);
3224   if (screen_virtual_segment)
3225     dosv_refresh_virtual_screen (0, screen_size);
3226 
3227   /* We have a situation here.  ScreenUpdate has just restored the
3228      screen contents as it was before we started drawing this menu.
3229      That includes any echo area message that could have been
3230      displayed back then.  (In reality, that echo area message will
3231      almost always be the ``keystroke echo'' that echoes the sequence
3232      of menu items chosen by the user.)  However, if the menu had some
3233      help messages, then displaying those messages caused Emacs to
3234      forget about the original echo area message.  So when
3235      ScreenUpdate restored it, it created a discrepancy between the
3236      actual screen contents and what Emacs internal data structures
3237      know about it.
3238 
3239      To avoid this conflict, we force Emacs to restore the original
3240      echo area message as we found it when we entered this function.
3241      The irony of this is that we then erase the restored message
3242      right away, so the only purpose of restoring it is so that
3243      erasing it works correctly...  */
3244   if (! NILP (saved_echo_area_message))
3245     message_with_string ("%s", saved_echo_area_message, 0);
3246   message1 (0);
3247   while (statecount--)
3248     xfree (state[statecount].screen_behind);
3249   IT_display_cursor (1);	/* Turn cursor back on.  */
3250   /* Clean up any mouse events that are waiting inside Emacs event queue.
3251      These events are likely to be generated before the menu was even
3252      displayed, probably because the user pressed and released the button
3253      (which invoked the menu) too quickly.  If we don't remove these events,
3254      Emacs will process them after we return and surprise the user.  */
3255   discard_mouse_events ();
3256   mouse_clear_clicks ();
3257   if (!kbd_buffer_events_waiting ())
3258     clear_input_pending ();
3259   /* Allow mouse events generation by dos_rawgetc.  */
3260   mouse_preempted--;
3261   return result;
3262 }
3263 
3264 /* Dispose of a menu.  */
3265 
3266 void
XMenuDestroy(Display * foo,XMenu * menu)3267 XMenuDestroy (Display *foo, XMenu *menu)
3268 {
3269   int i;
3270   if (menu->allocated)
3271     {
3272       for (i = 0; i < menu->count; i++)
3273 	if (menu->submenu[i])
3274 	  XMenuDestroy (foo, menu->submenu[i]);
3275       xfree (menu->text);
3276       xfree (menu->submenu);
3277       xfree (menu->panenumber);
3278       xfree (menu->help_text);
3279     }
3280   xfree (menu);
3281   menu_help_message = prev_menu_help_message = NULL;
3282 }
3283 #endif /* !HAVE_X_WINDOWS */
3284 
3285 /* ----------------------- DOS / UNIX conversion --------------------- */
3286 
3287 void msdos_downcase_filename (unsigned char *);
3288 
3289 /* Destructively turn backslashes into slashes.  */
3290 
3291 void
dostounix_filename(char * p)3292 dostounix_filename (char *p)
3293 {
3294   msdos_downcase_filename (p);
3295 
3296   while (*p)
3297     {
3298       if (*p == '\\')
3299 	*p = '/';
3300       p++;
3301     }
3302 }
3303 
3304 /* Destructively turn slashes into backslashes.  */
3305 
3306 void
unixtodos_filename(char * p)3307 unixtodos_filename (char *p)
3308 {
3309   if (p[1] == ':' && *p >= 'A' && *p <= 'Z')
3310     {
3311       *p += 'a' - 'A';
3312       p += 2;
3313     }
3314 
3315   while (*p)
3316     {
3317       if (*p == '/')
3318 	*p = '\\';
3319       p++;
3320     }
3321 }
3322 
3323 /* Get the default directory for a given drive.  0=def, 1=A, 2=B, ...  */
3324 
3325 int
getdefdir(int drive,char * dst)3326 getdefdir (int drive, char *dst)
3327 {
3328   char in_path[4], *p = in_path, e = errno;
3329 
3330   /* Generate "X:." (when drive is X) or "." (when drive is 0).  */
3331   if (drive != 0)
3332     {
3333       *p++ = drive + 'A' - 1;
3334       *p++ = ':';
3335     }
3336 
3337   *p++ = '.';
3338   *p = '\0';
3339   errno = 0;
3340   _fixpath (in_path, dst);
3341     /* _fixpath can set errno to ENOSYS on non-LFN systems because
3342        it queries the LFN support, so ignore that error.  */
3343   if ((errno && errno != ENOSYS) || *dst == '\0')
3344     return 0;
3345 
3346   msdos_downcase_filename (dst);
3347 
3348   errno = e;
3349   return 1;
3350 }
3351 
3352 char *
emacs_root_dir(void)3353 emacs_root_dir (void)
3354 {
3355   static char root_dir[4];
3356 
3357   sprintf (root_dir, "%c:/", 'A' + getdisk ());
3358   root_dir[0] = tolower (root_dir[0]);
3359   return root_dir;
3360 }
3361 
3362 /* Remove all CR's that are followed by a LF.  */
3363 
3364 int
crlf_to_lf(int n,unsigned char * buf)3365 crlf_to_lf (int n, unsigned char *buf)
3366 {
3367   unsigned char *np = buf, *startp = buf, *endp = buf + n;
3368 
3369   if (n == 0)
3370     return n;
3371   while (buf < endp - 1)
3372     {
3373       if (*buf == 0x0d)
3374 	{
3375 	  if (*(++buf) != 0x0a)
3376 	    *np++ = 0x0d;
3377 	}
3378       else
3379 	*np++ = *buf++;
3380     }
3381   if (buf < endp)
3382     *np++ = *buf++;
3383   return np - startp;
3384 }
3385 
3386 DEFUN ("msdos-long-file-names", Fmsdos_long_file_names, Smsdos_long_file_names,
3387        0, 0, 0,
3388        doc: /* Return non-nil if long file names are supported on MS-DOS.  */)
3389   (void)
3390 {
3391   return (_USE_LFN ? Qt : Qnil);
3392 }
3393 
3394 /* Convert alphabetic characters in a filename to lower-case.  */
3395 
3396 void
msdos_downcase_filename(unsigned char * p)3397 msdos_downcase_filename (unsigned char *p)
3398 {
3399   /* Always lower-case drive letters a-z, even if the filesystem
3400      preserves case in filenames.
3401      This is so MSDOS filenames could be compared by string comparison
3402      functions that are case-sensitive.  Even case-preserving filesystems
3403      do not distinguish case in drive letters.  */
3404   if (p[1] == ':' && *p >= 'A' && *p <= 'Z')
3405     {
3406       *p += 'a' - 'A';
3407       p += 2;
3408     }
3409 
3410   /* Under LFN we expect to get pathnames in their true case.  */
3411   if (NILP (Fmsdos_long_file_names ()))
3412     for ( ; *p; p++)
3413       if (*p >= 'A' && *p <= 'Z')
3414 	*p += 'a' - 'A';
3415 }
3416 
3417 DEFUN ("msdos-downcase-filename", Fmsdos_downcase_filename, Smsdos_downcase_filename,
3418        1, 1, 0,
3419        doc: /* Convert alphabetic characters in FILENAME to lower case and return that.
3420 When long filenames are supported, doesn't change FILENAME.
3421 If FILENAME is not a string, returns nil.
3422 The argument object is never altered--the value is a copy.  */)
3423   (Lisp_Object filename)
3424 {
3425   Lisp_Object tem;
3426 
3427   if (! STRINGP (filename))
3428     return Qnil;
3429 
3430   tem = Fcopy_sequence (filename);
3431   msdos_downcase_filename (SDATA (tem));
3432   return tem;
3433 }
3434 
3435 /* The Emacs root directory as determined by init_environment.  */
3436 
3437 static char emacsroot[MAXPATHLEN];
3438 
3439 char *
rootrelativepath(char * rel)3440 rootrelativepath (char *rel)
3441 {
3442   static char result[MAXPATHLEN + 10];
3443 
3444   strcpy (result, emacsroot);
3445   strcat (result, "/");
3446   strcat (result, rel);
3447   return result;
3448 }
3449 
3450 /* Define a lot of environment variables if not already defined.  Don't
3451    remove anything unless you know what you're doing -- lots of code will
3452    break if one or more of these are missing.  */
3453 
3454 void
init_environment(int argc,char ** argv,int skip_args)3455 init_environment (int argc, char **argv, int skip_args)
3456 {
3457   char *s, *t, *root;
3458   int len, i;
3459   static const char * const tempdirs[] = {
3460     "$TMPDIR", "$TEMP", "$TMP", "c:/"
3461   };
3462   const int imax = ARRAYELTS (tempdirs);
3463 
3464   /* Make sure they have a usable $TMPDIR.  Many Emacs functions use
3465      temporary files and assume "/tmp" if $TMPDIR is unset, which
3466      will break on DOS/Windows.  Refuse to work if we cannot find
3467      a directory, not even "c:/", usable for that purpose.  */
3468   for (i = 0; i < imax ; i++)
3469     {
3470       const char *tmp = tempdirs[i];
3471       char buf[FILENAME_MAX];
3472 
3473       if (*tmp == '$')
3474 	{
3475 	  int tmp_len;
3476 
3477 	  tmp = getenv (tmp + 1);
3478 	  if (!tmp)
3479 	    continue;
3480 
3481 	  /* Some lusers set TMPDIR=e:, probably because some losing
3482 	     programs cannot handle multiple slashes if they use e:/.
3483 	     e: fails in `access' below, so we interpret e: as e:/.  */
3484 	  tmp_len = strlen (tmp);
3485 	  if (tmp[tmp_len - 1] != '/' && tmp[tmp_len - 1] != '\\')
3486 	    {
3487 	      strcpy (buf, tmp);
3488 	      buf[tmp_len++] = '/', buf[tmp_len] = 0;
3489 	      tmp = buf;
3490 	    }
3491 	}
3492 
3493       /* Note that `access' can lie to us if the directory resides on a
3494 	 read-only filesystem, like CD-ROM or a write-protected floppy.
3495 	 The only way to be really sure is to actually create a file and
3496 	 see if it succeeds.  But I think that's too much to ask.  */
3497       if (tmp && access (tmp, D_OK) == 0)
3498 	{
3499 	  setenv ("TMPDIR", tmp, 1);
3500 	  break;
3501 	}
3502     }
3503   if (i >= imax)
3504     cmd_error_internal
3505       (Fcons (Qerror,
3506 	      Fcons (build_string ("no usable temporary directories found!!"),
3507 		     Qnil)),
3508        "While setting TMPDIR: ");
3509 
3510   /* Note the startup time, so we know not to clear the screen if we
3511      exit immediately; see IT_reset_terminal_modes.
3512      (Yes, I know `clock' returns zero the first time it's called, but
3513      I do this anyway, in case some wiseguy changes that at some point.)  */
3514   startup_time = clock ();
3515 
3516   /* Find our root from argv[0].  Assuming argv[0] is, say,
3517      "c:/emacs/bin/emacs.exe" our root will be "c:/emacs".  */
3518   root = alloca (MAXPATHLEN + 20);
3519   _fixpath (argv[0], root);
3520   msdos_downcase_filename (root);
3521   len = strlen (root);
3522   while (len > 0 && root[len] != '/' && root[len] != ':')
3523     len--;
3524   root[len] = '\0';
3525   if (len > 4
3526       && (strcmp (root + len - 4, "/bin") == 0
3527 	  || strcmp (root + len - 4, "/src") == 0)) /* under a debugger */
3528     root[len - 4] = '\0';
3529   else
3530     strcpy (root, "c:/emacs");  /* let's be defensive */
3531   len = strlen (root);
3532   strcpy (emacsroot, root);
3533 
3534   /* We default HOME to our root.  */
3535   setenv ("HOME", root, 0);
3536 
3537   /* We default EMACSPATH to root + "/bin".  */
3538   strcpy (root + len, "/bin");
3539   setenv ("EMACSPATH", root, 0);
3540 
3541   /* I don't expect anybody to ever use other terminals so the internal
3542      terminal is the default.  */
3543   setenv ("TERM", "internal", 0);
3544 
3545 #ifdef HAVE_X_WINDOWS
3546   /* Emacs expects DISPLAY to be set.  */
3547   setenv ("DISPLAY", "unix:0.0", 0);
3548 #endif
3549 
3550   /* SHELL is a bit tricky -- COMSPEC is the closest we come, but we must
3551      downcase it and mirror the backslashes.  */
3552   s = getenv ("COMSPEC");
3553   if (!s) s = "c:/command.com";
3554   t = alloca (strlen (s) + 1);
3555   strcpy (t, s);
3556   dostounix_filename (t);
3557   setenv ("SHELL", t, 0);
3558 
3559   /* PATH is also downcased and backslashes mirrored.  */
3560   s = getenv ("PATH");
3561   if (!s) s = "";
3562   t = alloca (strlen (s) + 3);
3563   /* Current directory is always considered part of MsDos's path but it is
3564      not normally mentioned.  Now it is.  */
3565   strcat (strcpy (t, ".;"), s);
3566   dostounix_filename (t); /* Not a single file name, but this should work.  */
3567   setenv ("PATH", t, 1);
3568 
3569   /* In some sense all dos users have root privileges, so...  */
3570   setenv ("USER", "root", 0);
3571   setenv ("NAME", getenv ("USER"), 0);
3572 
3573   /* Time zone determined from country code.  To make this possible, the
3574      country code may not span more than one time zone.  In other words,
3575      in the USA, you lose.  */
3576   if (!getenv ("TZ"))
3577     switch (dos_country_code)
3578       {
3579       case 31:			/* Belgium */
3580       case 32:			/* The Netherlands */
3581       case 33:			/* France */
3582       case 34:			/* Spain */
3583       case 36:			/* Hungary */
3584       case 38:			/* Yugoslavia (or what's left of it?) */
3585       case 39:			/* Italy */
3586       case 41:			/* Switzerland */
3587       case 42:			/* Tjekia */
3588       case 45:			/* Denmark */
3589       case 46:			/* Sweden */
3590       case 47:			/* Norway */
3591       case 48:			/* Poland */
3592       case 49:			/* Germany */
3593 	/* Daylight saving from last Sunday in March to last Sunday in
3594 	   September, both at 2AM.  */
3595 	setenv ("TZ", "MET-01METDST-02,M3.5.0/02:00,M9.5.0/02:00", 0);
3596 	break;
3597       case 44:			/* United Kingdom */
3598       case 351:			/* Portugal */
3599       case 354:			/* Iceland */
3600 	setenv ("TZ", "GMT+00", 0);
3601 	break;
3602       case 81:			/* Japan */
3603       case 82:			/* Korea */
3604 	setenv ("TZ", "JST-09", 0);
3605 	break;
3606       case 90:			/* Turkey */
3607       case 358:			/* Finland */
3608 	setenv ("TZ", "EET-02", 0);
3609 	break;
3610       case 972:			/* Israel */
3611 	/* This is an approximation.  (For exact rules, use the
3612 	   `zoneinfo/israel' file which comes with DJGPP, but you need
3613 	   to install it in `/usr/share/zoneinfo/' directory first.)  */
3614 	setenv ("TZ", "IST-02IDT-03,M4.1.6/00:00,M9.5.6/01:00", 0);
3615 	break;
3616       }
3617   tzset ();
3618 }
3619 
3620 
3621 
3622 static int break_stat;	 /* BREAK check mode status.	*/
3623 static int stdin_stat;	 /* stdin IOCTL status.		*/
3624 
3625 /* Turn off Dos' Ctrl-C checking and inhibit interpretation of
3626    control chars by DOS.   Determine the keyboard type.  */
3627 
3628 int
dos_ttraw(struct tty_display_info * tty)3629 dos_ttraw (struct tty_display_info *tty)
3630 {
3631   union REGS inregs, outregs;
3632   static int first_time = 1;
3633 
3634   /* If we are called for the initial terminal, it's too early to do
3635      anything, and termscript isn't set up.  */
3636   if (tty->terminal->type == output_initial)
3637     return 2;
3638 
3639   break_stat = getcbrk ();
3640   setcbrk (0);
3641 
3642   if (first_time)
3643     {
3644       inregs.h.ah = 0xc0;
3645       int86 (0x15, &inregs, &outregs);
3646       extended_kbd = (!outregs.x.cflag) && (outregs.h.ah == 0);
3647 
3648       have_mouse = 0;
3649 
3650       if (1
3651 #ifdef HAVE_X_WINDOWS
3652 	  && inhibit_window_system
3653 #endif
3654 	  )
3655 	{
3656 	  inregs.x.ax = 0x0021;
3657 	  int86 (0x33, &inregs, &outregs);
3658 	  have_mouse = (outregs.x.ax & 0xffff) == 0xffff;
3659 	  if (!have_mouse)
3660 	    {
3661 	      /* Reportedly, the above doesn't work for some mouse drivers.  There
3662 		 is an additional detection method that should work, but might be
3663 		 a little slower.  Use that as an alternative.  */
3664 	      inregs.x.ax = 0x0000;
3665 	      int86 (0x33, &inregs, &outregs);
3666 	      have_mouse = (outregs.x.ax & 0xffff) == 0xffff;
3667 	    }
3668 	  if (have_mouse)
3669 	    mouse_button_count = outregs.x.bx;
3670 
3671 #ifndef HAVE_X_WINDOWS
3672 	  /* Save the cursor shape used outside Emacs.  */
3673 	  outside_cursor = _farpeekw (_dos_ds, 0x460);
3674 #endif
3675 	}
3676 
3677       first_time = 0;
3678 
3679       stdin_stat = setmode (fileno (stdin), O_BINARY);
3680       return (stdin_stat != -1);
3681     }
3682   else
3683     return (setmode (fileno (stdin), O_BINARY) != -1);
3684 }
3685 
3686 /*  Restore status of standard input and Ctrl-C checking.  */
3687 
3688 int
dos_ttcooked(void)3689 dos_ttcooked (void)
3690 {
3691   union REGS inregs, outregs;
3692 
3693   setcbrk (break_stat);
3694   mouse_off ();
3695 
3696 #ifndef HAVE_X_WINDOWS
3697   /* Restore the cursor shape we found on startup.  */
3698   if (outside_cursor)
3699     {
3700       inregs.h.ah = 1;
3701       inregs.x.cx = outside_cursor;
3702       int86 (0x10, &inregs, &outregs);
3703     }
3704 #endif
3705 
3706   return (setmode (fileno (stdin), stdin_stat) != -1);
3707 }
3708 
3709 
3710 /* Run command as specified by ARGV in directory DIR.
3711    The command is run with input from TEMPIN, output to
3712    file TEMPOUT and stderr to TEMPERR.  */
3713 
3714 int
run_msdos_command(char ** argv,const char * working_dir,int tempin,int tempout,int temperr,char ** envv)3715 run_msdos_command (char **argv, const char *working_dir,
3716 		   int tempin, int tempout, int temperr, char **envv)
3717 {
3718   char *saveargv1, *saveargv2, *lowcase_argv0, *pa, *pl;
3719   char oldwd[MAXPATHLEN + 1]; /* Fixed size is safe on MSDOS.  */
3720   int msshell, result = -1, inbak, outbak, errbak, x, y;
3721   Lisp_Object cmd;
3722 
3723   /* Get current directory as MSDOS cwd is not per-process.  */
3724   getwd (oldwd);
3725 
3726   /* If argv[0] is the shell, it might come in any lettercase.
3727      Since `Fmember' is case-sensitive, we need to downcase
3728      argv[0], even if we are on case-preserving filesystems.  */
3729   lowcase_argv0 = alloca (strlen (argv[0]) + 1);
3730   for (pa = argv[0], pl = lowcase_argv0; *pa; pl++)
3731     {
3732       *pl = *pa++;
3733       if (*pl >= 'A' && *pl <= 'Z')
3734 	*pl += 'a' - 'A';
3735     }
3736   *pl = '\0';
3737 
3738   cmd = Ffile_name_nondirectory (build_string (lowcase_argv0));
3739   msshell = !NILP (Fmember (cmd, Fsymbol_value (intern ("msdos-shells"))))
3740     && !strcmp ("-c", argv[1]);
3741   if (msshell)
3742     {
3743       saveargv1 = argv[1];
3744       saveargv2 = argv[2];
3745       argv[1] = "/c";
3746       /* We only need to mirror slashes if a DOS shell will be invoked
3747 	 not via `system' (which does the mirroring itself).  Yes, that
3748 	 means DJGPP v1.x will lose here.  */
3749       if (argv[2] && argv[3])
3750 	{
3751 	  char *p = alloca (strlen (argv[2]) + 1);
3752 
3753 	  strcpy (argv[2] = p, saveargv2);
3754 	  while (*p && isspace (*p))
3755 	    p++;
3756 	  while (*p)
3757 	    {
3758 	      if (*p == '/')
3759 		*p++ = '\\';
3760 	      else
3761 		p++;
3762 	    }
3763 	}
3764     }
3765 
3766   chdir (working_dir);
3767   inbak = dup (0);
3768   outbak = dup (1);
3769   errbak = dup (2);
3770   if (inbak < 0 || outbak < 0 || errbak < 0)
3771     goto done; /* Allocation might fail due to lack of descriptors.  */
3772 
3773   if (have_mouse > 0)
3774     mouse_get_xy (&x, &y);
3775 
3776   if (!noninteractive)
3777     dos_ttcooked ();	/* do it here while 0 = stdin */
3778 
3779   dup2 (tempin, 0);
3780   dup2 (tempout, 1);
3781   dup2 (temperr, 2);
3782 
3783   if (msshell && !argv[3])
3784     {
3785       /* MS-DOS native shells are too restrictive.  For starters, they
3786 	 cannot grok commands longer than 126 characters.  In DJGPP v2
3787 	 and later, `system' is much smarter, so we'll call it instead.  */
3788 
3789       const char *cmnd;
3790 
3791       /* A shell gets a single argument--its full command
3792 	 line--whose original was saved in `saveargv2'.  */
3793 
3794       /* Don't let them pass empty command lines to `system', since
3795 	 with some shells it will try to invoke an interactive shell,
3796 	 which will hang Emacs.  */
3797       for (cmnd = saveargv2; *cmnd && isspace (*cmnd); cmnd++)
3798 	;
3799       if (*cmnd)
3800 	{
3801 	  extern char **SYS_ENVIRON;
3802 	  char **save_env = SYS_ENVIRON;
3803 	  int save_system_flags = __system_flags;
3804 
3805 	  /* Request the most powerful version of `system'.  We need
3806 	     all the help we can get to avoid calling stock DOS shells.  */
3807 	  __system_flags =  (__system_redirect
3808 			     | __system_use_shell
3809 			     | __system_allow_multiple_cmds
3810 			     | __system_allow_long_cmds
3811 			     | __system_handle_null_commands
3812 			     | __system_emulate_chdir);
3813 
3814 	  SYS_ENVIRON = envv;
3815 	  result = system (cmnd);
3816 	  __system_flags = save_system_flags;
3817 	  SYS_ENVIRON = save_env;
3818 	}
3819       else
3820 	result = 0;	/* emulate Unixy shell behavior with empty cmd line */
3821     }
3822   else
3823     result = spawnve (P_WAIT, argv[0], argv, envv);
3824 
3825   dup2 (inbak, 0);
3826   dup2 (outbak, 1);
3827   dup2 (errbak, 2);
3828   emacs_close (inbak);
3829   emacs_close (outbak);
3830   emacs_close (errbak);
3831 
3832   if (!noninteractive)
3833     dos_ttraw (CURTTY ());
3834   if (have_mouse > 0)
3835     {
3836       mouse_init ();
3837       mouse_moveto (x, y);
3838     }
3839 
3840   /* Some programs might change the meaning of the highest bit of the
3841      text attribute byte, so we get blinking characters instead of the
3842      bright background colors.  Restore that.  */
3843   if (!noninteractive)
3844     bright_bg ();
3845 
3846  done:
3847   chdir (oldwd);
3848   if (msshell)
3849     {
3850       argv[1] = saveargv1;
3851       argv[2] = saveargv2;
3852     }
3853   return result;
3854 }
3855 
3856 void
croak(char * badfunc)3857 croak (char *badfunc)
3858 {
3859   fprintf (stderr, "%s not yet implemented\r\n", badfunc);
3860   reset_all_sys_modes ();
3861   exit (1);
3862 }
3863 
3864 /*
3865  * A few unimplemented functions that we silently ignore.
3866  */
tcgetpgrp(int fd)3867 pid_t tcgetpgrp (int fd) { return 0; }
setpgid(int pid,int pgid)3868 int setpgid (int pid, int pgid) { return 0; }
setpriority(int x,int y,int z)3869 int setpriority (int x, int y, int z) { return 0; }
setsid(void)3870 pid_t setsid (void) { return 0; }
3871 
3872 
3873 /* Gnulib support and emulation.  */
3874 
3875 #if __DJGPP__ == 2 && __DJGPP_MINOR__ < 4
3876 ssize_t
readlink(const char * name,char * dummy1,size_t dummy2)3877 readlink (const char *name, char *dummy1, size_t dummy2)
3878 {
3879   /* `access' is much faster than `stat' on MS-DOS.  */
3880   if (access (name, F_OK) == 0)
3881     errno = EINVAL;
3882   return -1;
3883 }
3884 #endif
3885 
3886 /* dir_pathname is set by sys_opendir and used in readlinkat and in
3887    fstatat, when they get a special FD of zero, which means use the
3888    last directory opened by opendir.  */
3889 static char dir_pathname[MAXPATHLEN];
3890 DIR *
sys_opendir(const char * dirname)3891 sys_opendir (const char *dirname)
3892 {
3893   _fixpath (dirname, dir_pathname);
3894   return opendir (dirname);
3895 }
3896 
3897 ssize_t
readlinkat(int fd,char const * name,char * buffer,size_t buffer_size)3898 readlinkat (int fd, char const *name, char *buffer, size_t buffer_size)
3899 {
3900   /* Rely on a hack: an open directory is modeled as file descriptor 0,
3901      as in fstatat.  FIXME: Add proper support for readlinkat.  */
3902   char fullname[MAXPATHLEN];
3903 
3904   if (fd != AT_FDCWD)
3905     {
3906       if (strlen (dir_pathname) + strlen (name) + 1 >= MAXPATHLEN)
3907 	{
3908 	  errno = ENAMETOOLONG;
3909 	  return -1;
3910 	}
3911       sprintf (fullname, "%s/%s", dir_pathname, name);
3912       name = fullname;
3913     }
3914 
3915   return readlink (name, buffer, buffer_size);
3916 }
3917 
3918 char *
careadlinkat(int fd,char const * filename,char * buffer,size_t buffer_size,struct allocator const * alloc,ssize_t (* preadlinkat)(int,char const *,char *,size_t))3919 careadlinkat (int fd, char const *filename,
3920               char *buffer, size_t buffer_size,
3921               struct allocator const *alloc,
3922               ssize_t (*preadlinkat) (int, char const *, char *, size_t))
3923 {
3924   if (!buffer)
3925     {
3926       /* We don't support the fancy auto-allocation feature.  */
3927       if (!buffer_size)
3928 	errno = ENOSYS;
3929       else
3930 	errno = EINVAL;
3931       buffer = NULL;
3932     }
3933   else
3934     {
3935       ssize_t len = preadlinkat (fd, filename, buffer, buffer_size);
3936 
3937       if (len < 0 || len == buffer_size)
3938 	buffer = NULL;
3939       else
3940 	buffer[len + 1] = '\0';
3941     }
3942   return buffer;
3943 }
3944 
3945 /* Emulate faccessat(2).  */
3946 int
faccessat(int dirfd,const char * path,int mode,int flags)3947 faccessat (int dirfd, const char * path, int mode, int flags)
3948 {
3949   char fullname[MAXPATHLEN];
3950 
3951   /* We silently ignore FLAGS.  */
3952   flags = flags;
3953 
3954   if (dirfd != AT_FDCWD
3955       && !(IS_DIRECTORY_SEP (path[0])
3956 	   || IS_DEVICE_SEP (path[1])))
3957     {
3958       char lastc = dir_pathname[strlen (dir_pathname) - 1];
3959 
3960       if (strlen (dir_pathname) + strlen (path) + IS_DIRECTORY_SEP (lastc)
3961 	  >= MAXPATHLEN)
3962 	{
3963 	  errno = ENAMETOOLONG;
3964 	  return -1;
3965 	}
3966 
3967       sprintf (fullname, "%s%s%s",
3968 	       dir_pathname, IS_DIRECTORY_SEP (lastc) ? "" : "/", path);
3969       path = fullname;
3970     }
3971 
3972   if ((mode & F_OK) != 0 && IS_DIRECTORY_SEP (path[strlen (path) - 1]))
3973     mode |= D_OK;
3974 
3975   return access (path, mode);
3976 }
3977 
3978 /* Emulate fstatat.  */
3979 int
fstatat(int fd,char const * name,struct stat * st,int flags)3980 fstatat (int fd, char const *name, struct stat *st, int flags)
3981 {
3982   /* Rely on a hack: an open directory is modeled as file descriptor 0.
3983      This is good enough for the current usage in Emacs, but is fragile.
3984 
3985      FIXME: Add proper support for fdopendir, fstatat, readlinkat.
3986      Gnulib does this and can serve as a model.  */
3987   char fullname[MAXPATHLEN];
3988 
3989   flags = flags;
3990 
3991   if (fd != AT_FDCWD)
3992     {
3993       char lastc = dir_pathname[strlen (dir_pathname) - 1];
3994 
3995       if (strlen (dir_pathname) + strlen (name) + IS_DIRECTORY_SEP (lastc)
3996 	  >= MAXPATHLEN)
3997 	{
3998 	  errno = ENAMETOOLONG;
3999 	  return -1;
4000 	}
4001 
4002       sprintf (fullname, "%s%s%s",
4003 	       dir_pathname, IS_DIRECTORY_SEP (lastc) ? "" : "/", name);
4004       name = fullname;
4005     }
4006 
4007 #if __DJGPP__ > 2 || __DJGPP_MINOR__ > 3
4008   return (flags & AT_SYMLINK_NOFOLLOW) ? lstat (name, st) : stat (name, st);
4009 #else
4010   return stat (name, st);
4011 #endif
4012 }
4013 
4014 #if __DJGPP__ == 2 && __DJGPP_MINOR__ < 4
4015 /* Emulate the Posix unsetenv.  DJGPP v2.04 has this in the library.  */
4016 int
unsetenv(const char * name)4017 unsetenv (const char *name)
4018 {
4019   char *var;
4020   size_t name_len;
4021   int retval;
4022 
4023   if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
4024     {
4025       errno = EINVAL;
4026       return -1;
4027     }
4028 
4029   /* DJGPP's 'putenv' deletes the entry if it doesn't include '='.  */
4030   putenv (name);
4031 
4032   return 0;
4033 }
4034 #endif
4035 
4036 
4037 #ifndef HAVE_SELECT
4038 #include "sysselect.h"
4039 
4040 /* This yields the rest of the current time slice to the task manager.
4041    It should be called by any code which knows that it has nothing
4042    useful to do except idle.
4043 
4044    I don't use __dpmi_yield here, since versions of library before 2.02
4045    called Int 2Fh/AX=1680h there in a way that would wedge the DOS box
4046    on some versions of Windows 9X.  */
4047 
4048 void
dos_yield_time_slice(void)4049 dos_yield_time_slice (void)
4050 {
4051   _go32_dpmi_registers r;
4052 
4053   r.x.ax = 0x1680;
4054   r.x.ss = r.x.sp = r.x.flags = 0;
4055   _go32_dpmi_simulate_int (0x2f, &r);
4056   if (r.h.al == 0x80)
4057     errno = ENOSYS;
4058 }
4059 
4060 /* Only event queue is checked.  */
4061 /* We don't have to call timer_check here
4062    because wait_reading_process_output takes care of that.  */
4063 int
sys_select(int nfds,fd_set * rfds,fd_set * wfds,fd_set * efds,struct timespec * timeout,void * ignored)4064 sys_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds,
4065 	    struct timespec *timeout, void *ignored)
4066 {
4067   int check_input;
4068   struct timespec t;
4069 
4070   check_input = 0;
4071   if (rfds)
4072     {
4073       check_input = FD_ISSET (0, rfds);
4074       FD_ZERO (rfds);
4075     }
4076   if (wfds)
4077     FD_ZERO (wfds);
4078   if (efds)
4079     FD_ZERO (efds);
4080 
4081   if (nfds != 1)
4082     emacs_abort ();
4083 
4084   /* If we are looking only for the terminal, with no timeout,
4085      just read it and wait -- that's more efficient.  */
4086   if (!timeout)
4087     {
4088       while (!detect_input_pending ())
4089 	{
4090 	  dos_yield_time_slice ();
4091 	}
4092     }
4093   else
4094     {
4095       struct timespec clnow, cllast, cldiff;
4096 
4097       gettime (&t);
4098       cllast = make_timespec (t.tv_sec, t.tv_nsec);
4099 
4100       while (!check_input || !detect_input_pending ())
4101 	{
4102 	  gettime (&t);
4103 	  clnow = make_timespec (t.tv_sec, t.tv_nsec);
4104 	  cldiff = timespec_sub (clnow, cllast);
4105 	  /* Stop when timeout value is about to cross zero.  */
4106 	  if (timespec_cmp (*timeout, cldiff) <= 0)
4107 	    {
4108 	      timeout->tv_sec = 0;
4109 	      timeout->tv_nsec = 0;
4110 	      return 0;
4111 	    }
4112 	  *timeout = timespec_sub (*timeout, cldiff);
4113 	  cllast = clnow;
4114 	  dos_yield_time_slice ();
4115 	}
4116     }
4117 
4118   FD_SET (0, rfds);
4119   return 1;
4120 }
4121 #endif
4122 
4123 /*
4124  * Define overlaid functions:
4125  *
4126  *	chdir -> sys_chdir
4127  *	tzset -> init_gettimeofday
4128  *	abort -> dos_abort
4129  */
4130 
4131 #ifdef chdir
4132 #undef chdir
4133 extern int chdir (const char *);
4134 
4135 int
sys_chdir(const char * path)4136 sys_chdir (const char *path)
4137 {
4138   int len = strlen (path);
4139   char *tmp = (char *)path;
4140 
4141   if (*tmp && tmp[1] == ':')
4142     {
4143       if (getdisk () != tolower (tmp[0]) - 'a')
4144 	setdisk (tolower (tmp[0]) - 'a');
4145       tmp += 2;	/* strip drive: KFS 1995-07-06 */
4146       len -= 2;
4147     }
4148 
4149   if (len > 1 && (tmp[len - 1] == '/'))
4150     {
4151       char *tmp1 = (char *) alloca (len + 1);
4152       strcpy (tmp1, tmp);
4153       tmp1[len - 1] = 0;
4154       tmp = tmp1;
4155     }
4156   return chdir (tmp);
4157 }
4158 #endif
4159 
4160 #ifdef tzset
4161 #undef tzset
4162 extern void tzset (void);
4163 
4164 void
init_gettimeofday(void)4165 init_gettimeofday (void)
4166 {
4167   time_t ltm, gtm;
4168   struct tm *lstm;
4169 
4170   tzset ();
4171   ltm = gtm = time (NULL);
4172   ltm = mktime (lstm = localtime (&ltm));
4173   gtm = mktime (gmtime (&gtm));
4174   time_rec.tm_hour = 99;	/* force gettimeofday to get date */
4175   time_rec.tm_isdst = lstm->tm_isdst;
4176   dos_timezone_offset = time_rec.tm_gmtoff = (int)(gtm - ltm) / 60;
4177 }
4178 #endif
4179 
4180 static void
msdos_abort(void)4181 msdos_abort (void)
4182 {
4183   dos_ttcooked ();
4184   ScreenSetCursor (10, 0);
4185   cputs ("\r\n\nEmacs aborted!\r\n");
4186   raise (SIGABRT);
4187   exit (2);
4188 }
4189 
4190 void
msdos_fatal_signal(int sig)4191 msdos_fatal_signal (int sig)
4192 {
4193   if (sig == SIGABRT)
4194     msdos_abort ();
4195   else
4196     raise (sig);
4197 }
4198 
4199 void
syms_of_msdos(void)4200 syms_of_msdos (void)
4201 {
4202   recent_doskeys = Fmake_vector (make_fixnum (NUM_RECENT_DOSKEYS), Qnil);
4203   staticpro (&recent_doskeys);
4204 
4205 #ifndef HAVE_X_WINDOWS
4206 
4207   /* The following two are from xfns.c:  */
4208   DEFSYM (Qreverse, "reverse");
4209 
4210   DEFVAR_LISP ("dos-unsupported-char-glyph", Vdos_unsupported_char_glyph,
4211 	       doc: /* Glyph to display instead of chars not supported by current codepage.
4212 This variable is used only by MS-DOS terminals.  */);
4213   Vdos_unsupported_char_glyph = make_fixnum ('\177');
4214 
4215 #endif
4216 
4217   defsubr (&Srecent_doskeys);
4218   defsubr (&Smsdos_long_file_names);
4219   defsubr (&Smsdos_downcase_filename);
4220   defsubr (&Smsdos_remember_default_colors);
4221   defsubr (&Smsdos_set_mouse_buttons);
4222 }
4223 
4224 #endif /* MSDOS */
4225