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