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