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