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