.
[emacs.git] / src / msdos.c
blob1cea4af486c2b254c87f65ebdcceb3a2ccbd2eec
1 /* MS-DOS specific C utilities. -*- coding: raw-text -*-
2 Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 /* Contributed by Morten Welinder */
22 /* New display, keyboard, and mouse control by Kim F. Storm */
24 /* Note: some of the stuff here was taken from end of sysdep.c in demacs. */
26 #include <config.h>
28 #ifdef MSDOS
29 #include "lisp.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <time.h>
33 #include <sys/param.h>
34 #include <sys/time.h>
35 #include <dos.h>
36 #include <errno.h>
37 #include <string.h> /* for bzero and string functions */
38 #include <sys/stat.h> /* for _fixpath */
39 #include <unistd.h> /* for chdir, dup, dup2, etc. */
40 #if __DJGPP__ >= 2
41 #include <fcntl.h>
42 #include <io.h> /* for setmode */
43 #include <dpmi.h> /* for __dpmi_xxx stuff */
44 #include <sys/farptr.h> /* for _farsetsel, _farnspokeb */
45 #include <libc/dosio.h> /* for _USE_LFN */
46 #include <conio.h> /* for cputs */
47 #endif
49 #include "msdos.h"
50 #include "systime.h"
51 #include "termhooks.h"
52 #include "termchar.h"
53 #include "dispextern.h"
54 #include "dosfns.h"
55 #include "termopts.h"
56 #include "charset.h"
57 #include "coding.h"
58 #include "disptab.h"
59 #include "frame.h"
60 #include "window.h"
61 #include "buffer.h"
62 #include "commands.h"
63 #include <go32.h>
64 #include <pc.h>
65 #include <ctype.h>
66 /* #include <process.h> */
67 /* Damn that local process.h! Instead we can define P_WAIT ourselves. */
68 #define P_WAIT 1
70 #ifndef _USE_LFN
71 #define _USE_LFN 0
72 #endif
74 #ifndef _dos_ds
75 #define _dos_ds _go32_info_block.selector_for_linear_memory
76 #endif
78 #if __DJGPP__ > 1
80 #include <signal.h>
81 #include "syssignal.h"
83 #ifndef SYSTEM_MALLOC
85 #ifdef GNU_MALLOC
87 /* If other `malloc' than ours is used, force our `sbrk' behave like
88 Unix programs expect (resize memory blocks to keep them contiguous).
89 If `sbrk' from `ralloc.c' is NOT used, also zero-out sbrk'ed memory,
90 because that's what `gmalloc' expects to get. */
91 #include <crt0.h>
93 #ifdef REL_ALLOC
94 int _crt0_startup_flags = _CRT0_FLAG_UNIX_SBRK;
95 #else /* not REL_ALLOC */
96 int _crt0_startup_flags = (_CRT0_FLAG_UNIX_SBRK | _CRT0_FLAG_FILL_SBRK_MEMORY);
97 #endif /* not REL_ALLOC */
98 #endif /* GNU_MALLOC */
100 #endif /* not SYSTEM_MALLOC */
101 #endif /* __DJGPP__ > 1 */
103 static unsigned long
104 event_timestamp ()
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 int have_mouse; /* 0: no, 1: enabled, -1: disabled */
127 static int mouse_visible;
129 static int mouse_last_x;
130 static int mouse_last_y;
132 static int mouse_button_translate[NUM_MOUSE_BUTTONS];
133 static int mouse_button_count;
135 void
136 mouse_on ()
138 union REGS regs;
140 if (have_mouse > 0 && !mouse_visible)
142 if (termscript)
143 fprintf (termscript, "<M_ON>");
144 regs.x.ax = 0x0001;
145 int86 (0x33, &regs, &regs);
146 mouse_visible = 1;
150 void
151 mouse_off ()
153 union REGS regs;
155 if (have_mouse > 0 && mouse_visible)
157 if (termscript)
158 fprintf (termscript, "<M_OFF>");
159 regs.x.ax = 0x0002;
160 int86 (0x33, &regs, &regs);
161 mouse_visible = 0;
165 static void
166 mouse_get_xy (int *x, int *y)
168 union REGS regs;
170 regs.x.ax = 0x0003;
171 int86 (0x33, &regs, &regs);
172 *x = regs.x.cx / 8;
173 *y = regs.x.dx / 8;
176 void
177 mouse_moveto (x, y)
178 int x, y;
180 union REGS regs;
182 if (termscript)
183 fprintf (termscript, "<M_XY=%dx%d>", x, y);
184 regs.x.ax = 0x0004;
185 mouse_last_x = regs.x.cx = x * 8;
186 mouse_last_y = regs.x.dx = y * 8;
187 int86 (0x33, &regs, &regs);
190 static int
191 mouse_pressed (b, xp, yp)
192 int b, *xp, *yp;
194 union REGS regs;
196 if (b >= mouse_button_count)
197 return 0;
198 regs.x.ax = 0x0005;
199 regs.x.bx = mouse_button_translate[b];
200 int86 (0x33, &regs, &regs);
201 if (regs.x.bx)
202 *xp = regs.x.cx / 8, *yp = regs.x.dx / 8;
203 return (regs.x.bx != 0);
206 static int
207 mouse_released (b, xp, yp)
208 int b, *xp, *yp;
210 union REGS regs;
212 if (b >= mouse_button_count)
213 return 0;
214 regs.x.ax = 0x0006;
215 regs.x.bx = mouse_button_translate[b];
216 int86 (0x33, &regs, &regs);
217 if (regs.x.bx)
218 *xp = regs.x.cx / 8, *yp = regs.x.dx / 8;
219 return (regs.x.bx != 0);
222 static int
223 mouse_button_depressed (b, xp, yp)
224 int b, *xp, *yp;
226 union REGS regs;
228 if (b >= mouse_button_count)
229 return 0;
230 regs.x.ax = 0x0003;
231 int86 (0x33, &regs, &regs);
232 if ((regs.x.bx & (1 << mouse_button_translate[b])) != 0)
234 *xp = regs.x.cx / 8;
235 *yp = regs.x.dx / 8;
236 return 1;
238 return 0;
241 void
242 mouse_get_pos (f, insist, bar_window, part, x, y, time)
243 FRAME_PTR *f;
244 int insist;
245 Lisp_Object *bar_window, *x, *y;
246 enum scroll_bar_part *part;
247 unsigned long *time;
249 int ix, iy;
250 Lisp_Object frame, tail;
252 /* Clear the mouse-moved flag for every frame on this display. */
253 FOR_EACH_FRAME (tail, frame)
254 XFRAME (frame)->mouse_moved = 0;
256 *f = selected_frame;
257 *bar_window = Qnil;
258 mouse_get_xy (&ix, &iy);
259 *time = event_timestamp ();
260 *x = make_number (mouse_last_x = ix);
261 *y = make_number (mouse_last_y = iy);
264 static void
265 mouse_check_moved ()
267 int x, y;
269 mouse_get_xy (&x, &y);
270 selected_frame->mouse_moved |= (x != mouse_last_x || y != mouse_last_y);
271 mouse_last_x = x;
272 mouse_last_y = y;
275 void
276 mouse_init ()
278 union REGS regs;
279 int b;
281 if (termscript)
282 fprintf (termscript, "<M_INIT>");
284 regs.x.ax = 0x0021;
285 int86 (0x33, &regs, &regs);
287 /* Reset the mouse last press/release info. It seems that Windows
288 doesn't do that automatically when function 21h is called, which
289 causes Emacs to ``remember'' the click that switched focus to the
290 window just before Emacs was started from that window. */
291 for (b = 0; b < mouse_button_count; b++)
293 int dummy_x, dummy_y;
295 (void) mouse_pressed (b, &dummy_x, &dummy_y);
296 (void) mouse_released (b, &dummy_x, &dummy_y);
299 regs.x.ax = 0x0007;
300 regs.x.cx = 0;
301 regs.x.dx = 8 * (ScreenCols () - 1);
302 int86 (0x33, &regs, &regs);
304 regs.x.ax = 0x0008;
305 regs.x.cx = 0;
306 regs.x.dx = 8 * (ScreenRows () - 1);
307 int86 (0x33, &regs, &regs);
309 mouse_moveto (0, 0);
310 mouse_visible = 0;
313 /* ------------------------- Screen control ----------------------
317 static int internal_terminal = 0;
319 #ifndef HAVE_X_WINDOWS
320 extern unsigned char ScreenAttrib;
321 static int screen_face;
322 static int highlight;
324 static int screen_size_X;
325 static int screen_size_Y;
326 static int screen_size;
328 static int current_pos_X;
329 static int current_pos_Y;
330 static int new_pos_X;
331 static int new_pos_Y;
333 static void *startup_screen_buffer;
334 static int startup_screen_size_X;
335 static int startup_screen_size_Y;
336 static int startup_pos_X;
337 static int startup_pos_Y;
338 static unsigned char startup_screen_attrib;
340 static clock_t startup_time;
342 static int term_setup_done;
344 /* Similar to the_only_frame. */
345 struct x_output the_only_x_display;
347 /* This is never dereferenced. */
348 Display *x_current_display;
350 /* Support for DOS/V (allows Japanese characters to be displayed on
351 standard, non-Japanese, ATs). Only supported for DJGPP v2 and later. */
353 /* Holds the address of the text-mode screen buffer. */
354 static unsigned long screen_old_address = 0;
355 /* Segment and offset of the virtual screen. If 0, DOS/V is NOT loaded. */
356 static unsigned short screen_virtual_segment = 0;
357 static unsigned short screen_virtual_offset = 0;
358 /* A flag to control how to display unibyte 8-bit characters. */
359 extern int unibyte_display_via_language_environment;
361 #if __DJGPP__ > 1
362 /* Update the screen from a part of relocated DOS/V screen buffer which
363 begins at OFFSET and includes COUNT characters. */
364 static void
365 dosv_refresh_virtual_screen (int offset, int count)
367 __dpmi_regs regs;
369 if (offset < 0 || count < 0) /* paranoia; illegal values crash DOS/V */
370 return;
372 regs.h.ah = 0xff; /* update relocated screen */
373 regs.x.es = screen_virtual_segment;
374 regs.x.di = screen_virtual_offset + offset;
375 regs.x.cx = count;
376 __dpmi_int (0x10, &regs);
378 #endif
380 static void
381 dos_direct_output (y, x, buf, len)
382 int y;
383 int x;
384 char *buf;
385 int len;
387 int t0 = 2 * (x + y * screen_size_X);
388 int t = t0 + (int) ScreenPrimary;
389 int l0 = len;
391 #if (__DJGPP__ < 2)
392 while (--len >= 0) {
393 dosmemput (buf++, 1, t);
394 t += 2;
396 #else
397 /* This is faster. */
398 for (_farsetsel (_dos_ds); --len >= 0; t += 2, buf++)
399 _farnspokeb (t, *buf);
401 if (screen_virtual_segment)
402 dosv_refresh_virtual_screen (t0, l0);
403 #endif
405 #endif
407 /* Flash the screen as a substitute for BEEPs. */
409 #if (__DJGPP__ < 2)
410 static void
411 do_visible_bell (xorattr)
412 unsigned char xorattr;
414 asm volatile
415 (" movb $1,%%dl
416 visible_bell_0:
417 movl _ScreenPrimary,%%eax
418 call dosmemsetup
419 movl %%eax,%%ebx
420 movl %1,%%ecx
421 movb %0,%%al
422 incl %%ebx
423 visible_bell_1:
424 xorb %%al,%%gs:(%%ebx)
425 addl $2,%%ebx
426 decl %%ecx
427 jne visible_bell_1
428 decb %%dl
429 jne visible_bell_3
430 visible_bell_2:
431 movzwl %%ax,%%eax
432 movzwl %%ax,%%eax
433 movzwl %%ax,%%eax
434 movzwl %%ax,%%eax
435 decw %%cx
436 jne visible_bell_2
437 jmp visible_bell_0
438 visible_bell_3:"
439 : /* no output */
440 : "m" (xorattr), "g" (screen_size)
441 : "%eax", "%ebx", /* "%gs",*/ "%ecx", "%edx");
444 static void
445 ScreenVisualBell (void)
447 /* This creates an xor-mask that will swap the default fore- and
448 background colors. */
449 do_visible_bell (((the_only_x_display.foreground_pixel
450 ^ the_only_x_display.background_pixel)
451 * 0x11) & 0x7f);
453 #endif
455 #ifndef HAVE_X_WINDOWS
457 static int blink_bit = -1; /* the state of the blink bit at startup */
459 /* Enable bright background colors. */
460 static void
461 bright_bg (void)
463 union REGS regs;
465 /* Remember the original state of the blink/bright-background bit.
466 It is stored at 0040:0065h in the BIOS data area. */
467 if (blink_bit == -1)
468 blink_bit = (_farpeekb (_dos_ds, 0x465) & 0x20) == 0x20;
470 regs.h.bl = 0;
471 regs.x.ax = 0x1003;
472 int86 (0x10, &regs, &regs);
475 /* Disable bright background colors (and enable blinking) if we found
476 the video system in that state at startup. */
477 static void
478 maybe_enable_blinking (void)
480 if (blink_bit == 1)
482 union REGS regs;
484 regs.h.bl = 1;
485 regs.x.ax = 0x1003;
486 int86 (0x10, &regs, &regs);
490 /* Set the screen dimensions so that it can show no less than
491 ROWS x COLS frame. */
493 void
494 dos_set_window_size (rows, cols)
495 int *rows, *cols;
497 char video_name[30];
498 Lisp_Object video_mode;
499 int video_mode_value;
500 int have_vga = 0;
501 union REGS regs;
502 int current_rows = ScreenRows (), current_cols = ScreenCols ();
504 if (*rows == current_rows && *cols == current_cols)
505 return;
507 /* Do we have a VGA? */
508 regs.x.ax = 0x1a00;
509 int86 (0x10, &regs, &regs);
510 if (regs.h.al == 0x1a && regs.h.bl > 5 && regs.h.bl < 13)
511 have_vga = 1;
513 mouse_off ();
515 /* If the user specified a special video mode for these dimensions,
516 use that mode. */
517 sprintf (video_name, "screen-dimensions-%dx%d", *rows, *cols);
518 video_mode = XSYMBOL (Fintern_soft (build_string (video_name),
519 Qnil))-> value;
521 if (INTEGERP (video_mode)
522 && (video_mode_value = XINT (video_mode)) > 0)
524 regs.x.ax = video_mode_value;
525 int86 (0x10, &regs, &regs);
527 if (have_mouse)
529 /* Must hardware-reset the mouse, or else it won't update
530 its notion of screen dimensions for some non-standard
531 video modes. This is *painfully* slow... */
532 regs.x.ax = 0;
533 int86 (0x33, &regs, &regs);
537 /* Find one of the dimensions supported by standard EGA/VGA
538 which gives us at least the required dimensions. */
540 #if __DJGPP__ > 1
542 else
544 static struct {
545 int rows;
546 int need_vga;
547 } std_dimension[] = {
548 {25, 0},
549 {28, 1},
550 {35, 0},
551 {40, 1},
552 {43, 0},
553 {50, 1}
555 int i = 0;
557 while (i < sizeof (std_dimension) / sizeof (std_dimension[0]))
559 if (std_dimension[i].need_vga <= have_vga
560 && std_dimension[i].rows >= *rows)
562 if (std_dimension[i].rows != current_rows
563 || *cols != current_cols)
564 _set_screen_lines (std_dimension[i].rows);
565 break;
567 i++;
571 #else /* not __DJGPP__ > 1 */
573 else if (*rows <= 25)
575 if (current_rows != 25 || current_cols != 80)
577 regs.x.ax = 3;
578 int86 (0x10, &regs, &regs);
579 regs.x.ax = 0x1101;
580 regs.h.bl = 0;
581 int86 (0x10, &regs, &regs);
582 regs.x.ax = 0x1200;
583 regs.h.bl = 32;
584 int86 (0x10, &regs, &regs);
585 regs.x.ax = 3;
586 int86 (0x10, &regs, &regs);
589 else if (*rows <= 50)
590 if (have_vga && (current_rows != 50 || current_cols != 80)
591 || *rows <= 43 && (current_rows != 43 || current_cols != 80))
593 regs.x.ax = 3;
594 int86 (0x10, &regs, &regs);
595 regs.x.ax = 0x1112;
596 regs.h.bl = 0;
597 int86 (0x10, &regs, &regs);
598 regs.x.ax = 0x1200;
599 regs.h.bl = 32;
600 int86 (0x10, &regs, &regs);
601 regs.x.ax = 0x0100;
602 regs.x.cx = 7;
603 int86 (0x10, &regs, &regs);
605 #endif /* not __DJGPP__ > 1 */
607 if (have_mouse)
609 mouse_init ();
610 mouse_on ();
613 /* Tell the caller what dimensions have been REALLY set. */
614 *rows = ScreenRows ();
615 *cols = ScreenCols ();
617 /* Enable bright background colors. */
618 bright_bg ();
620 /* FIXME: I'm not sure the above will run at all on DOS/V. But let's
621 be defensive anyway. */
622 if (screen_virtual_segment)
623 dosv_refresh_virtual_screen (0, *cols * *rows);
626 /* If we write a character in the position where the mouse is,
627 the mouse cursor may need to be refreshed. */
629 static void
630 mouse_off_maybe ()
632 int x, y;
634 if (!mouse_visible)
635 return;
637 mouse_get_xy (&x, &y);
638 if (y != new_pos_Y || x < new_pos_X)
639 return;
641 mouse_off ();
644 static void
645 IT_ring_bell (void)
647 if (visible_bell)
649 mouse_off ();
650 ScreenVisualBell ();
652 else
654 union REGS inregs, outregs;
655 inregs.h.ah = 2;
656 inregs.h.dl = 7;
657 intdos (&inregs, &outregs);
661 /* Given a face id FACE, extract the face parameters to be used for
662 display until the face changes. The face parameters (actually, its
663 color) are used to construct the video attribute byte for each
664 glyph during the construction of the buffer that is then blitted to
665 the video RAM. */
666 static void
667 IT_set_face (int face)
669 struct face *fp = FACE_FROM_ID (selected_frame, face);
670 unsigned long fg, bg;
672 if (!fp)
674 fp = FACE_FROM_ID (selected_frame, DEFAULT_FACE_ID);
675 /* The default face for the frame should always be realized and
676 cached. */
677 if (!fp)
678 abort ();
680 screen_face = face;
681 fg = fp->foreground;
682 bg = fp->background;
684 /* Don't use invalid colors. In particular, a color of -1 means use
685 the colors of the default face, except that if highlight is on,
686 invert the foreground and the background. Note that we assume
687 all 16 colors to be available for the background, since Emacs
688 switches on this mode (and loses the blinking attribute) at
689 startup. */
690 if (fg == (unsigned long)-1)
691 fg = highlight ? FRAME_BACKGROUND_PIXEL (selected_frame)
692 : FRAME_FOREGROUND_PIXEL (selected_frame);
693 if (bg == (unsigned long)-1)
694 bg = highlight ? FRAME_FOREGROUND_PIXEL (selected_frame)
695 : FRAME_BACKGROUND_PIXEL (selected_frame);
696 if (termscript)
697 fprintf (termscript, "<FACE %d%s: %d/%d>",
698 face, highlight ? "H" : "", fp->foreground, fp->background);
699 if (fg >= 0 && fg < 16)
701 ScreenAttrib &= 0xf0;
702 ScreenAttrib |= fg;
704 if (bg >= 0 && bg < 16)
706 ScreenAttrib &= 0x0f;
707 ScreenAttrib |= ((bg & 0x0f) << 4);
711 Lisp_Object Vdos_unsupported_char_glyph;
713 static void
714 IT_write_glyphs (struct glyph *str, int str_len)
716 unsigned char *screen_buf, *screen_bp, *screen_buf_end, *bp;
717 int unsupported_face = FAST_GLYPH_FACE (Vdos_unsupported_char_glyph);
718 unsigned unsupported_char= FAST_GLYPH_CHAR (Vdos_unsupported_char_glyph);
719 int offset = 2 * (new_pos_X + screen_size_X * new_pos_Y);
720 register int sl = str_len;
721 register int tlen = GLYPH_TABLE_LENGTH;
722 register Lisp_Object *tbase = GLYPH_TABLE_BASE;
724 struct coding_system *coding = (CODING_REQUIRE_ENCODING (&terminal_coding)
725 ? &terminal_coding
726 : &safe_terminal_coding);
728 /* Do we need to consider conversion of unibyte characters to
729 multibyte? */
730 int convert_unibyte_characters
731 = (NILP (current_buffer->enable_multibyte_characters)
732 && unibyte_display_via_language_environment);
734 if (str_len == 0) return;
736 screen_buf = screen_bp = alloca (str_len * 2);
737 screen_buf_end = screen_buf + str_len * 2;
739 /* Since faces get cached and uncached behind our back, we can't
740 rely on their indices in the cache being consistent across
741 invocations. So always reset the screen face to the default
742 face of the frame, before writing glyphs, and let the glyphs
743 set the right face if it's different from the default. */
744 IT_set_face (DEFAULT_FACE_ID);
746 /* The mode bit CODING_MODE_LAST_BLOCK should be set to 1 only at
747 the tail. */
748 terminal_coding.mode &= ~CODING_MODE_LAST_BLOCK;
749 while (sl)
751 int cf, chlen, enclen;
752 unsigned char workbuf[4], *buf;
753 unsigned ch;
754 register GLYPH g = GLYPH_FROM_CHAR_GLYPH (*str);
756 /* Find the actual glyph to display by traversing the entire
757 aliases chain for this glyph. */
758 GLYPH_FOLLOW_ALIASES (tbase, tlen, g);
760 /* Glyphs with GLYPH_MASK_PADDING bit set are actually there
761 only for the redisplay code to know how many columns does
762 this character occupy on the screen. Skip padding glyphs. */
763 if (CHAR_GLYPH_PADDING_P (*str))
765 str++;
766 sl--;
768 else
770 /* Convert the character code to multibyte, if they
771 requested display via language environment. */
772 ch = FAST_GLYPH_CHAR (g);
773 /* We only want to convert unibyte characters to multibyte
774 in unibyte buffers! Otherwise, the 8-bit code might come
775 from the display table set up to display foreign characters. */
776 if (SINGLE_BYTE_CHAR_P (ch) && convert_unibyte_characters
777 && (ch >= 0240
778 || (ch >= 0200 && !NILP (Vnonascii_translation_table))))
779 ch = unibyte_char_to_multibyte (ch);
781 /* Invalid characters are displayed with a special glyph. */
782 if (! GLYPH_CHAR_VALID_P (ch))
784 g = !NILP (Vdos_unsupported_char_glyph)
785 ? Vdos_unsupported_char_glyph
786 : MAKE_GLYPH (selected_frame, '\177',
787 GLYPH_FACE (selected_frame, g));
788 ch = FAST_GLYPH_CHAR (g);
790 if (COMPOSITE_CHAR_P (ch))
792 /* If CH is a composite character, we can display
793 only the first component. */
794 g = cmpchar_table[COMPOSITE_CHAR_ID (ch)]->glyph[0],
795 ch = GLYPH_CHAR (selected_frame, g);
796 cf = FAST_GLYPH_FACE (g);
799 /* If the face of this glyph is different from the current
800 screen face, update the screen attribute byte. */
801 cf = FAST_GLYPH_FACE (g);
802 if (cf != screen_face)
803 IT_set_face (cf); /* handles invalid faces gracefully */
805 if (GLYPH_SIMPLE_P (tbase, tlen, g))
806 /* We generate the multi-byte form of CH in BUF. */
807 chlen = CHAR_STRING (ch, workbuf, buf);
808 else
810 /* We have a string in Vglyph_table. */
811 chlen = GLYPH_LENGTH (tbase, g);
812 buf = GLYPH_STRING (tbase, g);
815 /* If the character is not multibyte, don't bother converting it. */
816 if (chlen == 1)
818 *conversion_buffer = (unsigned char)ch;
819 chlen = 0;
820 enclen = 1;
822 else
824 encode_coding (coding, buf, conversion_buffer, chlen,
825 conversion_buffer_size);
826 chlen -= coding->consumed;
827 enclen = coding->produced;
829 /* Replace glyph codes that cannot be converted by
830 terminal_coding with Vdos_unsupported_char_glyph. */
831 if (*conversion_buffer == '?')
833 char *cbp = conversion_buffer;
835 while (cbp < conversion_buffer + enclen && *cbp == '?')
836 *cbp++ = unsupported_char;
837 if (unsupported_face != screen_face)
838 IT_set_face (unsupported_face);
842 if (enclen + chlen > screen_buf_end - screen_bp)
844 /* The allocated buffer for screen writes is too small.
845 Flush it and loop again without incrementing STR, so
846 that the next loop will begin with the same glyph. */
847 int nbytes = screen_bp - screen_buf;
849 mouse_off_maybe ();
850 dosmemput (screen_buf, nbytes, (int)ScreenPrimary + offset);
851 if (screen_virtual_segment)
852 dosv_refresh_virtual_screen (offset, nbytes / 2);
853 new_pos_X += nbytes / 2;
854 offset += nbytes;
856 /* Prepare to reuse the same buffer again. */
857 screen_bp = screen_buf;
859 else
861 /* There's enough place in the allocated buffer to add
862 the encoding of this glyph. */
864 /* First, copy the encoded bytes. */
865 for (bp = conversion_buffer; enclen--; bp++)
867 *screen_bp++ = (unsigned char)*bp;
868 *screen_bp++ = ScreenAttrib;
869 if (termscript)
870 fputc (*bp, termscript);
873 /* Now copy the bytes not consumed by the encoding. */
874 if (chlen > 0)
876 buf += coding->consumed;
877 while (chlen--)
879 if (termscript)
880 fputc (*buf, termscript);
881 *screen_bp++ = (unsigned char)*buf++;
882 *screen_bp++ = ScreenAttrib;
886 /* Update STR and its remaining length. */
887 str++;
888 sl--;
893 /* Dump whatever is left in the screen buffer. */
894 mouse_off_maybe ();
895 dosmemput (screen_buf, screen_bp - screen_buf, (int)ScreenPrimary + offset);
896 if (screen_virtual_segment)
897 dosv_refresh_virtual_screen (offset, (screen_bp - screen_buf) / 2);
898 new_pos_X += (screen_bp - screen_buf) / 2;
900 /* We may have to output some codes to terminate the writing. */
901 if (CODING_REQUIRE_FLUSHING (coding))
903 coding->mode |= CODING_MODE_LAST_BLOCK;
904 encode_coding (coding, "", conversion_buffer, 0, conversion_buffer_size);
905 if (coding->produced > 0)
907 for (screen_bp = screen_buf, bp = conversion_buffer;
908 coding->produced--; bp++)
910 *screen_bp++ = (unsigned char)*bp;
911 *screen_bp++ = ScreenAttrib;
912 if (termscript)
913 fputc (*bp, termscript);
915 offset += screen_bp - screen_buf;
916 mouse_off_maybe ();
917 dosmemput (screen_buf, screen_bp - screen_buf,
918 (int)ScreenPrimary + offset);
919 if (screen_virtual_segment)
920 dosv_refresh_virtual_screen (offset, (screen_bp - screen_buf) / 2);
921 new_pos_X += (screen_bp - screen_buf) / 2;
926 static void
927 IT_clear_end_of_line (int first_unused)
929 char *spaces, *sp;
930 int i, j;
931 int offset = 2 * (new_pos_X + screen_size_X * new_pos_Y);
932 extern int fatal_error_in_progress;
934 if (fatal_error_in_progress)
935 return;
937 IT_set_face (0);
938 if (termscript)
939 fprintf (termscript, "<CLR:EOL>");
940 i = (j = screen_size_X - new_pos_X) * 2;
941 spaces = sp = alloca (i);
943 while (--j >= 0)
945 *sp++ = ' ';
946 *sp++ = ScreenAttrib;
949 mouse_off_maybe ();
950 dosmemput (spaces, i, (int)ScreenPrimary + offset);
951 if (screen_virtual_segment)
952 dosv_refresh_virtual_screen (offset, i / 2);
955 static void
956 IT_clear_screen (void)
958 if (termscript)
959 fprintf (termscript, "<CLR:SCR>");
960 IT_set_face (0);
961 mouse_off ();
962 ScreenClear ();
963 if (screen_virtual_segment)
964 dosv_refresh_virtual_screen (0, screen_size);
965 new_pos_X = new_pos_Y = 0;
968 static void
969 IT_clear_to_end (void)
971 if (termscript)
972 fprintf (termscript, "<CLR:EOS>");
974 while (new_pos_Y < screen_size_Y) {
975 new_pos_X = 0;
976 IT_clear_end_of_line (0);
977 new_pos_Y++;
981 static void
982 IT_cursor_to (int y, int x)
984 if (termscript)
985 fprintf (termscript, "\n<XY=%dx%d>", x, y);
986 new_pos_X = x;
987 new_pos_Y = y;
990 static int cursor_cleared;
992 static void
993 IT_display_cursor (int on)
995 if (on && cursor_cleared)
997 ScreenSetCursor (current_pos_Y, current_pos_X);
998 cursor_cleared = 0;
1000 else if (!on && !cursor_cleared)
1002 ScreenSetCursor (-1, -1);
1003 cursor_cleared = 1;
1007 /* Emacs calls cursor-movement functions a lot when it updates the
1008 display (probably a legacy of old terminals where you cannot
1009 update a screen line without first moving the cursor there).
1010 However, cursor movement is expensive on MSDOS (it calls a slow
1011 BIOS function and requires 2 mode switches), while actual screen
1012 updates access the video memory directly and don't depend on
1013 cursor position. To avoid slowing down the redisplay, we cheat:
1014 all functions that move the cursor only set internal variables
1015 which record the cursor position, whereas the cursor is only
1016 moved to its final position whenever screen update is complete.
1018 `IT_cmgoto' is called from the keyboard reading loop and when the
1019 frame update is complete. This means that we are ready for user
1020 input, so we update the cursor position to show where the point is,
1021 and also make the mouse pointer visible.
1023 Special treatment is required when the cursor is in the echo area,
1024 to put the cursor at the end of the text displayed there. */
1026 static void
1027 IT_cmgoto (FRAME_PTR f)
1029 /* Only set the cursor to where it should be if the display is
1030 already in sync with the window contents. */
1031 int update_cursor_pos = MODIFF == unchanged_modified;
1032 static int previous_pos_X = -1;
1034 /* If the display is in sync, forget any previous knowledge about
1035 cursor position. This is primarily for unexpected events like
1036 C-g in the minibuffer. */
1037 if (update_cursor_pos && previous_pos_X >= 0)
1038 previous_pos_X = -1;
1039 /* If we are in the echo area, put the cursor at the
1040 end of the echo area message. */
1041 if (!update_cursor_pos
1042 && XFASTINT (XWINDOW (FRAME_MINIBUF_WINDOW (f))->top) <= new_pos_Y)
1044 int tem_X = current_pos_X, dummy;
1046 if (echo_area_glyphs)
1048 tem_X = echo_area_glyphs_length;
1049 /* Save current cursor position, to be restored after the
1050 echo area message is erased. Only remember one level
1051 of previous cursor position. */
1052 if (previous_pos_X == -1)
1053 ScreenGetCursor (&dummy, &previous_pos_X);
1055 else if (previous_pos_X >= 0)
1057 /* We wind up here after the echo area message is erased.
1058 Restore the cursor position we remembered above. */
1059 tem_X = previous_pos_X;
1060 previous_pos_X = -1;
1063 if (current_pos_X != tem_X)
1065 new_pos_X = tem_X;
1066 update_cursor_pos = 1;
1070 if (update_cursor_pos
1071 && (current_pos_X != new_pos_X || current_pos_Y != new_pos_Y))
1073 ScreenSetCursor (current_pos_Y = new_pos_Y, current_pos_X = new_pos_X);
1074 if (termscript)
1075 fprintf (termscript, "\n<CURSOR:%dx%d>", current_pos_X, current_pos_Y);
1078 /* Maybe cursor is invisible, so make it visible. */
1079 IT_display_cursor (1);
1081 /* Mouse pointer should be always visible if we are waiting for
1082 keyboard input. */
1083 if (!mouse_visible)
1084 mouse_on ();
1087 static void
1088 IT_reassert_line_highlight (int new, int vpos)
1090 highlight = new;
1091 IT_set_face (0); /* To possibly clear the highlighting. */
1094 static void
1095 IT_change_line_highlight (int new_highlight, int y, int vpos, int first_unused_hpos)
1097 highlight = new_highlight;
1098 IT_set_face (0); /* To possibly clear the highlighting. */
1099 IT_cursor_to (vpos, 0);
1100 IT_clear_end_of_line (first_unused_hpos);
1103 static void
1104 IT_update_begin (struct frame *foo)
1106 highlight = 0;
1107 IT_set_face (0); /* To possibly clear the highlighting. */
1108 screen_face = -1;
1111 static void
1112 IT_update_end (struct frame *foo)
1116 /* Copy LEN glyphs displayed on a single line whose vertical position
1117 is YPOS, beginning at horizontal position XFROM to horizontal
1118 position XTO, by moving blocks in the video memory. Used by
1119 functions that insert and delete glyphs. */
1120 static void
1121 IT_copy_glyphs (int xfrom, int xto, size_t len, int ypos)
1123 /* The offsets of source and destination relative to the
1124 conventional memorty selector. */
1125 int from = 2 * (xfrom + screen_size_X * ypos) + ScreenPrimary;
1126 int to = 2 * (xto + screen_size_X * ypos) + ScreenPrimary;
1128 if (from == to || len <= 0)
1129 return;
1131 _farsetsel (_dos_ds);
1133 /* The source and destination might overlap, so we need to move
1134 glyphs non-destructively. */
1135 if (from > to)
1137 for ( ; len; from += 2, to += 2, len--)
1138 _farnspokew (to, _farnspeekw (from));
1140 else
1142 from += (len - 1) * 2;
1143 to += (len - 1) * 2;
1144 for ( ; len; from -= 2, to -= 2, len--)
1145 _farnspokew (to, _farnspeekw (from));
1147 if (screen_virtual_segment)
1148 dosv_refresh_virtual_screen (ypos * screen_size_X * 2, screen_size_X);
1151 /* Insert and delete glyphs. */
1152 static void
1153 IT_insert_glyphs (start, len)
1154 register struct glyph *start;
1155 register int len;
1157 int shift_by_width = screen_size_X - (new_pos_X + len);
1159 /* Shift right the glyphs from the nominal cursor position to the
1160 end of this line. */
1161 IT_copy_glyphs (new_pos_X, new_pos_X + len, shift_by_width, new_pos_Y);
1163 /* Now write the glyphs to be inserted. */
1164 IT_write_glyphs (start, len);
1167 static void
1168 IT_delete_glyphs (n)
1169 register int n;
1171 abort ();
1174 /* set-window-configuration on window.c needs this. */
1175 void
1176 x_set_menu_bar_lines (f, value, oldval)
1177 struct frame *f;
1178 Lisp_Object value, oldval;
1180 set_menu_bar_lines (f, value, oldval);
1183 /* This was copied from xfns.c */
1185 Lisp_Object Qbackground_color;
1186 Lisp_Object Qforeground_color;
1187 extern Lisp_Object Qtitle;
1189 /* IT_set_terminal_modes is called when emacs is started,
1190 resumed, and whenever the screen is redrawn! */
1192 static void
1193 IT_set_terminal_modes (void)
1195 if (termscript)
1196 fprintf (termscript, "\n<SET_TERM>");
1197 highlight = 0;
1199 screen_size_X = ScreenCols ();
1200 screen_size_Y = ScreenRows ();
1201 screen_size = screen_size_X * screen_size_Y;
1203 new_pos_X = new_pos_Y = 0;
1204 current_pos_X = current_pos_Y = -1;
1206 if (term_setup_done)
1207 return;
1208 term_setup_done = 1;
1210 startup_screen_size_X = screen_size_X;
1211 startup_screen_size_Y = screen_size_Y;
1212 startup_screen_attrib = ScreenAttrib;
1214 #if __DJGPP__ > 1
1215 /* Is DOS/V (or any other RSIS software which relocates
1216 the screen) installed? */
1218 unsigned short es_value;
1219 __dpmi_regs regs;
1221 regs.h.ah = 0xfe; /* get relocated screen address */
1222 if (ScreenPrimary == 0xb0000UL || ScreenPrimary == 0xb8000UL)
1223 regs.x.es = (ScreenPrimary >> 4) & 0xffff;
1224 else if (screen_old_address) /* already switched to Japanese mode once */
1225 regs.x.es = (screen_old_address >> 4) & 0xffff;
1226 else
1227 regs.x.es = ScreenMode () == 7 ? 0xb000 : 0xb800;
1228 regs.x.di = 0;
1229 es_value = regs.x.es;
1230 __dpmi_int (0x10, &regs);
1232 if (regs.x.es != es_value)
1234 /* screen_old_address is only set if ScreenPrimary does NOT
1235 already point to the relocated buffer address returned by
1236 the Int 10h/AX=FEh call above. DJGPP v2.02 and later sets
1237 ScreenPrimary to that address at startup under DOS/V. */
1238 if (regs.x.es != (ScreenPrimary >> 4) & 0xffff)
1239 screen_old_address = ScreenPrimary;
1240 screen_virtual_segment = regs.x.es;
1241 screen_virtual_offset = regs.x.di;
1242 ScreenPrimary = (screen_virtual_segment << 4) + screen_virtual_offset;
1245 #endif /* __DJGPP__ > 1 */
1247 ScreenGetCursor (&startup_pos_Y, &startup_pos_X);
1248 ScreenRetrieve (startup_screen_buffer = xmalloc (screen_size * 2));
1250 if (termscript)
1251 fprintf (termscript, "<SCREEN SAVED (dimensions=%dx%d)>\n",
1252 screen_size_X, screen_size_Y);
1254 bright_bg ();
1257 /* IT_reset_terminal_modes is called when emacs is
1258 suspended or killed. */
1260 static void
1261 IT_reset_terminal_modes (void)
1263 int display_row_start = (int) ScreenPrimary;
1264 int saved_row_len = startup_screen_size_X * 2;
1265 int update_row_len = ScreenCols () * 2;
1266 int current_rows = ScreenRows ();
1267 int to_next_row = update_row_len;
1268 unsigned char *saved_row = startup_screen_buffer;
1269 int cursor_pos_X = ScreenCols () - 1;
1270 int cursor_pos_Y = ScreenRows () - 1;
1272 if (termscript)
1273 fprintf (termscript, "\n<RESET_TERM>");
1275 highlight = 0;
1277 if (!term_setup_done)
1278 return;
1280 mouse_off ();
1282 /* Leave the video system in the same state as we found it,
1283 as far as the blink/bright-background bit is concerned. */
1284 maybe_enable_blinking ();
1286 /* We have a situation here.
1287 We cannot just do ScreenUpdate(startup_screen_buffer) because
1288 the luser could have changed screen dimensions inside Emacs
1289 and failed (or didn't want) to restore them before killing
1290 Emacs. ScreenUpdate() uses the *current* screen dimensions and
1291 thus will happily use memory outside what was allocated for
1292 `startup_screen_buffer'.
1293 Thus we only restore as much as the current screen dimensions
1294 can hold, and clear the rest (if the saved screen is smaller than
1295 the current) with the color attribute saved at startup. The cursor
1296 is also restored within the visible dimensions. */
1298 ScreenAttrib = startup_screen_attrib;
1300 /* Don't restore the screen if we are exiting less than 2 seconds
1301 after startup: we might be crashing, and the screen might show
1302 some vital clues to what's wrong. */
1303 if (clock () - startup_time >= 2*CLOCKS_PER_SEC)
1305 ScreenClear ();
1306 if (screen_virtual_segment)
1307 dosv_refresh_virtual_screen (0, screen_size);
1309 if (update_row_len > saved_row_len)
1310 update_row_len = saved_row_len;
1311 if (current_rows > startup_screen_size_Y)
1312 current_rows = startup_screen_size_Y;
1314 if (termscript)
1315 fprintf (termscript, "<SCREEN RESTORED (dimensions=%dx%d)>\n",
1316 update_row_len / 2, current_rows);
1318 while (current_rows--)
1320 dosmemput (saved_row, update_row_len, display_row_start);
1321 if (screen_virtual_segment)
1322 dosv_refresh_virtual_screen (display_row_start - ScreenPrimary,
1323 update_row_len / 2);
1324 saved_row += saved_row_len;
1325 display_row_start += to_next_row;
1328 if (startup_pos_X < cursor_pos_X)
1329 cursor_pos_X = startup_pos_X;
1330 if (startup_pos_Y < cursor_pos_Y)
1331 cursor_pos_Y = startup_pos_Y;
1333 ScreenSetCursor (cursor_pos_Y, cursor_pos_X);
1334 xfree (startup_screen_buffer);
1336 term_setup_done = 0;
1339 static void
1340 IT_set_terminal_window (int foo)
1344 void
1345 IT_set_frame_parameters (f, alist)
1346 struct frame *f;
1347 Lisp_Object alist;
1349 Lisp_Object tail;
1350 int length = XINT (Flength (alist));
1351 int i;
1352 Lisp_Object *parms
1353 = (Lisp_Object *) alloca (length * sizeof (Lisp_Object));
1354 Lisp_Object *values
1355 = (Lisp_Object *) alloca (length * sizeof (Lisp_Object));
1356 int redraw;
1357 struct face *dflt = NULL;
1359 if (FRAME_FACE_CACHE (f))
1360 dflt = FACE_FROM_ID (f, DEFAULT_FACE_ID);
1362 redraw = 0;
1364 /* Extract parm names and values into those vectors. */
1365 i = 0;
1366 for (tail = alist; CONSP (tail); tail = Fcdr (tail))
1368 Lisp_Object elt;
1370 elt = Fcar (tail);
1371 parms[i] = Fcar (elt);
1372 CHECK_SYMBOL (parms[i], 1);
1373 values[i] = Fcdr (elt);
1374 i++;
1378 /* Now process them in reverse of specified order. */
1379 for (i--; i >= 0; i--)
1381 Lisp_Object prop = parms[i];
1382 Lisp_Object val = values[i];
1384 if (EQ (prop, Qforeground_color))
1386 unsigned long new_color = load_color (f, NULL, val,
1387 LFACE_FOREGROUND_INDEX);
1388 if (new_color != ~0)
1390 if (!dflt)
1391 abort ();
1392 FRAME_FOREGROUND_PIXEL (f) = new_color;
1393 dflt->foreground = new_color;
1394 redraw = 1;
1395 if (termscript)
1396 fprintf (termscript, "<FGCOLOR %lu>\n", new_color);
1399 else if (EQ (prop, Qbackground_color))
1401 unsigned long new_color = load_color (f, NULL, val,
1402 LFACE_BACKGROUND_INDEX);
1403 if (new_color != ~0)
1405 if (!dflt)
1406 abort ();
1407 FRAME_BACKGROUND_PIXEL (f) = new_color;
1408 dflt->background = new_color;
1409 redraw = 1;
1410 if (termscript)
1411 fprintf (termscript, "<BGCOLOR %lu>\n", new_color);
1414 else if (EQ (prop, Qtitle))
1416 x_set_title (f, val);
1417 if (termscript)
1418 fprintf (termscript, "<TITLE: %s>\n", XSTRING (val)->data);
1420 else if (EQ (prop, intern ("reverse")) && EQ (val, Qt))
1422 unsigned long fg = FRAME_FOREGROUND_PIXEL (f);
1424 if (!dflt)
1425 abort ();
1426 FRAME_FOREGROUND_PIXEL (f) = FRAME_BACKGROUND_PIXEL (f); /* FIXME! */
1427 FRAME_BACKGROUND_PIXEL (f) = fg;
1428 dflt->foreground = FRAME_FOREGROUND_PIXEL (f);
1429 dflt->foreground = fg;
1430 if (termscript)
1431 fprintf (termscript, "<INVERSE-VIDEO>\n");
1433 store_frame_param (f, prop, val);
1437 if (redraw)
1439 recompute_basic_faces (f);
1440 if (f == selected_frame)
1441 redraw_frame (f);
1445 extern void init_frame_faces (FRAME_PTR);
1447 #endif /* !HAVE_X_WINDOWS */
1450 /* Do we need the internal terminal? */
1452 void
1453 internal_terminal_init ()
1455 char *term = getenv ("TERM");
1456 char *colors;
1458 #ifdef HAVE_X_WINDOWS
1459 if (!inhibit_window_system)
1460 return;
1461 #endif
1463 internal_terminal
1464 = (!noninteractive) && term && !strcmp (term, "internal");
1466 if (getenv ("EMACSTEST"))
1467 termscript = fopen (getenv ("EMACSTEST"), "wt");
1469 #ifndef HAVE_X_WINDOWS
1470 if (!internal_terminal || inhibit_window_system)
1472 selected_frame->output_method = output_termcap;
1473 return;
1476 Vwindow_system = intern ("pc");
1477 Vwindow_system_version = make_number (1);
1479 /* If Emacs was dumped on DOS/V machine, forget the stale VRAM address. */
1480 screen_old_address = 0;
1482 bzero (&the_only_x_display, sizeof the_only_x_display);
1483 the_only_x_display.background_pixel = 7; /* White */
1484 the_only_x_display.foreground_pixel = 0; /* Black */
1485 bright_bg ();
1486 colors = getenv ("EMACSCOLORS");
1487 if (colors && strlen (colors) >= 2)
1489 /* The colors use 4 bits each (we enable bright background). */
1490 if (isdigit (colors[0]))
1491 colors[0] -= '0';
1492 else if (isxdigit (colors[0]))
1493 colors[0] -= (isupper (colors[0]) ? 'A' : 'a') - 10;
1494 if (colors[0] >= 0 && colors[0] < 16)
1495 the_only_x_display.foreground_pixel = colors[0];
1496 if (isdigit (colors[1]))
1497 colors[1] -= '0';
1498 else if (isxdigit (colors[1]))
1499 colors[1] -= (isupper (colors[1]) ? 'A' : 'a') - 10;
1500 if (colors[1] >= 0 && colors[1] < 16)
1501 the_only_x_display.background_pixel = colors[1];
1503 the_only_x_display.line_height = 1;
1504 the_only_x_display.font = (XFontStruct *)1; /* must *not* be zero */
1506 init_frame_faces (selected_frame);
1508 ring_bell_hook = IT_ring_bell;
1509 insert_glyphs_hook = IT_insert_glyphs;
1510 delete_glyphs_hook = IT_delete_glyphs;
1511 write_glyphs_hook = IT_write_glyphs;
1512 cursor_to_hook = raw_cursor_to_hook = IT_cursor_to;
1513 clear_to_end_hook = IT_clear_to_end;
1514 clear_end_of_line_hook = IT_clear_end_of_line;
1515 clear_frame_hook = IT_clear_screen;
1516 change_line_highlight_hook = IT_change_line_highlight;
1517 update_begin_hook = IT_update_begin;
1518 update_end_hook = IT_update_end;
1519 reassert_line_highlight_hook = IT_reassert_line_highlight;
1520 frame_up_to_date_hook = IT_cmgoto; /* position cursor when update is done */
1522 /* These hooks are called by term.c without being checked. */
1523 set_terminal_modes_hook = IT_set_terminal_modes;
1524 reset_terminal_modes_hook = IT_reset_terminal_modes;
1525 set_terminal_window_hook = IT_set_terminal_window;
1526 char_ins_del_ok = 0;
1527 #endif
1530 dos_get_saved_screen (screen, rows, cols)
1531 char **screen;
1532 int *rows;
1533 int *cols;
1535 #ifndef HAVE_X_WINDOWS
1536 *screen = startup_screen_buffer;
1537 *cols = startup_screen_size_X;
1538 *rows = startup_screen_size_Y;
1539 return *screen != (char *)0;
1540 #else
1541 return 0;
1542 #endif
1545 #ifndef HAVE_X_WINDOWS
1547 /* We are not X, but we can emulate it well enough for our needs... */
1548 void
1549 check_x (void)
1551 if (! FRAME_MSDOS_P (selected_frame))
1552 error ("Not running under a windows system");
1555 #endif
1558 /* ----------------------- Keyboard control ----------------------
1560 * Keymaps reflect the following keyboard layout:
1562 * 0 1 2 3 4 5 6 7 8 9 10 11 12 BS
1563 * TAB 15 16 17 18 19 20 21 22 23 24 25 26 (41)
1564 * CLOK 30 31 32 33 34 35 36 37 38 39 40 (41) RET
1565 * SH () 45 46 47 48 49 50 51 52 53 54 SHIFT
1566 * SPACE
1569 #define Ignore 0x0000
1570 #define Normal 0x0000 /* normal key - alt changes scan-code */
1571 #define FctKey 0x1000 /* func key if c == 0, else c */
1572 #define Special 0x2000 /* func key even if c != 0 */
1573 #define ModFct 0x3000 /* special if mod-keys, else 'c' */
1574 #define Map 0x4000 /* alt scan-code, map to unshift/shift key */
1575 #define KeyPad 0x5000 /* map to insert/kp-0 depending on c == 0xe0 */
1576 #define Grey 0x6000 /* Grey keypad key */
1578 #define Alt 0x0100 /* alt scan-code */
1579 #define Ctrl 0x0200 /* ctrl scan-code */
1580 #define Shift 0x0400 /* shift scan-code */
1582 static int extended_kbd; /* 101 (102) keyboard present. */
1584 struct kbd_translate {
1585 unsigned char sc;
1586 unsigned char ch;
1587 unsigned short code;
1590 struct dos_keyboard_map
1592 char *unshifted;
1593 char *shifted;
1594 char *alt_gr;
1595 struct kbd_translate *translate_table;
1599 static struct dos_keyboard_map us_keyboard = {
1600 /* 0 1 2 3 4 5 */
1601 /* 01234567890123456789012345678901234567890 12345678901234 */
1602 "`1234567890-= qwertyuiop[] asdfghjkl;'\\ zxcvbnm,./ ",
1603 /* 0123456789012345678901234567890123456789 012345678901234 */
1604 "~!@#$%^&*()_+ QWERTYUIOP{} ASDFGHJKL:\"| ZXCVBNM<>? ",
1605 0, /* no Alt-Gr key */
1606 0 /* no translate table */
1609 static struct dos_keyboard_map fr_keyboard = {
1610 /* 0 1 2 3 4 5 */
1611 /* 012 3456789012345678901234567890123456789012345678901234 */
1612 "ý&‚\",(-Š_€…)= azertyuiop^$ qsdfghjklm—* wxcvbnm;:! ",
1613 /* 0123456789012345678901234567890123456789012345678901234 */
1614 " 1234567890ø+ AZERTYUIOPùœ QSDFGHJKLM%æ WXCVBN?./õ ",
1615 /* 01234567 89012345678901234567890123456789012345678901234 */
1616 " ~#{[|`\\^@]} Ï ",
1617 0 /* no translate table */
1621 * Italian keyboard support, country code 39.
1622 * '<' 56:3c*0000
1623 * '>' 56:3e*0000
1624 * added also {,},` as, respectively, AltGr-8, AltGr-9, AltGr-'
1625 * Donated by Stefano Brozzi <brozzis@mag00.cedi.unipr.it>
1628 static struct kbd_translate it_kbd_translate_table[] = {
1629 { 0x56, 0x3c, Normal | 13 },
1630 { 0x56, 0x3e, Normal | 27 },
1631 { 0, 0, 0 }
1633 static struct dos_keyboard_map it_keyboard = {
1634 /* 0 1 2 3 4 5 */
1635 /* 0 123456789012345678901234567890123456789012345678901234 */
1636 "\\1234567890'�< qwertyuiopŠ+> asdfghjkl•…— zxcvbnm,.- ",
1637 /* 01 23456789012345678901234567890123456789012345678901234 */
1638 "|!\"œ$%&/()=?^> QWERTYUIOP‚* ASDFGHJKL‡øõ ZXCVBNM;:_ ",
1639 /* 0123456789012345678901234567890123456789012345678901234 */
1640 " {}~` [] @# ",
1641 it_kbd_translate_table
1644 static struct dos_keyboard_map dk_keyboard = {
1645 /* 0 1 2 3 4 5 */
1646 /* 0123456789012345678901234567890123456789012345678901234 */
1647 "«1234567890+| qwertyuiop†~ asdfghjkl‘›' zxcvbnm,.- ",
1648 /* 01 23456789012345678901234567890123456789012345678901234 */
1649 "õ!\"#$%&/()=?` QWERTYUIOP�^ ASDFGHJKL’�* ZXCVBNM;:_ ",
1650 /* 0123456789012345678901234567890123456789012345678901234 */
1651 " @œ$ {[]} | ",
1652 0 /* no translate table */
1655 static struct kbd_translate jp_kbd_translate_table[] = {
1656 { 0x73, 0x5c, Normal | 0 },
1657 { 0x73, 0x5f, Normal | 0 },
1658 { 0x73, 0x1c, Map | 0 },
1659 { 0x7d, 0x5c, Normal | 13 },
1660 { 0x7d, 0x7c, Normal | 13 },
1661 { 0x7d, 0x1c, Map | 13 },
1662 { 0, 0, 0 }
1664 static struct dos_keyboard_map jp_keyboard = {
1665 /* 0 1 2 3 4 5 */
1666 /* 0123456789012 345678901234567890123456789012345678901234 */
1667 "\\1234567890-^\\ qwertyuiop@[ asdfghjkl;:] zxcvbnm,./ ",
1668 /* 01 23456789012345678901234567890123456789012345678901234 */
1669 "_!\"#$%&'()~=~| QWERTYUIOP`{ ASDFGHJKL+*} ZXCVBNM<>? ",
1670 0, /* no Alt-Gr key */
1671 jp_kbd_translate_table
1674 static struct keyboard_layout_list
1676 int country_code;
1677 struct dos_keyboard_map *keyboard_map;
1678 } keyboard_layout_list[] =
1680 1, &us_keyboard,
1681 33, &fr_keyboard,
1682 39, &it_keyboard,
1683 45, &dk_keyboard,
1684 81, &jp_keyboard
1687 static struct dos_keyboard_map *keyboard;
1688 static int keyboard_map_all;
1689 static int international_keyboard;
1692 dos_set_keyboard (code, always)
1693 int code;
1694 int always;
1696 int i;
1697 _go32_dpmi_registers regs;
1699 /* See if Keyb.Com is installed (for international keyboard support).
1700 Note: calling Int 2Fh via int86 wedges the DOS box on some versions
1701 of Windows 9X! So don't do that! */
1702 regs.x.ax = 0xad80;
1703 regs.x.ss = regs.x.sp = regs.x.flags = 0;
1704 _go32_dpmi_simulate_int (0x2f, &regs);
1705 if (regs.h.al == 0xff)
1706 international_keyboard = 1;
1708 /* Initialize to US settings, for countries that don't have their own. */
1709 keyboard = keyboard_layout_list[0].keyboard_map;
1710 keyboard_map_all = always;
1711 dos_keyboard_layout = 1;
1713 for (i = 0; i < (sizeof (keyboard_layout_list)/sizeof (struct keyboard_layout_list)); i++)
1714 if (code == keyboard_layout_list[i].country_code)
1716 keyboard = keyboard_layout_list[i].keyboard_map;
1717 keyboard_map_all = always;
1718 dos_keyboard_layout = code;
1719 return 1;
1721 return 0;
1724 static struct
1726 unsigned char char_code; /* normal code */
1727 unsigned char meta_code; /* M- code */
1728 unsigned char keypad_code; /* keypad code */
1729 unsigned char editkey_code; /* edit key */
1730 } keypad_translate_map[] = {
1731 '0', '0', 0xb0, /* kp-0 */ 0x63, /* insert */
1732 '1', '1', 0xb1, /* kp-1 */ 0x57, /* end */
1733 '2', '2', 0xb2, /* kp-2 */ 0x54, /* down */
1734 '3', '3', 0xb3, /* kp-3 */ 0x56, /* next */
1735 '4', '4', 0xb4, /* kp-4 */ 0x51, /* left */
1736 '5', '5', 0xb5, /* kp-5 */ 0xb5, /* kp-5 */
1737 '6', '6', 0xb6, /* kp-6 */ 0x53, /* right */
1738 '7', '7', 0xb7, /* kp-7 */ 0x50, /* home */
1739 '8', '8', 0xb8, /* kp-8 */ 0x52, /* up */
1740 '9', '9', 0xb9, /* kp-9 */ 0x55, /* prior */
1741 '.', '-', 0xae, /* kp-decimal */ 0xff /* delete */
1744 static struct
1746 unsigned char char_code; /* normal code */
1747 unsigned char keypad_code; /* keypad code */
1748 } grey_key_translate_map[] = {
1749 '/', 0xaf, /* kp-decimal */
1750 '*', 0xaa, /* kp-multiply */
1751 '-', 0xad, /* kp-subtract */
1752 '+', 0xab, /* kp-add */
1753 '\r', 0x8d /* kp-enter */
1756 static unsigned short
1757 ibmpc_translate_map[] =
1759 /* --------------- 00 to 0f --------------- */
1760 Normal | 0xff, /* Ctrl Break + Alt-NNN */
1761 Alt | ModFct | 0x1b, /* Escape */
1762 Normal | 1, /* '1' */
1763 Normal | 2, /* '2' */
1764 Normal | 3, /* '3' */
1765 Normal | 4, /* '4' */
1766 Normal | 5, /* '5' */
1767 Normal | 6, /* '6' */
1768 Normal | 7, /* '7' */
1769 Normal | 8, /* '8' */
1770 Normal | 9, /* '9' */
1771 Normal | 10, /* '0' */
1772 Normal | 11, /* '-' */
1773 Normal | 12, /* '=' */
1774 Special | 0x08, /* Backspace */
1775 ModFct | 0x74, /* Tab/Backtab */
1777 /* --------------- 10 to 1f --------------- */
1778 Map | 15, /* 'q' */
1779 Map | 16, /* 'w' */
1780 Map | 17, /* 'e' */
1781 Map | 18, /* 'r' */
1782 Map | 19, /* 't' */
1783 Map | 20, /* 'y' */
1784 Map | 21, /* 'u' */
1785 Map | 22, /* 'i' */
1786 Map | 23, /* 'o' */
1787 Map | 24, /* 'p' */
1788 Map | 25, /* '[' */
1789 Map | 26, /* ']' */
1790 ModFct | 0x0d, /* Return */
1791 Ignore, /* Ctrl */
1792 Map | 30, /* 'a' */
1793 Map | 31, /* 's' */
1795 /* --------------- 20 to 2f --------------- */
1796 Map | 32, /* 'd' */
1797 Map | 33, /* 'f' */
1798 Map | 34, /* 'g' */
1799 Map | 35, /* 'h' */
1800 Map | 36, /* 'j' */
1801 Map | 37, /* 'k' */
1802 Map | 38, /* 'l' */
1803 Map | 39, /* ';' */
1804 Map | 40, /* '\'' */
1805 Map | 0, /* '`' */
1806 Ignore, /* Left shift */
1807 Map | 41, /* '\\' */
1808 Map | 45, /* 'z' */
1809 Map | 46, /* 'x' */
1810 Map | 47, /* 'c' */
1811 Map | 48, /* 'v' */
1813 /* --------------- 30 to 3f --------------- */
1814 Map | 49, /* 'b' */
1815 Map | 50, /* 'n' */
1816 Map | 51, /* 'm' */
1817 Map | 52, /* ',' */
1818 Map | 53, /* '.' */
1819 Map | 54, /* '/' */
1820 Ignore, /* Right shift */
1821 Grey | 1, /* Grey * */
1822 Ignore, /* Alt */
1823 Normal | 55, /* ' ' */
1824 Ignore, /* Caps Lock */
1825 FctKey | 0xbe, /* F1 */
1826 FctKey | 0xbf, /* F2 */
1827 FctKey | 0xc0, /* F3 */
1828 FctKey | 0xc1, /* F4 */
1829 FctKey | 0xc2, /* F5 */
1831 /* --------------- 40 to 4f --------------- */
1832 FctKey | 0xc3, /* F6 */
1833 FctKey | 0xc4, /* F7 */
1834 FctKey | 0xc5, /* F8 */
1835 FctKey | 0xc6, /* F9 */
1836 FctKey | 0xc7, /* F10 */
1837 Ignore, /* Num Lock */
1838 Ignore, /* Scroll Lock */
1839 KeyPad | 7, /* Home */
1840 KeyPad | 8, /* Up */
1841 KeyPad | 9, /* Page Up */
1842 Grey | 2, /* Grey - */
1843 KeyPad | 4, /* Left */
1844 KeyPad | 5, /* Keypad 5 */
1845 KeyPad | 6, /* Right */
1846 Grey | 3, /* Grey + */
1847 KeyPad | 1, /* End */
1849 /* --------------- 50 to 5f --------------- */
1850 KeyPad | 2, /* Down */
1851 KeyPad | 3, /* Page Down */
1852 KeyPad | 0, /* Insert */
1853 KeyPad | 10, /* Delete */
1854 Shift | FctKey | 0xbe, /* (Shift) F1 */
1855 Shift | FctKey | 0xbf, /* (Shift) F2 */
1856 Shift | FctKey | 0xc0, /* (Shift) F3 */
1857 Shift | FctKey | 0xc1, /* (Shift) F4 */
1858 Shift | FctKey | 0xc2, /* (Shift) F5 */
1859 Shift | FctKey | 0xc3, /* (Shift) F6 */
1860 Shift | FctKey | 0xc4, /* (Shift) F7 */
1861 Shift | FctKey | 0xc5, /* (Shift) F8 */
1862 Shift | FctKey | 0xc6, /* (Shift) F9 */
1863 Shift | FctKey | 0xc7, /* (Shift) F10 */
1864 Ctrl | FctKey | 0xbe, /* (Ctrl) F1 */
1865 Ctrl | FctKey | 0xbf, /* (Ctrl) F2 */
1867 /* --------------- 60 to 6f --------------- */
1868 Ctrl | FctKey | 0xc0, /* (Ctrl) F3 */
1869 Ctrl | FctKey | 0xc1, /* (Ctrl) F4 */
1870 Ctrl | FctKey | 0xc2, /* (Ctrl) F5 */
1871 Ctrl | FctKey | 0xc3, /* (Ctrl) F6 */
1872 Ctrl | FctKey | 0xc4, /* (Ctrl) F7 */
1873 Ctrl | FctKey | 0xc5, /* (Ctrl) F8 */
1874 Ctrl | FctKey | 0xc6, /* (Ctrl) F9 */
1875 Ctrl | FctKey | 0xc7, /* (Ctrl) F10 */
1876 Alt | FctKey | 0xbe, /* (Alt) F1 */
1877 Alt | FctKey | 0xbf, /* (Alt) F2 */
1878 Alt | FctKey | 0xc0, /* (Alt) F3 */
1879 Alt | FctKey | 0xc1, /* (Alt) F4 */
1880 Alt | FctKey | 0xc2, /* (Alt) F5 */
1881 Alt | FctKey | 0xc3, /* (Alt) F6 */
1882 Alt | FctKey | 0xc4, /* (Alt) F7 */
1883 Alt | FctKey | 0xc5, /* (Alt) F8 */
1885 /* --------------- 70 to 7f --------------- */
1886 Alt | FctKey | 0xc6, /* (Alt) F9 */
1887 Alt | FctKey | 0xc7, /* (Alt) F10 */
1888 Ctrl | FctKey | 0x6d, /* (Ctrl) Sys Rq */
1889 Ctrl | KeyPad | 4, /* (Ctrl) Left */
1890 Ctrl | KeyPad | 6, /* (Ctrl) Right */
1891 Ctrl | KeyPad | 1, /* (Ctrl) End */
1892 Ctrl | KeyPad | 3, /* (Ctrl) Page Down */
1893 Ctrl | KeyPad | 7, /* (Ctrl) Home */
1894 Alt | Map | 1, /* '1' */
1895 Alt | Map | 2, /* '2' */
1896 Alt | Map | 3, /* '3' */
1897 Alt | Map | 4, /* '4' */
1898 Alt | Map | 5, /* '5' */
1899 Alt | Map | 6, /* '6' */
1900 Alt | Map | 7, /* '7' */
1901 Alt | Map | 8, /* '8' */
1903 /* --------------- 80 to 8f --------------- */
1904 Alt | Map | 9, /* '9' */
1905 Alt | Map | 10, /* '0' */
1906 Alt | Map | 11, /* '-' */
1907 Alt | Map | 12, /* '=' */
1908 Ctrl | KeyPad | 9, /* (Ctrl) Page Up */
1909 FctKey | 0xc8, /* F11 */
1910 FctKey | 0xc9, /* F12 */
1911 Shift | FctKey | 0xc8, /* (Shift) F11 */
1912 Shift | FctKey | 0xc9, /* (Shift) F12 */
1913 Ctrl | FctKey | 0xc8, /* (Ctrl) F11 */
1914 Ctrl | FctKey | 0xc9, /* (Ctrl) F12 */
1915 Alt | FctKey | 0xc8, /* (Alt) F11 */
1916 Alt | FctKey | 0xc9, /* (Alt) F12 */
1917 Ctrl | KeyPad | 8, /* (Ctrl) Up */
1918 Ctrl | Grey | 2, /* (Ctrl) Grey - */
1919 Ctrl | KeyPad | 5, /* (Ctrl) Keypad 5 */
1921 /* --------------- 90 to 9f --------------- */
1922 Ctrl | Grey | 3, /* (Ctrl) Grey + */
1923 Ctrl | KeyPad | 2, /* (Ctrl) Down */
1924 Ctrl | KeyPad | 0, /* (Ctrl) Insert */
1925 Ctrl | KeyPad | 10, /* (Ctrl) Delete */
1926 Ctrl | FctKey | 0x09, /* (Ctrl) Tab */
1927 Ctrl | Grey | 0, /* (Ctrl) Grey / */
1928 Ctrl | Grey | 1, /* (Ctrl) Grey * */
1929 Alt | FctKey | 0x50, /* (Alt) Home */
1930 Alt | FctKey | 0x52, /* (Alt) Up */
1931 Alt | FctKey | 0x55, /* (Alt) Page Up */
1932 Ignore, /* NO KEY */
1933 Alt | FctKey | 0x51, /* (Alt) Left */
1934 Ignore, /* NO KEY */
1935 Alt | FctKey | 0x53, /* (Alt) Right */
1936 Ignore, /* NO KEY */
1937 Alt | FctKey | 0x57, /* (Alt) End */
1939 /* --------------- a0 to af --------------- */
1940 Alt | KeyPad | 2, /* (Alt) Down */
1941 Alt | KeyPad | 3, /* (Alt) Page Down */
1942 Alt | KeyPad | 0, /* (Alt) Insert */
1943 Alt | KeyPad | 10, /* (Alt) Delete */
1944 Alt | Grey | 0, /* (Alt) Grey / */
1945 Alt | FctKey | 0x09, /* (Alt) Tab */
1946 Alt | Grey | 4 /* (Alt) Keypad Enter */
1949 /* These bit-positions corresponds to values returned by BIOS */
1950 #define SHIFT_P 0x0003 /* two bits! */
1951 #define CTRL_P 0x0004
1952 #define ALT_P 0x0008
1953 #define SCRLOCK_P 0x0010
1954 #define NUMLOCK_P 0x0020
1955 #define CAPSLOCK_P 0x0040
1956 #define ALT_GR_P 0x0800
1957 #define SUPER_P 0x4000 /* pseudo */
1958 #define HYPER_P 0x8000 /* pseudo */
1960 static int
1961 dos_get_modifiers (keymask)
1962 int *keymask;
1964 union REGS regs;
1965 int mask;
1966 int modifiers = 0;
1968 /* Calculate modifier bits */
1969 regs.h.ah = extended_kbd ? 0x12 : 0x02;
1970 int86 (0x16, &regs, &regs);
1972 if (!extended_kbd)
1974 mask = regs.h.al & (SHIFT_P | CTRL_P | ALT_P |
1975 SCRLOCK_P | NUMLOCK_P | CAPSLOCK_P);
1977 else
1979 mask = regs.h.al & (SHIFT_P |
1980 SCRLOCK_P | NUMLOCK_P | CAPSLOCK_P);
1982 /* Do not break international keyboard support. */
1983 /* When Keyb.Com is loaded, the right Alt key is */
1984 /* used for accessing characters like { and } */
1985 if (regs.h.ah & 2) /* Left ALT pressed ? */
1986 mask |= ALT_P;
1988 if ((regs.h.ah & 8) != 0) /* Right ALT pressed ? */
1990 mask |= ALT_GR_P;
1991 if (dos_hyper_key == 1)
1993 mask |= HYPER_P;
1994 modifiers |= hyper_modifier;
1996 else if (dos_super_key == 1)
1998 mask |= SUPER_P;
1999 modifiers |= super_modifier;
2001 else if (!international_keyboard)
2003 /* If Keyb.Com is NOT installed, let Right Alt behave
2004 like the Left Alt. */
2005 mask &= ~ALT_GR_P;
2006 mask |= ALT_P;
2010 if (regs.h.ah & 1) /* Left CTRL pressed ? */
2011 mask |= CTRL_P;
2013 if (regs.h.ah & 4) /* Right CTRL pressed ? */
2015 if (dos_hyper_key == 2)
2017 mask |= HYPER_P;
2018 modifiers |= hyper_modifier;
2020 else if (dos_super_key == 2)
2022 mask |= SUPER_P;
2023 modifiers |= super_modifier;
2025 else
2026 mask |= CTRL_P;
2030 if (mask & SHIFT_P)
2031 modifiers |= shift_modifier;
2032 if (mask & CTRL_P)
2033 modifiers |= ctrl_modifier;
2034 if (mask & ALT_P)
2035 modifiers |= meta_modifier;
2037 if (keymask)
2038 *keymask = mask;
2039 return modifiers;
2042 #define NUM_RECENT_DOSKEYS (100)
2043 int recent_doskeys_index; /* Index for storing next element into recent_doskeys */
2044 int total_doskeys; /* Total number of elements stored into recent_doskeys */
2045 Lisp_Object recent_doskeys; /* A vector, holding the last 100 keystrokes */
2047 DEFUN ("recent-doskeys", Frecent_doskeys, Srecent_doskeys, 0, 0, 0,
2048 "Return vector of last 100 keyboard input values seen in dos_rawgetc.\n\
2049 Each input key receives two values in this vector: first the ASCII code,\n\
2050 and then the scan code.")
2053 Lisp_Object *keys = XVECTOR (recent_doskeys)->contents;
2054 Lisp_Object val;
2056 if (total_doskeys < NUM_RECENT_DOSKEYS)
2057 return Fvector (total_doskeys, keys);
2058 else
2060 val = Fvector (NUM_RECENT_DOSKEYS, keys);
2061 bcopy (keys + recent_doskeys_index,
2062 XVECTOR (val)->contents,
2063 (NUM_RECENT_DOSKEYS - recent_doskeys_index) * sizeof (Lisp_Object));
2064 bcopy (keys,
2065 XVECTOR (val)->contents + NUM_RECENT_DOSKEYS - recent_doskeys_index,
2066 recent_doskeys_index * sizeof (Lisp_Object));
2067 return val;
2071 /* Get a char from keyboard. Function keys are put into the event queue. */
2073 extern void kbd_buffer_store_event (struct input_event *);
2074 static int mouse_preempted = 0; /* non-zero when XMenu gobbles mouse events */
2076 static int
2077 dos_rawgetc ()
2079 struct input_event event;
2080 union REGS regs;
2082 #ifndef HAVE_X_WINDOWS
2083 /* Maybe put the cursor where it should be. */
2084 IT_cmgoto (selected_frame);
2085 #endif
2087 /* The following condition is equivalent to `kbhit ()', except that
2088 it uses the bios to do its job. This pleases DESQview/X. */
2089 while ((regs.h.ah = extended_kbd ? 0x11 : 0x01),
2090 int86 (0x16, &regs, &regs),
2091 (regs.x.flags & 0x40) == 0)
2093 union REGS regs;
2094 register unsigned char c;
2095 int sc, code = -1, mask, kp_mode;
2096 int modifiers;
2098 regs.h.ah = extended_kbd ? 0x10 : 0x00;
2099 int86 (0x16, &regs, &regs);
2100 c = regs.h.al;
2101 sc = regs.h.ah;
2103 total_doskeys += 2;
2104 XVECTOR (recent_doskeys)->contents[recent_doskeys_index++]
2105 = make_number (c);
2106 if (recent_doskeys_index == NUM_RECENT_DOSKEYS)
2107 recent_doskeys_index = 0;
2108 XVECTOR (recent_doskeys)->contents[recent_doskeys_index++]
2109 = make_number (sc);
2110 if (recent_doskeys_index == NUM_RECENT_DOSKEYS)
2111 recent_doskeys_index = 0;
2113 modifiers = dos_get_modifiers (&mask);
2115 #ifndef HAVE_X_WINDOWS
2116 if (!NILP (Vdos_display_scancodes))
2118 char buf[11];
2119 sprintf (buf, "%02x:%02x*%04x",
2120 (unsigned) (sc&0xff), (unsigned) c, mask);
2121 dos_direct_output (screen_size_Y - 2, screen_size_X - 12, buf, 10);
2123 #endif
2125 if (sc == 0xe0)
2127 switch (c)
2129 case 10: /* Ctrl Grey Enter */
2130 code = Ctrl | Grey | 4;
2131 break;
2132 case 13: /* Grey Enter */
2133 code = Grey | 4;
2134 break;
2135 case '/': /* Grey / */
2136 code = Grey | 0;
2137 break;
2138 default:
2139 continue;
2141 c = 0;
2143 else
2145 /* Try the keyboard-private translation table first. */
2146 if (keyboard->translate_table)
2148 struct kbd_translate *p = keyboard->translate_table;
2150 while (p->sc)
2152 if (p->sc == sc && p->ch == c)
2154 code = p->code;
2155 break;
2157 p++;
2160 /* If the private table didn't translate it, use the general
2161 one. */
2162 if (code == -1)
2164 if (sc >= (sizeof (ibmpc_translate_map) / sizeof (short)))
2165 continue;
2166 if ((code = ibmpc_translate_map[sc]) == Ignore)
2167 continue;
2171 if (c == 0)
2173 /* We only look at the keyboard Ctrl/Shift/Alt keys when
2174 Emacs is ready to read a key. Therefore, if they press
2175 `Alt-x' when Emacs is busy, by the time we get to
2176 `dos_get_modifiers', they might have already released the
2177 Alt key, and Emacs gets just `x', which is BAD.
2178 However, for keys with the `Map' property set, the ASCII
2179 code returns zero iff Alt is pressed. So, when we DON'T
2180 have to support international_keyboard, we don't have to
2181 distinguish between the left and right Alt keys, and we
2182 can set the META modifier for any keys with the `Map'
2183 property if they return zero ASCII code (c = 0). */
2184 if ( (code & Alt)
2185 || ( (code & 0xf000) == Map && !international_keyboard))
2186 modifiers |= meta_modifier;
2187 if (code & Ctrl)
2188 modifiers |= ctrl_modifier;
2189 if (code & Shift)
2190 modifiers |= shift_modifier;
2193 switch (code & 0xf000)
2195 case ModFct:
2196 if (c && !(mask & (SHIFT_P | ALT_P | CTRL_P | HYPER_P | SUPER_P)))
2197 return c;
2198 c = 0; /* Special */
2200 case FctKey:
2201 if (c != 0)
2202 return c;
2204 case Special:
2205 code |= 0xff00;
2206 break;
2208 case Normal:
2209 if (sc == 0)
2211 if (c == 0) /* ctrl-break */
2212 continue;
2213 return c; /* ALT-nnn */
2215 if (!keyboard_map_all)
2217 if (c != ' ')
2218 return c;
2219 code = c;
2220 break;
2223 case Map:
2224 if (c && !(mask & ALT_P) && !((mask & SHIFT_P) && (mask & CTRL_P)))
2225 if (!keyboard_map_all)
2226 return c;
2228 code &= 0xff;
2229 if (mask & ALT_P && code <= 10 && code > 0 && dos_keypad_mode & 0x200)
2230 mask |= SHIFT_P; /* ALT-1 => M-! etc. */
2232 if (mask & SHIFT_P)
2234 code = keyboard->shifted[code];
2235 mask -= SHIFT_P;
2236 modifiers &= ~shift_modifier;
2238 else
2239 if ((mask & ALT_GR_P) && keyboard->alt_gr && keyboard->alt_gr[code] != ' ')
2240 code = keyboard->alt_gr[code];
2241 else
2242 code = keyboard->unshifted[code];
2243 break;
2245 case KeyPad:
2246 code &= 0xff;
2247 if (c == 0xe0) /* edit key */
2248 kp_mode = 3;
2249 else
2250 if ((mask & (NUMLOCK_P|CTRL_P|SHIFT_P|ALT_P)) == NUMLOCK_P) /* numlock on */
2251 kp_mode = dos_keypad_mode & 0x03;
2252 else
2253 kp_mode = (dos_keypad_mode >> 4) & 0x03;
2255 switch (kp_mode)
2257 case 0:
2258 if (code == 10 && dos_decimal_point)
2259 return dos_decimal_point;
2260 return keypad_translate_map[code].char_code;
2262 case 1:
2263 code = 0xff00 | keypad_translate_map[code].keypad_code;
2264 break;
2266 case 2:
2267 code = keypad_translate_map[code].meta_code;
2268 modifiers = meta_modifier;
2269 break;
2271 case 3:
2272 code = 0xff00 | keypad_translate_map[code].editkey_code;
2273 break;
2275 break;
2277 case Grey:
2278 code &= 0xff;
2279 kp_mode = ((mask & (NUMLOCK_P|CTRL_P|SHIFT_P|ALT_P)) == NUMLOCK_P) ? 0x04 : 0x40;
2280 if (dos_keypad_mode & kp_mode)
2281 code = 0xff00 | grey_key_translate_map[code].keypad_code;
2282 else
2283 code = grey_key_translate_map[code].char_code;
2284 break;
2287 make_event:
2288 if (code == 0)
2289 continue;
2291 if (code >= 0x100)
2292 event.kind = non_ascii_keystroke;
2293 else
2294 event.kind = ascii_keystroke;
2295 event.code = code;
2296 event.modifiers = modifiers;
2297 XSETFRAME (event.frame_or_window, selected_frame);
2298 event.timestamp = event_timestamp ();
2299 kbd_buffer_store_event (&event);
2302 if (have_mouse > 0 && !mouse_preempted)
2304 int but, press, x, y, ok;
2306 /* Check for mouse movement *before* buttons. */
2307 mouse_check_moved ();
2309 for (but = 0; but < NUM_MOUSE_BUTTONS; but++)
2310 for (press = 0; press < 2; press++)
2312 int button_num = but;
2314 if (press)
2315 ok = mouse_pressed (but, &x, &y);
2316 else
2317 ok = mouse_released (but, &x, &y);
2318 if (ok)
2320 /* Allow a simultaneous press/release of Mouse-1 and
2321 Mouse-2 to simulate Mouse-3 on two-button mice. */
2322 if (mouse_button_count == 2 && but < 2)
2324 int x2, y2; /* don't clobber original coordinates */
2326 /* If only one button is pressed, wait 100 msec and
2327 check again. This way, Speedy Gonzales isn't
2328 punished, while the slow get their chance. */
2329 if (press && mouse_pressed (1-but, &x2, &y2)
2330 || !press && mouse_released (1-but, &x2, &y2))
2331 button_num = 2;
2332 else
2334 delay (100);
2335 if (press && mouse_pressed (1-but, &x2, &y2)
2336 || !press && mouse_released (1-but, &x2, &y2))
2337 button_num = 2;
2341 event.kind = mouse_click;
2342 event.code = button_num;
2343 event.modifiers = dos_get_modifiers (0)
2344 | (press ? down_modifier : up_modifier);
2345 event.x = x;
2346 event.y = y;
2347 XSETFRAME (event.frame_or_window, selected_frame);
2348 event.timestamp = event_timestamp ();
2349 kbd_buffer_store_event (&event);
2354 return -1;
2357 static int prev_get_char = -1;
2359 /* Return 1 if a key is ready to be read without suspending execution. */
2361 dos_keysns ()
2363 if (prev_get_char != -1)
2364 return 1;
2365 else
2366 return ((prev_get_char = dos_rawgetc ()) != -1);
2369 /* Read a key. Return -1 if no key is ready. */
2371 dos_keyread ()
2373 if (prev_get_char != -1)
2375 int c = prev_get_char;
2376 prev_get_char = -1;
2377 return c;
2379 else
2380 return dos_rawgetc ();
2383 #ifndef HAVE_X_WINDOWS
2384 /* See xterm.c for more info. */
2385 void
2386 pixel_to_glyph_coords (f, pix_x, pix_y, x, y, bounds, noclip)
2387 FRAME_PTR f;
2388 register int pix_x, pix_y;
2389 register int *x, *y;
2390 XRectangle *bounds;
2391 int noclip;
2393 if (bounds) abort ();
2395 /* Ignore clipping. */
2397 *x = pix_x;
2398 *y = pix_y;
2401 void
2402 glyph_to_pixel_coords (f, x, y, pix_x, pix_y)
2403 FRAME_PTR f;
2404 register int x, y;
2405 register int *pix_x, *pix_y;
2407 *pix_x = x;
2408 *pix_y = y;
2411 /* Simulation of X's menus. Nothing too fancy here -- just make it work
2412 for now.
2414 Actually, I don't know the meaning of all the parameters of the functions
2415 here -- I only know how they are called by xmenu.c. I could of course
2416 grab the nearest Xlib manual (down the hall, second-to-last door on the
2417 left), but I don't think it's worth the effort. */
2419 static XMenu *
2420 IT_menu_create ()
2422 XMenu *menu;
2424 menu = (XMenu *) xmalloc (sizeof (XMenu));
2425 menu->allocated = menu->count = menu->panecount = menu->width = 0;
2426 return menu;
2429 /* Allocate some (more) memory for MENU ensuring that there is room for one
2430 for item. */
2432 static void
2433 IT_menu_make_room (XMenu *menu)
2435 if (menu->allocated == 0)
2437 int count = menu->allocated = 10;
2438 menu->text = (char **) xmalloc (count * sizeof (char *));
2439 menu->submenu = (XMenu **) xmalloc (count * sizeof (XMenu *));
2440 menu->panenumber = (int *) xmalloc (count * sizeof (int));
2442 else if (menu->allocated == menu->count)
2444 int count = menu->allocated = menu->allocated + 10;
2445 menu->text
2446 = (char **) xrealloc (menu->text, count * sizeof (char *));
2447 menu->submenu
2448 = (XMenu **) xrealloc (menu->submenu, count * sizeof (XMenu *));
2449 menu->panenumber
2450 = (int *) xrealloc (menu->panenumber, count * sizeof (int));
2454 /* Search the given menu structure for a given pane number. */
2456 static XMenu *
2457 IT_menu_search_pane (XMenu *menu, int pane)
2459 int i;
2460 XMenu *try;
2462 for (i = 0; i < menu->count; i++)
2463 if (menu->submenu[i])
2465 if (pane == menu->panenumber[i])
2466 return menu->submenu[i];
2467 if ((try = IT_menu_search_pane (menu->submenu[i], pane)))
2468 return try;
2470 return (XMenu *) 0;
2473 /* Determine how much screen space a given menu needs. */
2475 static void
2476 IT_menu_calc_size (XMenu *menu, int *width, int *height)
2478 int i, h2, w2, maxsubwidth, maxheight;
2480 maxsubwidth = 0;
2481 maxheight = menu->count;
2482 for (i = 0; i < menu->count; i++)
2484 if (menu->submenu[i])
2486 IT_menu_calc_size (menu->submenu[i], &w2, &h2);
2487 if (w2 > maxsubwidth) maxsubwidth = w2;
2488 if (i + h2 > maxheight) maxheight = i + h2;
2491 *width = menu->width + maxsubwidth;
2492 *height = maxheight;
2495 /* Display MENU at (X,Y) using FACES. */
2497 static void
2498 IT_menu_display (XMenu *menu, int y, int x, int *faces)
2500 int i, j, face, width;
2501 struct glyph *text, *p;
2502 char *q;
2503 int mx, my;
2504 int enabled, mousehere;
2505 int row, col;
2507 width = menu->width;
2508 text = (struct glyph *) xmalloc ((width + 2) * sizeof (struct glyph));
2509 ScreenGetCursor (&row, &col);
2510 mouse_get_xy (&mx, &my);
2511 IT_update_begin (selected_frame);
2512 for (i = 0; i < menu->count; i++)
2514 IT_cursor_to (y + i, x);
2515 enabled
2516 = (!menu->submenu[i] && menu->panenumber[i]) || (menu->submenu[i]);
2517 mousehere = (y + i == my && x <= mx && mx < x + width + 2);
2518 face = faces[enabled + mousehere * 2];
2519 p = text;
2520 SET_CHAR_GLYPH (*p, ' ', face, 0);
2521 p++;
2522 for (j = 0, q = menu->text[i]; *q; j++)
2524 if (*q > 26)
2526 SET_CHAR_GLYPH (*p, *q++, face, 0);
2527 p++;
2529 else /* make '^x' */
2531 SET_CHAR_GLYPH (*p, '^', face, 0);
2532 p++;
2533 j++;
2534 SET_CHAR_GLYPH (*p, *q++ + 64, face, 0);
2535 p++;
2539 for (; j < width; j++, p++)
2540 SET_CHAR_GLYPH (*p, ' ', face, 0);
2542 SET_CHAR_GLYPH (*p, menu->submenu[i] ? 16 : ' ', face, 0);
2543 p++;
2544 IT_write_glyphs (text, width + 2);
2546 IT_update_end (selected_frame);
2547 IT_cursor_to (row, col);
2548 xfree (text);
2551 /* --------------------------- X Menu emulation ---------------------- */
2553 /* Report availability of menus. */
2556 have_menus_p ()
2558 return 1;
2561 /* Create a brand new menu structure. */
2563 XMenu *
2564 XMenuCreate (Display *foo1, Window foo2, char *foo3)
2566 return IT_menu_create ();
2569 /* Create a new pane and place it on the outer-most level. It is not
2570 clear that it should be placed out there, but I don't know what else
2571 to do. */
2574 XMenuAddPane (Display *foo, XMenu *menu, char *txt, int enable)
2576 int len;
2577 char *p;
2579 if (!enable)
2580 abort ();
2582 IT_menu_make_room (menu);
2583 menu->submenu[menu->count] = IT_menu_create ();
2584 menu->text[menu->count] = txt;
2585 menu->panenumber[menu->count] = ++menu->panecount;
2586 menu->count++;
2588 /* Adjust length for possible control characters (which will
2589 be written as ^x). */
2590 for (len = strlen (txt), p = txt; *p; p++)
2591 if (*p < 27)
2592 len++;
2594 if (len > menu->width)
2595 menu->width = len;
2597 return menu->panecount;
2600 /* Create a new item in a menu pane. */
2603 XMenuAddSelection (Display *bar, XMenu *menu, int pane,
2604 int foo, char *txt, int enable)
2606 int len;
2607 char *p;
2609 if (pane)
2610 if (!(menu = IT_menu_search_pane (menu, pane)))
2611 return XM_FAILURE;
2612 IT_menu_make_room (menu);
2613 menu->submenu[menu->count] = (XMenu *) 0;
2614 menu->text[menu->count] = txt;
2615 menu->panenumber[menu->count] = enable;
2616 menu->count++;
2618 /* Adjust length for possible control characters (which will
2619 be written as ^x). */
2620 for (len = strlen (txt), p = txt; *p; p++)
2621 if (*p < 27)
2622 len++;
2624 if (len > menu->width)
2625 menu->width = len;
2627 return XM_SUCCESS;
2630 /* Decide where the menu would be placed if requested at (X,Y). */
2632 void
2633 XMenuLocate (Display *foo0, XMenu *menu, int foo1, int foo2, int x, int y,
2634 int *ulx, int *uly, int *width, int *height)
2636 IT_menu_calc_size (menu, width, height);
2637 *ulx = x + 1;
2638 *uly = y;
2639 *width += 2;
2642 struct IT_menu_state
2644 void *screen_behind;
2645 XMenu *menu;
2646 int pane;
2647 int x, y;
2651 /* Display menu, wait for user's response, and return that response. */
2654 XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx,
2655 int x0, int y0, unsigned ButtonMask, char **txt)
2657 struct IT_menu_state *state;
2658 int statecount;
2659 int x, y, i, b;
2660 int screensize;
2661 int faces[4];
2662 Lisp_Object selectface;
2663 int leave, result, onepane;
2664 int title_faces[4]; /* face to display the menu title */
2665 int buffers_num_deleted = 0;
2667 /* Just in case we got here without a mouse present... */
2668 if (have_mouse <= 0)
2669 return XM_IA_SELECT;
2670 /* Don't allow non-positive x0 and y0, lest the menu will wrap
2671 around the display. */
2672 if (x0 <= 0)
2673 x0 = 1;
2674 if (y0 <= 0)
2675 y0 = 1;
2677 /* We will process all the mouse events directly, so we had
2678 better prevented dos_rawgetc from stealing them from us. */
2679 mouse_preempted++;
2681 state = alloca (menu->panecount * sizeof (struct IT_menu_state));
2682 screensize = screen_size * 2;
2683 faces[0]
2684 = lookup_derived_face (selected_frame, intern ("msdos-menu-passive-face"),
2685 CHARSET_ASCII, DEFAULT_FACE_ID);
2686 faces[1]
2687 = lookup_derived_face (selected_frame, intern ("msdos-menu-active-face"),
2688 CHARSET_ASCII, DEFAULT_FACE_ID);
2689 selectface = intern ("msdos-menu-select-face");
2690 faces[2] = lookup_derived_face (selected_frame, selectface,
2691 CHARSET_ASCII, faces[0]);
2692 faces[3] = lookup_derived_face (selected_frame, selectface,
2693 CHARSET_ASCII, faces[1]);
2695 /* Make sure the menu title is always displayed with
2696 `msdos-menu-active-face', no matter where the mouse pointer is. */
2697 for (i = 0; i < 4; i++)
2698 title_faces[i] = faces[3];
2700 statecount = 1;
2702 /* Don't let the title for the "Buffers" popup menu include a
2703 digit (which is ugly).
2705 This is a terrible kludge, but I think the "Buffers" case is
2706 the only one where the title includes a number, so it doesn't
2707 seem to be necessary to make this more general. */
2708 if (strncmp (menu->text[0], "Buffers 1", 9) == 0)
2710 menu->text[0][7] = '\0';
2711 buffers_num_deleted = 1;
2713 state[0].menu = menu;
2714 mouse_off ();
2715 ScreenRetrieve (state[0].screen_behind = xmalloc (screensize));
2717 /* Turn off the cursor. Otherwise it shows through the menu
2718 panes, which is ugly. */
2719 IT_display_cursor (0);
2721 IT_menu_display (menu, y0 - 1, x0 - 1, title_faces); /* display menu title */
2722 if (buffers_num_deleted)
2723 menu->text[0][7] = ' ';
2724 if ((onepane = menu->count == 1 && menu->submenu[0]))
2726 menu->width = menu->submenu[0]->width;
2727 state[0].menu = menu->submenu[0];
2729 else
2731 state[0].menu = menu;
2733 state[0].x = x0 - 1;
2734 state[0].y = y0;
2735 state[0].pane = onepane;
2737 mouse_last_x = -1; /* A hack that forces display. */
2738 leave = 0;
2739 while (!leave)
2741 if (!mouse_visible) mouse_on ();
2742 mouse_check_moved ();
2743 if (selected_frame->mouse_moved)
2745 selected_frame->mouse_moved = 0;
2746 result = XM_IA_SELECT;
2747 mouse_get_xy (&x, &y);
2748 for (i = 0; i < statecount; i++)
2749 if (state[i].x <= x && x < state[i].x + state[i].menu->width + 2)
2751 int dy = y - state[i].y;
2752 if (0 <= dy && dy < state[i].menu->count)
2754 if (!state[i].menu->submenu[dy])
2755 if (state[i].menu->panenumber[dy])
2756 result = XM_SUCCESS;
2757 else
2758 result = XM_IA_SELECT;
2759 *pane = state[i].pane - 1;
2760 *selidx = dy;
2761 /* We hit some part of a menu, so drop extra menus that
2762 have been opened. That does not include an open and
2763 active submenu. */
2764 if (i != statecount - 2
2765 || state[i].menu->submenu[dy] != state[i+1].menu)
2766 while (i != statecount - 1)
2768 statecount--;
2769 mouse_off ();
2770 ScreenUpdate (state[statecount].screen_behind);
2771 if (screen_virtual_segment)
2772 dosv_refresh_virtual_screen (0, screen_size);
2773 xfree (state[statecount].screen_behind);
2775 if (i == statecount - 1 && state[i].menu->submenu[dy])
2777 IT_menu_display (state[i].menu,
2778 state[i].y,
2779 state[i].x,
2780 faces);
2781 state[statecount].menu = state[i].menu->submenu[dy];
2782 state[statecount].pane = state[i].menu->panenumber[dy];
2783 mouse_off ();
2784 ScreenRetrieve (state[statecount].screen_behind
2785 = xmalloc (screensize));
2786 state[statecount].x
2787 = state[i].x + state[i].menu->width + 2;
2788 state[statecount].y = y;
2789 statecount++;
2793 IT_menu_display (state[statecount - 1].menu,
2794 state[statecount - 1].y,
2795 state[statecount - 1].x,
2796 faces);
2798 else
2799 /* We are busy-waiting for the mouse to move, so let's be nice
2800 to other Windows applications by releasing our time slice. */
2801 __dpmi_yield ();
2802 for (b = 0; b < mouse_button_count && !leave; b++)
2804 /* Only leave if user both pressed and released the mouse, and in
2805 that order. This avoids popping down the menu pane unless
2806 the user is really done with it. */
2807 if (mouse_pressed (b, &x, &y))
2809 while (mouse_button_depressed (b, &x, &y))
2810 __dpmi_yield ();
2811 leave = 1;
2813 (void) mouse_released (b, &x, &y);
2817 mouse_off ();
2818 ScreenUpdate (state[0].screen_behind);
2819 if (screen_virtual_segment)
2820 dosv_refresh_virtual_screen (0, screen_size);
2821 while (statecount--)
2822 xfree (state[statecount].screen_behind);
2823 IT_display_cursor (1); /* turn cursor back on */
2824 /* Clean up any mouse events that are waiting inside Emacs event queue.
2825 These events are likely to be generated before the menu was even
2826 displayed, probably because the user pressed and released the button
2827 (which invoked the menu) too quickly. If we don't remove these events,
2828 Emacs will process them after we return and surprise the user. */
2829 discard_mouse_events ();
2830 /* Allow mouse events generation by dos_rawgetc. */
2831 mouse_preempted--;
2832 return result;
2835 /* Dispose of a menu. */
2837 void
2838 XMenuDestroy (Display *foo, XMenu *menu)
2840 int i;
2841 if (menu->allocated)
2843 for (i = 0; i < menu->count; i++)
2844 if (menu->submenu[i])
2845 XMenuDestroy (foo, menu->submenu[i]);
2846 xfree (menu->text);
2847 xfree (menu->submenu);
2848 xfree (menu->panenumber);
2850 xfree (menu);
2854 x_pixel_width (struct frame *f)
2856 return FRAME_WIDTH (f);
2860 x_pixel_height (struct frame *f)
2862 return FRAME_HEIGHT (f);
2864 #endif /* !HAVE_X_WINDOWS */
2866 /* ----------------------- DOS / UNIX conversion --------------------- */
2868 void msdos_downcase_filename (unsigned char *);
2870 /* Destructively turn backslashes into slashes. */
2872 void
2873 dostounix_filename (p)
2874 register char *p;
2876 msdos_downcase_filename (p);
2878 while (*p)
2880 if (*p == '\\')
2881 *p = '/';
2882 p++;
2886 /* Destructively turn slashes into backslashes. */
2888 void
2889 unixtodos_filename (p)
2890 register char *p;
2892 if (p[1] == ':' && *p >= 'A' && *p <= 'Z')
2894 *p += 'a' - 'A';
2895 p += 2;
2898 while (*p)
2900 if (*p == '/')
2901 *p = '\\';
2902 p++;
2906 /* Get the default directory for a given drive. 0=def, 1=A, 2=B, ... */
2909 getdefdir (drive, dst)
2910 int drive;
2911 char *dst;
2913 char in_path[4], *p = in_path;
2914 int e = errno;
2916 /* Generate "X:." (when drive is X) or "." (when drive is 0). */
2917 if (drive != 0)
2919 *p++ = drive + 'A' - 1;
2920 *p++ = ':';
2923 *p++ = '.';
2924 *p = '\0';
2925 errno = 0;
2926 _fixpath (in_path, dst);
2927 /* _fixpath can set errno to ENOSYS on non-LFN systems because
2928 it queries the LFN support, so ignore that error. */
2929 if ((errno && errno != ENOSYS) || *dst == '\0')
2930 return 0;
2932 msdos_downcase_filename (dst);
2934 errno = e;
2935 return 1;
2938 /* Remove all CR's that are followed by a LF. */
2941 crlf_to_lf (n, buf)
2942 register int n;
2943 register unsigned char *buf;
2945 unsigned char *np = buf;
2946 unsigned char *startp = buf;
2947 unsigned char *endp = buf + n;
2949 if (n == 0)
2950 return n;
2951 while (buf < endp - 1)
2953 if (*buf == 0x0d)
2955 if (*(++buf) != 0x0a)
2956 *np++ = 0x0d;
2958 else
2959 *np++ = *buf++;
2961 if (buf < endp)
2962 *np++ = *buf++;
2963 return np - startp;
2966 #if defined(__DJGPP__) && __DJGPP__ == 2 && __DJGPP_MINOR__ == 0
2968 /* In DJGPP v2.0, library `write' can call `malloc', which might
2969 cause relocation of the buffer whose address we get in ADDR.
2970 Here is a version of `write' that avoids calling `malloc',
2971 to serve us until such time as the library is fixed.
2972 Actually, what we define here is called `__write', because
2973 `write' is a stub that just jmp's to `__write' (to be
2974 POSIXLY-correct with respect to the global name-space). */
2976 #include <io.h> /* for _write */
2977 #include <libc/dosio.h> /* for __file_handle_modes[] */
2979 static char xbuf[64 * 1024]; /* DOS cannot write more in one chunk */
2981 #define XBUF_END (xbuf + sizeof (xbuf) - 1)
2984 __write (int handle, const void *buffer, size_t count)
2986 if (count == 0)
2987 return 0;
2989 if(__file_handle_modes[handle] & O_BINARY)
2990 return _write (handle, buffer, count);
2991 else
2993 char *xbp = xbuf;
2994 const char *bp = buffer;
2995 int total_written = 0;
2996 int nmoved = 0, ncr = 0;
2998 while (count)
3000 /* The next test makes sure there's space for at least 2 more
3001 characters in xbuf[], so both CR and LF can be put there. */
3002 if (xbp < XBUF_END)
3004 if (*bp == '\n')
3006 ncr++;
3007 *xbp++ = '\r';
3009 *xbp++ = *bp++;
3010 nmoved++;
3011 count--;
3013 if (xbp >= XBUF_END || !count)
3015 size_t to_write = nmoved + ncr;
3016 int written = _write (handle, xbuf, to_write);
3018 if (written == -1)
3019 return -1;
3020 else
3021 total_written += nmoved; /* CRs aren't counted in ret value */
3023 /* If some, but not all were written (disk full?), return
3024 an estimate of the total written bytes not counting CRs. */
3025 if (written < to_write)
3026 return total_written - (to_write - written) * nmoved/to_write;
3028 nmoved = 0;
3029 ncr = 0;
3030 xbp = xbuf;
3033 return total_written;
3037 /* A low-level file-renaming function which works around Windows 95 bug.
3038 This is pulled directly out of DJGPP v2.01 library sources, and only
3039 used when you compile with DJGPP v2.0. */
3041 #include <io.h>
3043 int _rename(const char *old, const char *new)
3045 __dpmi_regs r;
3046 int olen = strlen(old) + 1;
3047 int i;
3048 int use_lfn = _USE_LFN;
3049 char tempfile[FILENAME_MAX];
3050 const char *orig = old;
3051 int lfn_fd = -1;
3053 r.x.dx = __tb_offset;
3054 r.x.di = __tb_offset + olen;
3055 r.x.ds = r.x.es = __tb_segment;
3057 if (use_lfn)
3059 /* Windows 95 bug: for some filenames, when you rename
3060 file -> file~ (as in Emacs, to leave a backup), the
3061 short 8+3 alias doesn't change, which effectively
3062 makes OLD and NEW the same file. We must rename
3063 through a temporary file to work around this. */
3065 char *pbase = 0, *p;
3066 static char try_char[] = "abcdefghijklmnopqrstuvwxyz012345789";
3067 int idx = sizeof(try_char) - 1;
3069 /* Generate a temporary name. Can't use `tmpnam', since $TMPDIR
3070 might point to another drive, which will fail the DOS call. */
3071 strcpy(tempfile, old);
3072 for (p = tempfile; *p; p++) /* ensure temporary is on the same drive */
3073 if (*p == '/' || *p == '\\' || *p == ':')
3074 pbase = p;
3075 if (pbase)
3076 pbase++;
3077 else
3078 pbase = tempfile;
3079 strcpy(pbase, "X$$djren$$.$$temp$$");
3083 if (idx <= 0)
3084 return -1;
3085 *pbase = try_char[--idx];
3086 } while (_chmod(tempfile, 0) != -1);
3088 r.x.ax = 0x7156;
3089 _put_path2(tempfile, olen);
3090 _put_path(old);
3091 __dpmi_int(0x21, &r);
3092 if (r.x.flags & 1)
3094 errno = __doserr_to_errno(r.x.ax);
3095 return -1;
3098 /* Now create a file with the original name. This will
3099 ensure that NEW will always have a 8+3 alias
3100 different from that of OLD. (Seems to be required
3101 when NameNumericTail in the Registry is set to 0.) */
3102 lfn_fd = _creat(old, 0);
3104 olen = strlen(tempfile) + 1;
3105 old = tempfile;
3106 r.x.di = __tb_offset + olen;
3109 for (i=0; i<2; i++)
3111 if(use_lfn)
3112 r.x.ax = 0x7156;
3113 else
3114 r.h.ah = 0x56;
3115 _put_path2(new, olen);
3116 _put_path(old);
3117 __dpmi_int(0x21, &r);
3118 if(r.x.flags & 1)
3120 if (r.x.ax == 5 && i == 0) /* access denied */
3121 remove(new); /* and try again */
3122 else
3124 errno = __doserr_to_errno(r.x.ax);
3126 /* Restore to original name if we renamed it to temporary. */
3127 if (use_lfn)
3129 if (lfn_fd != -1)
3131 _close (lfn_fd);
3132 remove (orig);
3134 _put_path2(orig, olen);
3135 _put_path(tempfile);
3136 r.x.ax = 0x7156;
3137 __dpmi_int(0x21, &r);
3139 return -1;
3142 else
3143 break;
3146 /* Success. Delete the file possibly created to work
3147 around the Windows 95 bug. */
3148 if (lfn_fd != -1)
3149 return (_close (lfn_fd) == 0) ? remove (orig) : -1;
3150 return 0;
3153 #endif /* __DJGPP__ == 2 && __DJGPP_MINOR__ == 0 */
3155 DEFUN ("msdos-long-file-names", Fmsdos_long_file_names, Smsdos_long_file_names,
3156 0, 0, 0,
3157 "Return non-nil if long file names are supported on MSDOS.")
3160 return (_USE_LFN ? Qt : Qnil);
3163 /* Convert alphabetic characters in a filename to lower-case. */
3165 void
3166 msdos_downcase_filename (p)
3167 register unsigned char *p;
3169 /* Always lower-case drive letters a-z, even if the filesystem
3170 preserves case in filenames.
3171 This is so MSDOS filenames could be compared by string comparison
3172 functions that are case-sensitive. Even case-preserving filesystems
3173 do not distinguish case in drive letters. */
3174 if (p[1] == ':' && *p >= 'A' && *p <= 'Z')
3176 *p += 'a' - 'A';
3177 p += 2;
3180 /* Under LFN we expect to get pathnames in their true case. */
3181 if (NILP (Fmsdos_long_file_names ()))
3182 for ( ; *p; p++)
3183 if (*p >= 'A' && *p <= 'Z')
3184 *p += 'a' - 'A';
3187 DEFUN ("msdos-downcase-filename", Fmsdos_downcase_filename, Smsdos_downcase_filename,
3188 1, 1, 0,
3189 "Convert alphabetic characters in FILENAME to lower case and return that.\n\
3190 When long filenames are supported, doesn't change FILENAME.\n\
3191 If FILENAME is not a string, returns nil.\n\
3192 The argument object is never altered--the value is a copy.")
3193 (filename)
3194 Lisp_Object filename;
3196 Lisp_Object tem;
3198 if (! STRINGP (filename))
3199 return Qnil;
3201 tem = Fcopy_sequence (filename);
3202 msdos_downcase_filename (XSTRING (tem)->data);
3203 return tem;
3206 /* The Emacs root directory as determined by init_environment. */
3208 static char emacsroot[MAXPATHLEN];
3210 char *
3211 rootrelativepath (rel)
3212 char *rel;
3214 static char result[MAXPATHLEN + 10];
3216 strcpy (result, emacsroot);
3217 strcat (result, "/");
3218 strcat (result, rel);
3219 return result;
3222 /* Define a lot of environment variables if not already defined. Don't
3223 remove anything unless you know what you're doing -- lots of code will
3224 break if one or more of these are missing. */
3226 void
3227 init_environment (argc, argv, skip_args)
3228 int argc;
3229 char **argv;
3230 int skip_args;
3232 char *s, *t, *root;
3233 int len;
3234 static const char * const tempdirs[] = {
3235 "$TMPDIR", "$TEMP", "$TMP", "c:/"
3237 int i;
3238 const int imax = sizeof (tempdirs) / sizeof (tempdirs[0]);
3240 /* Make sure they have a usable $TMPDIR. Many Emacs functions use
3241 temporary files and assume "/tmp" if $TMPDIR is unset, which
3242 will break on DOS/Windows. Refuse to work if we cannot find
3243 a directory, not even "c:/", usable for that purpose. */
3244 for (i = 0; i < imax ; i++)
3246 const char *tmp = tempdirs[i];
3248 if (*tmp == '$')
3249 tmp = getenv (tmp + 1);
3250 /* Note that `access' can lie to us if the directory resides on a
3251 read-only filesystem, like CD-ROM or a write-protected floppy.
3252 The only way to be really sure is to actually create a file and
3253 see if it succeeds. But I think that's too much to ask. */
3254 if (tmp && access (tmp, D_OK) == 0)
3256 setenv ("TMPDIR", tmp, 1);
3257 break;
3260 if (i >= imax)
3261 cmd_error_internal
3262 (Fcons (Qerror,
3263 Fcons (build_string ("no usable temporary directories found!!"),
3264 Qnil)),
3265 "While setting TMPDIR: ");
3267 /* Note the startup time, so we know not to clear the screen if we
3268 exit immediately; see IT_reset_terminal_modes.
3269 (Yes, I know `clock' returns zero the first time it's called, but
3270 I do this anyway, in case some wiseguy changes that at some point.) */
3271 startup_time = clock ();
3273 /* Find our root from argv[0]. Assuming argv[0] is, say,
3274 "c:/emacs/bin/emacs.exe" our root will be "c:/emacs". */
3275 root = alloca (MAXPATHLEN + 20);
3276 _fixpath (argv[0], root);
3277 msdos_downcase_filename (root);
3278 len = strlen (root);
3279 while (len > 0 && root[len] != '/' && root[len] != ':')
3280 len--;
3281 root[len] = '\0';
3282 if (len > 4
3283 && (strcmp (root + len - 4, "/bin") == 0
3284 || strcmp (root + len - 4, "/src") == 0)) /* under a debugger */
3285 root[len - 4] = '\0';
3286 else
3287 strcpy (root, "c:/emacs"); /* let's be defensive */
3288 len = strlen (root);
3289 strcpy (emacsroot, root);
3291 /* We default HOME to our root. */
3292 setenv ("HOME", root, 0);
3294 /* We default EMACSPATH to root + "/bin". */
3295 strcpy (root + len, "/bin");
3296 setenv ("EMACSPATH", root, 0);
3298 /* I don't expect anybody to ever use other terminals so the internal
3299 terminal is the default. */
3300 setenv ("TERM", "internal", 0);
3302 #ifdef HAVE_X_WINDOWS
3303 /* Emacs expects DISPLAY to be set. */
3304 setenv ("DISPLAY", "unix:0.0", 0);
3305 #endif
3307 /* SHELL is a bit tricky -- COMSPEC is the closest we come, but we must
3308 downcase it and mirror the backslashes. */
3309 s = getenv ("COMSPEC");
3310 if (!s) s = "c:/command.com";
3311 t = alloca (strlen (s) + 1);
3312 strcpy (t, s);
3313 dostounix_filename (t);
3314 setenv ("SHELL", t, 0);
3316 /* PATH is also downcased and backslashes mirrored. */
3317 s = getenv ("PATH");
3318 if (!s) s = "";
3319 t = alloca (strlen (s) + 3);
3320 /* Current directory is always considered part of MsDos's path but it is
3321 not normally mentioned. Now it is. */
3322 strcat (strcpy (t, ".;"), s);
3323 dostounix_filename (t); /* Not a single file name, but this should work. */
3324 setenv ("PATH", t, 1);
3326 /* In some sense all dos users have root privileges, so... */
3327 setenv ("USER", "root", 0);
3328 setenv ("NAME", getenv ("USER"), 0);
3330 /* Time zone determined from country code. To make this possible, the
3331 country code may not span more than one time zone. In other words,
3332 in the USA, you lose. */
3333 if (!getenv ("TZ"))
3334 switch (dos_country_code)
3336 case 31: /* Belgium */
3337 case 32: /* The Netherlands */
3338 case 33: /* France */
3339 case 34: /* Spain */
3340 case 36: /* Hungary */
3341 case 38: /* Yugoslavia (or what's left of it?) */
3342 case 39: /* Italy */
3343 case 41: /* Switzerland */
3344 case 42: /* Tjekia */
3345 case 45: /* Denmark */
3346 case 46: /* Sweden */
3347 case 47: /* Norway */
3348 case 48: /* Poland */
3349 case 49: /* Germany */
3350 /* Daylight saving from last Sunday in March to last Sunday in
3351 September, both at 2AM. */
3352 setenv ("TZ", "MET-01METDST-02,M3.5.0/02:00,M9.5.0/02:00", 0);
3353 break;
3354 case 44: /* United Kingdom */
3355 case 351: /* Portugal */
3356 case 354: /* Iceland */
3357 setenv ("TZ", "GMT+00", 0);
3358 break;
3359 case 81: /* Japan */
3360 case 82: /* Korea */
3361 setenv ("TZ", "JST-09", 0);
3362 break;
3363 case 90: /* Turkey */
3364 case 358: /* Finland */
3365 setenv ("TZ", "EET-02", 0);
3366 break;
3367 case 972: /* Israel */
3368 /* This is an approximation. (For exact rules, use the
3369 `zoneinfo/israel' file which comes with DJGPP, but you need
3370 to install it in `/usr/share/zoneinfo/' directory first.) */
3371 setenv ("TZ", "IST-02IDT-03,M4.1.6/00:00,M9.5.6/01:00", 0);
3372 break;
3374 tzset ();
3379 static int break_stat; /* BREAK check mode status. */
3380 static int stdin_stat; /* stdin IOCTL status. */
3382 #if __DJGPP__ < 2
3384 /* These must be global. */
3385 static _go32_dpmi_seginfo ctrl_break_vector;
3386 static _go32_dpmi_registers ctrl_break_regs;
3387 static int ctrlbreakinstalled = 0;
3389 /* Interrupt level detection of Ctrl-Break. Don't do anything fancy here! */
3391 void
3392 ctrl_break_func (regs)
3393 _go32_dpmi_registers *regs;
3395 Vquit_flag = Qt;
3398 void
3399 install_ctrl_break_check ()
3401 if (!ctrlbreakinstalled)
3403 /* Don't press Ctrl-Break if you don't have either DPMI or Emacs
3404 was compiler with Djgpp 1.11 maintenance level 5 or later! */
3405 ctrlbreakinstalled = 1;
3406 ctrl_break_vector.pm_offset = (int) ctrl_break_func;
3407 _go32_dpmi_allocate_real_mode_callback_iret (&ctrl_break_vector,
3408 &ctrl_break_regs);
3409 _go32_dpmi_set_real_mode_interrupt_vector (0x1b, &ctrl_break_vector);
3413 #endif /* __DJGPP__ < 2 */
3415 /* Turn off Dos' Ctrl-C checking and inhibit interpretation of
3416 control chars by DOS. Determine the keyboard type. */
3419 dos_ttraw ()
3421 union REGS inregs, outregs;
3422 static int first_time = 1;
3424 break_stat = getcbrk ();
3425 setcbrk (0);
3426 #if __DJGPP__ < 2
3427 install_ctrl_break_check ();
3428 #endif
3430 if (first_time)
3432 inregs.h.ah = 0xc0;
3433 int86 (0x15, &inregs, &outregs);
3434 extended_kbd = (!outregs.x.cflag) && (outregs.h.ah == 0);
3436 have_mouse = 0;
3438 if (internal_terminal
3439 #ifdef HAVE_X_WINDOWS
3440 && inhibit_window_system
3441 #endif
3444 inregs.x.ax = 0x0021;
3445 int86 (0x33, &inregs, &outregs);
3446 have_mouse = (outregs.x.ax & 0xffff) == 0xffff;
3447 if (!have_mouse)
3449 /* Reportedly, the above doesn't work for some mouse drivers. There
3450 is an additional detection method that should work, but might be
3451 a little slower. Use that as an alternative. */
3452 inregs.x.ax = 0x0000;
3453 int86 (0x33, &inregs, &outregs);
3454 have_mouse = (outregs.x.ax & 0xffff) == 0xffff;
3457 if (have_mouse)
3459 have_mouse = 1; /* enable mouse */
3460 mouse_visible = 0;
3462 if (outregs.x.bx == 3)
3464 mouse_button_count = 3;
3465 mouse_button_translate[0] = 0; /* Left */
3466 mouse_button_translate[1] = 2; /* Middle */
3467 mouse_button_translate[2] = 1; /* Right */
3469 else
3471 mouse_button_count = 2;
3472 mouse_button_translate[0] = 0;
3473 mouse_button_translate[1] = 1;
3475 mouse_position_hook = &mouse_get_pos;
3476 mouse_init ();
3480 first_time = 0;
3482 #if __DJGPP__ >= 2
3484 stdin_stat = setmode (fileno (stdin), O_BINARY);
3485 return (stdin_stat != -1);
3487 else
3488 return (setmode (fileno (stdin), O_BINARY) != -1);
3490 #else /* __DJGPP__ < 2 */
3494 /* I think it is wrong to overwrite `stdin_stat' every time
3495 but the first one this function is called, but I don't
3496 want to change the way it used to work in v1.x.--EZ */
3498 inregs.x.ax = 0x4400; /* Get IOCTL status. */
3499 inregs.x.bx = 0x00; /* 0 = stdin. */
3500 intdos (&inregs, &outregs);
3501 stdin_stat = outregs.h.dl;
3503 inregs.x.dx = stdin_stat | 0x0020; /* raw mode */
3504 inregs.x.ax = 0x4401; /* Set IOCTL status */
3505 intdos (&inregs, &outregs);
3506 return !outregs.x.cflag;
3508 #endif /* __DJGPP__ < 2 */
3511 /* Restore status of standard input and Ctrl-C checking. */
3514 dos_ttcooked ()
3516 union REGS inregs, outregs;
3518 setcbrk (break_stat);
3519 mouse_off ();
3521 #if __DJGPP__ >= 2
3523 return (setmode (fileno (stdin), stdin_stat) != -1);
3525 #else /* not __DJGPP__ >= 2 */
3527 inregs.x.ax = 0x4401; /* Set IOCTL status. */
3528 inregs.x.bx = 0x00; /* 0 = stdin. */
3529 inregs.x.dx = stdin_stat;
3530 intdos (&inregs, &outregs);
3531 return !outregs.x.cflag;
3533 #endif /* not __DJGPP__ >= 2 */
3537 /* Run command as specified by ARGV in directory DIR.
3538 The command is run with input from TEMPIN, output to
3539 file TEMPOUT and stderr to TEMPERR. */
3542 run_msdos_command (argv, working_dir, tempin, tempout, temperr, envv)
3543 unsigned char **argv;
3544 const char *working_dir;
3545 int tempin, tempout, temperr;
3546 char **envv;
3548 char *saveargv1, *saveargv2, *lowcase_argv0, *pa, *pl;
3549 char oldwd[MAXPATHLEN + 1]; /* Fixed size is safe on MSDOS. */
3550 int msshell, result = -1;
3551 int inbak, outbak, errbak;
3552 int x, y;
3553 Lisp_Object cmd;
3555 /* Get current directory as MSDOS cwd is not per-process. */
3556 getwd (oldwd);
3558 /* If argv[0] is the shell, it might come in any lettercase.
3559 Since `Fmember' is case-sensitive, we need to downcase
3560 argv[0], even if we are on case-preserving filesystems. */
3561 lowcase_argv0 = alloca (strlen (argv[0]) + 1);
3562 for (pa = argv[0], pl = lowcase_argv0; *pa; pl++)
3564 *pl = *pa++;
3565 if (*pl >= 'A' && *pl <= 'Z')
3566 *pl += 'a' - 'A';
3568 *pl = '\0';
3570 cmd = Ffile_name_nondirectory (build_string (lowcase_argv0));
3571 msshell = !NILP (Fmember (cmd, Fsymbol_value (intern ("msdos-shells"))))
3572 && !strcmp ("-c", argv[1]);
3573 if (msshell)
3575 saveargv1 = argv[1];
3576 saveargv2 = argv[2];
3577 argv[1] = "/c";
3578 if (argv[2])
3580 char *p = alloca (strlen (argv[2]) + 1);
3582 strcpy (argv[2] = p, saveargv2);
3583 while (*p && isspace (*p))
3584 p++;
3585 while (*p && !isspace (*p))
3586 if (*p == '/')
3587 *p++ = '\\';
3588 else
3589 p++;
3593 chdir (working_dir);
3594 inbak = dup (0);
3595 outbak = dup (1);
3596 errbak = dup (2);
3597 if (inbak < 0 || outbak < 0 || errbak < 0)
3598 goto done; /* Allocation might fail due to lack of descriptors. */
3600 if (have_mouse > 0)
3601 mouse_get_xy (&x, &y);
3603 dos_ttcooked (); /* do it here while 0 = stdin */
3605 dup2 (tempin, 0);
3606 dup2 (tempout, 1);
3607 dup2 (temperr, 2);
3609 #if __DJGPP__ > 1
3611 if (msshell && !argv[3])
3613 /* MS-DOS native shells are too restrictive. For starters, they
3614 cannot grok commands longer than 126 characters. In DJGPP v2
3615 and later, `system' is much smarter, so we'll call it instead. */
3617 const char *cmnd;
3619 /* A shell gets a single argument--its full command
3620 line--whose original was saved in `saveargv2'. */
3622 /* Don't let them pass empty command lines to `system', since
3623 with some shells it will try to invoke an interactive shell,
3624 which will hang Emacs. */
3625 for (cmnd = saveargv2; *cmnd && isspace (*cmnd); cmnd++)
3627 if (*cmnd)
3629 extern char **environ;
3630 int save_system_flags = __system_flags;
3632 /* Request the most powerful version of `system'. We need
3633 all the help we can get to avoid calling stock DOS shells. */
3634 __system_flags = (__system_redirect
3635 | __system_use_shell
3636 | __system_allow_multiple_cmds
3637 | __system_allow_long_cmds
3638 | __system_handle_null_commands
3639 | __system_emulate_chdir);
3641 environ = envv;
3642 result = system (cmnd);
3643 __system_flags = save_system_flags;
3645 else
3646 result = 0; /* emulate Unixy shell behavior with empty cmd line */
3648 else
3650 #endif /* __DJGPP__ > 1 */
3652 result = spawnve (P_WAIT, argv[0], argv, envv);
3654 dup2 (inbak, 0);
3655 dup2 (outbak, 1);
3656 dup2 (errbak, 2);
3657 close (inbak);
3658 close (outbak);
3659 close (errbak);
3661 dos_ttraw ();
3662 if (have_mouse > 0)
3664 mouse_init ();
3665 mouse_moveto (x, y);
3668 /* Some programs might change the meaning of the highest bit of the
3669 text attribute byte, so we get blinking characters instead of the
3670 bright background colors. Restore that. */
3671 bright_bg ();
3673 done:
3674 chdir (oldwd);
3675 if (msshell)
3677 argv[1] = saveargv1;
3678 argv[2] = saveargv2;
3680 return result;
3683 croak (badfunc)
3684 char *badfunc;
3686 fprintf (stderr, "%s not yet implemented\r\n", badfunc);
3687 reset_sys_modes ();
3688 exit (1);
3691 #if __DJGPP__ < 2
3693 /* ------------------------- Compatibility functions -------------------
3694 * gethostname
3695 * gettimeofday
3698 /* Hostnames for a pc are not really funny,
3699 but they are used in change log so we emulate the best we can. */
3701 gethostname (p, size)
3702 char *p;
3703 int size;
3705 char *q = egetenv ("HOSTNAME");
3707 if (!q) q = "pc";
3708 strcpy (p, q);
3709 return 0;
3712 /* When time zones are set from Ms-Dos too many C-libraries are playing
3713 tricks with time values. We solve this by defining our own version
3714 of `gettimeofday' bypassing GO32. Our version needs to be initialized
3715 once and after each call to `tzset' with TZ changed. That is
3716 accomplished by aliasing tzset to init_gettimeofday. */
3718 static struct tm time_rec;
3721 gettimeofday (struct timeval *tp, struct timezone *tzp)
3723 if (tp)
3725 struct time t;
3726 struct tm tm;
3728 gettime (&t);
3729 if (t.ti_hour < time_rec.tm_hour) /* midnight wrap */
3731 struct date d;
3732 getdate (&d);
3733 time_rec.tm_year = d.da_year - 1900;
3734 time_rec.tm_mon = d.da_mon - 1;
3735 time_rec.tm_mday = d.da_day;
3738 time_rec.tm_hour = t.ti_hour;
3739 time_rec.tm_min = t.ti_min;
3740 time_rec.tm_sec = t.ti_sec;
3742 tm = time_rec;
3743 tm.tm_gmtoff = dos_timezone_offset;
3745 tp->tv_sec = mktime (&tm); /* may modify tm */
3746 tp->tv_usec = t.ti_hund * (1000000 / 100);
3748 /* Ignore tzp; it's obsolescent. */
3749 return 0;
3752 #endif /* __DJGPP__ < 2 */
3755 * A list of unimplemented functions that we silently ignore.
3758 #if __DJGPP__ < 2
3759 unsigned alarm (s) unsigned s; {}
3760 fork () { return 0; }
3761 int kill (x, y) int x, y; { return -1; }
3762 nice (p) int p; {}
3763 void volatile pause () {}
3764 sigsetmask (x) int x; { return 0; }
3765 sigblock (mask) int mask; { return 0; }
3766 #endif
3768 void request_sigio (void) {}
3769 setpgrp () {return 0; }
3770 setpriority (x,y,z) int x,y,z; { return 0; }
3771 void unrequest_sigio (void) {}
3773 #if __DJGPP__ > 1
3775 #ifdef POSIX_SIGNALS
3777 /* Augment DJGPP library POSIX signal functions. This is needed
3778 as of DJGPP v2.01, but might be in the library in later releases. */
3780 #include <libc/bss.h>
3782 /* A counter to know when to re-initialize the static sets. */
3783 static int sigprocmask_count = -1;
3785 /* Which signals are currently blocked (initially none). */
3786 static sigset_t current_mask;
3788 /* Which signals are pending (initially none). */
3789 static sigset_t pending_signals;
3791 /* Previous handlers to restore when the blocked signals are unblocked. */
3792 typedef void (*sighandler_t)(int);
3793 static sighandler_t prev_handlers[320];
3795 /* A signal handler which just records that a signal occured
3796 (it will be raised later, if and when the signal is unblocked). */
3797 static void
3798 sig_suspender (signo)
3799 int signo;
3801 sigaddset (&pending_signals, signo);
3805 sigprocmask (how, new_set, old_set)
3806 int how;
3807 const sigset_t *new_set;
3808 sigset_t *old_set;
3810 int signo;
3811 sigset_t new_mask;
3813 /* If called for the first time, initialize. */
3814 if (sigprocmask_count != __bss_count)
3816 sigprocmask_count = __bss_count;
3817 sigemptyset (&pending_signals);
3818 sigemptyset (&current_mask);
3819 for (signo = 0; signo < 320; signo++)
3820 prev_handlers[signo] = SIG_ERR;
3823 if (old_set)
3824 *old_set = current_mask;
3826 if (new_set == 0)
3827 return 0;
3829 if (how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK)
3831 errno = EINVAL;
3832 return -1;
3835 sigemptyset (&new_mask);
3837 /* DJGPP supports upto 320 signals. */
3838 for (signo = 0; signo < 320; signo++)
3840 if (sigismember (&current_mask, signo))
3841 sigaddset (&new_mask, signo);
3842 else if (sigismember (new_set, signo) && how != SIG_UNBLOCK)
3844 sigaddset (&new_mask, signo);
3846 /* SIGKILL is silently ignored, as on other platforms. */
3847 if (signo != SIGKILL && prev_handlers[signo] == SIG_ERR)
3848 prev_handlers[signo] = signal (signo, sig_suspender);
3850 if (( how == SIG_UNBLOCK
3851 && sigismember (&new_mask, signo)
3852 && sigismember (new_set, signo))
3853 || (how == SIG_SETMASK
3854 && sigismember (&new_mask, signo)
3855 && !sigismember (new_set, signo)))
3857 sigdelset (&new_mask, signo);
3858 if (prev_handlers[signo] != SIG_ERR)
3860 signal (signo, prev_handlers[signo]);
3861 prev_handlers[signo] = SIG_ERR;
3863 if (sigismember (&pending_signals, signo))
3865 sigdelset (&pending_signals, signo);
3866 raise (signo);
3870 current_mask = new_mask;
3871 return 0;
3874 #else /* not POSIX_SIGNALS */
3876 sigsetmask (x) int x; { return 0; }
3877 sigblock (mask) int mask; { return 0; }
3879 #endif /* not POSIX_SIGNALS */
3880 #endif /* __DJGPP__ > 1 */
3882 #ifndef HAVE_SELECT
3883 #include "sysselect.h"
3885 #ifndef EMACS_TIME_ZERO_OR_NEG_P
3886 #define EMACS_TIME_ZERO_OR_NEG_P(time) \
3887 ((long)(time).tv_sec < 0 \
3888 || ((time).tv_sec == 0 \
3889 && (long)(time).tv_usec <= 0))
3890 #endif
3892 /* This yields the rest of the current time slice to the task manager.
3893 It should be called by any code which knows that it has nothing
3894 useful to do except idle.
3896 I don't use __dpmi_yield here, since versions of library before 2.02
3897 called Int 2Fh/AX=1680h there in a way that would wedge the DOS box
3898 on some versions of Windows 9X. */
3900 void
3901 dos_yield_time_slice (void)
3903 _go32_dpmi_registers r;
3905 r.x.ax = 0x1680;
3906 r.x.ss = r.x.sp = r.x.flags = 0;
3907 _go32_dpmi_simulate_int (0x2f, &r);
3908 if (r.h.al == 0x80)
3909 errno = ENOSYS;
3912 /* Only event queue is checked. */
3913 /* We don't have to call timer_check here
3914 because wait_reading_process_input takes care of that. */
3916 sys_select (nfds, rfds, wfds, efds, timeout)
3917 int nfds;
3918 SELECT_TYPE *rfds, *wfds, *efds;
3919 EMACS_TIME *timeout;
3921 int check_input;
3922 struct time t;
3924 check_input = 0;
3925 if (rfds)
3927 check_input = FD_ISSET (0, rfds);
3928 FD_ZERO (rfds);
3930 if (wfds)
3931 FD_ZERO (wfds);
3932 if (efds)
3933 FD_ZERO (efds);
3935 if (nfds != 1)
3936 abort ();
3938 /* If we are looking only for the terminal, with no timeout,
3939 just read it and wait -- that's more efficient. */
3940 if (!timeout)
3942 while (!detect_input_pending ())
3944 dos_yield_time_slice ();
3947 else
3949 EMACS_TIME clnow, cllast, cldiff;
3951 gettime (&t);
3952 EMACS_SET_SECS_USECS (cllast, t.ti_sec, t.ti_hund * 10000L);
3954 while (!check_input || !detect_input_pending ())
3956 gettime (&t);
3957 EMACS_SET_SECS_USECS (clnow, t.ti_sec, t.ti_hund * 10000L);
3958 EMACS_SUB_TIME (cldiff, clnow, cllast);
3960 /* When seconds wrap around, we assume that no more than
3961 1 minute passed since last `gettime'. */
3962 if (EMACS_TIME_NEG_P (cldiff))
3963 EMACS_SET_SECS (cldiff, EMACS_SECS (cldiff) + 60);
3964 EMACS_SUB_TIME (*timeout, *timeout, cldiff);
3966 /* Stop when timeout value crosses zero. */
3967 if (EMACS_TIME_ZERO_OR_NEG_P (*timeout))
3968 return 0;
3969 cllast = clnow;
3970 dos_yield_time_slice ();
3974 FD_SET (0, rfds);
3975 return 1;
3977 #endif
3980 * Define overlaid functions:
3982 * chdir -> sys_chdir
3983 * tzset -> init_gettimeofday
3984 * abort -> dos_abort
3987 #ifdef chdir
3988 #undef chdir
3989 extern int chdir ();
3992 sys_chdir (path)
3993 const char* path;
3995 int len = strlen (path);
3996 char *tmp = (char *)path;
3998 if (*tmp && tmp[1] == ':')
4000 if (getdisk () != tolower (tmp[0]) - 'a')
4001 setdisk (tolower (tmp[0]) - 'a');
4002 tmp += 2; /* strip drive: KFS 1995-07-06 */
4003 len -= 2;
4006 if (len > 1 && (tmp[len - 1] == '/'))
4008 char *tmp1 = (char *) alloca (len + 1);
4009 strcpy (tmp1, tmp);
4010 tmp1[len - 1] = 0;
4011 tmp = tmp1;
4013 return chdir (tmp);
4015 #endif
4017 #ifdef tzset
4018 #undef tzset
4019 extern void tzset (void);
4021 void
4022 init_gettimeofday ()
4024 time_t ltm, gtm;
4025 struct tm *lstm;
4027 tzset ();
4028 ltm = gtm = time (NULL);
4029 ltm = mktime (lstm = localtime (&ltm));
4030 gtm = mktime (gmtime (&gtm));
4031 time_rec.tm_hour = 99; /* force gettimeofday to get date */
4032 time_rec.tm_isdst = lstm->tm_isdst;
4033 dos_timezone_offset = time_rec.tm_gmtoff = (int)(gtm - ltm) / 60;
4035 #endif
4037 #ifdef abort
4038 #undef abort
4039 void
4040 dos_abort (file, line)
4041 char *file;
4042 int line;
4044 char buffer1[200], buffer2[400];
4045 int i, j;
4047 sprintf (buffer1, "<EMACS FATAL ERROR IN %s LINE %d>", file, line);
4048 for (i = j = 0; buffer1[i]; i++) {
4049 buffer2[j++] = buffer1[i];
4050 buffer2[j++] = 0x70;
4052 dosmemput (buffer2, j, (int)ScreenPrimary);
4053 ScreenSetCursor (2, 0);
4054 abort ();
4056 #else
4057 void
4058 abort ()
4060 dos_ttcooked ();
4061 ScreenSetCursor (10, 0);
4062 cputs ("\r\n\nEmacs aborted!\r\n");
4063 #if __DJGPP__ > 1
4064 #if __DJGPP__ == 2 && __DJGPP_MINOR__ < 2
4065 if (screen_virtual_segment)
4066 dosv_refresh_virtual_screen (2 * 10 * screen_size_X, 4 * screen_size_X);
4067 /* Generate traceback, so we could tell whodunit. */
4068 signal (SIGINT, SIG_DFL);
4069 __asm__ __volatile__ ("movb $0x1b,%al;call ___djgpp_hw_exception");
4070 #else /* __DJGPP_MINOR__ >= 2 */
4071 raise (SIGABRT);
4072 #endif /* __DJGPP_MINOR__ >= 2 */
4073 #endif
4074 exit (2);
4076 #endif
4078 /* The following two are required so that customization feature
4079 won't complain about unbound variables. */
4080 #ifndef HAVE_X_WINDOWS
4081 /* Search path for bitmap files (xfns.c). */
4082 Lisp_Object Vx_bitmap_file_path;
4083 #endif
4084 #ifndef subprocesses
4085 /* Nonzero means delete a process right away if it exits (process.c). */
4086 static int delete_exited_processes;
4087 #endif
4089 syms_of_msdos ()
4091 recent_doskeys = Fmake_vector (make_number (NUM_RECENT_DOSKEYS), Qnil);
4092 staticpro (&recent_doskeys);
4093 #ifndef HAVE_X_WINDOWS
4094 DEFVAR_LISP ("x-bitmap-file-path", &Vx_bitmap_file_path,
4095 "List of directories to search for bitmap files for X.");
4096 Vx_bitmap_file_path = decode_env_path ((char *) 0, ".");
4098 /* The following three are from xfns.c: */
4099 Qbackground_color = intern ("background-color");
4100 staticpro (&Qbackground_color);
4101 Qforeground_color = intern ("foreground-color");
4102 staticpro (&Qforeground_color);
4104 DEFVAR_LISP ("dos-unsupported-char-glyph", &Vdos_unsupported_char_glyph,
4105 "*Glyph to display instead of chars not supported by current codepage.\n\
4107 This variable is used only by MSDOS terminals.");
4108 Vdos_unsupported_char_glyph = '\177';
4109 #endif
4110 #ifndef subprocesses
4111 DEFVAR_BOOL ("delete-exited-processes", &delete_exited_processes,
4112 "*Non-nil means delete processes immediately when they exit.\n\
4113 nil means don't delete them until `list-processes' is run.");
4114 delete_exited_processes = 0;
4115 #endif
4117 defsubr (&Srecent_doskeys);
4118 defsubr (&Smsdos_long_file_names);
4119 defsubr (&Smsdos_downcase_filename);
4122 #endif /* MSDOS */