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