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