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