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