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