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