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