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