* window.h (struct window): Replace hchild, vchild and buffer slots
[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_x (void)
1924 {
1925 if (! FRAME_MSDOS_P (SELECTED_FRAME ()))
1926 error ("Not running under a window system");
1927 }
1928
1929 #endif
1930
1931 \f
1932 /* ----------------------- Keyboard control ----------------------
1933 *
1934 * Keymaps reflect the following keyboard layout:
1935 *
1936 * 0 1 2 3 4 5 6 7 8 9 10 11 12 BS
1937 * TAB 15 16 17 18 19 20 21 22 23 24 25 26 (41)
1938 * CLOK 30 31 32 33 34 35 36 37 38 39 40 (41) RET
1939 * SH () 45 46 47 48 49 50 51 52 53 54 SHIFT
1940 * SPACE
1941 */
1942
1943 #define Ignore 0x0000
1944 #define Normal 0x0000 /* normal key - alt changes scan-code */
1945 #define FctKey 0x1000 /* func key if c == 0, else c */
1946 #define Special 0x2000 /* func key even if c != 0 */
1947 #define ModFct 0x3000 /* special if mod-keys, else 'c' */
1948 #define Map 0x4000 /* alt scan-code, map to unshift/shift key */
1949 #define KeyPad 0x5000 /* map to insert/kp-0 depending on c == 0xe0 */
1950 #define Grey 0x6000 /* Grey keypad key */
1951
1952 #define Alt 0x0100 /* alt scan-code */
1953 #define Ctrl 0x0200 /* ctrl scan-code */
1954 #define Shift 0x0400 /* shift scan-code */
1955
1956 static int extended_kbd; /* 101 (102) keyboard present. */
1957
1958 struct kbd_translate {
1959 unsigned char sc;
1960 unsigned char ch;
1961 unsigned short code;
1962 };
1963
1964 struct dos_keyboard_map
1965 {
1966 char *unshifted;
1967 char *shifted;
1968 char *alt_gr;
1969 struct kbd_translate *translate_table;
1970 };
1971
1972
1973 static struct dos_keyboard_map us_keyboard = {
1974 /* 0 1 2 3 4 5 */
1975 /* 01234567890123456789012345678901234567890 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 /* Report availability of menus. */
2987
2988 int
2989 have_menus_p (void) { return 1; }
2990
2991 /* Create a brand new menu structure. */
2992
2993 XMenu *
2994 XMenuCreate (Display *foo1, Window foo2, char *foo3)
2995 {
2996 return IT_menu_create ();
2997 }
2998
2999 /* Create a new pane and place it on the outer-most level. It is not
3000 clear that it should be placed out there, but I don't know what else
3001 to do. */
3002
3003 int
3004 XMenuAddPane (Display *foo, XMenu *menu, const char *txt, int enable)
3005 {
3006 int len;
3007 const char *p;
3008
3009 if (!enable)
3010 emacs_abort ();
3011
3012 IT_menu_make_room (menu);
3013 menu->submenu[menu->count] = IT_menu_create ();
3014 menu->text[menu->count] = (char *)txt;
3015 menu->panenumber[menu->count] = ++menu->panecount;
3016 menu->help_text[menu->count] = NULL;
3017 menu->count++;
3018
3019 /* Adjust length for possible control characters (which will
3020 be written as ^x). */
3021 for (len = strlen (txt), p = txt; *p; p++)
3022 if (*p < 27)
3023 len++;
3024
3025 if (len > menu->width)
3026 menu->width = len;
3027
3028 return menu->panecount;
3029 }
3030
3031 /* Create a new item in a menu pane. */
3032
3033 int
3034 XMenuAddSelection (Display *bar, XMenu *menu, int pane,
3035 int foo, char *txt, int enable, char const *help_text)
3036 {
3037 int len;
3038 char *p;
3039
3040 if (pane)
3041 if (!(menu = IT_menu_search_pane (menu, pane)))
3042 return XM_FAILURE;
3043 IT_menu_make_room (menu);
3044 menu->submenu[menu->count] = (XMenu *) 0;
3045 menu->text[menu->count] = txt;
3046 menu->panenumber[menu->count] = enable;
3047 menu->help_text[menu->count] = help_text;
3048 menu->count++;
3049
3050 /* Adjust length for possible control characters (which will
3051 be written as ^x). */
3052 for (len = strlen (txt), p = txt; *p; p++)
3053 if (*p < 27)
3054 len++;
3055
3056 if (len > menu->width)
3057 menu->width = len;
3058
3059 return XM_SUCCESS;
3060 }
3061
3062 /* Decide where the menu would be placed if requested at (X,Y). */
3063
3064 void
3065 XMenuLocate (Display *foo0, XMenu *menu, int foo1, int foo2, int x, int y,
3066 int *ulx, int *uly, int *width, int *height)
3067 {
3068 IT_menu_calc_size (menu, width, height);
3069 *ulx = x + 1;
3070 *uly = y;
3071 *width += 2;
3072 }
3073
3074 struct IT_menu_state
3075 {
3076 void *screen_behind;
3077 XMenu *menu;
3078 int pane;
3079 int x, y;
3080 };
3081
3082
3083 /* Display menu, wait for user's response, and return that response. */
3084
3085 int
3086 XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx,
3087 int x0, int y0, unsigned ButtonMask, char **txt,
3088 void (*help_callback)(char const *, int, int))
3089 {
3090 struct IT_menu_state *state;
3091 int statecount, x, y, i, b, screensize, leave, result, onepane;
3092 int title_faces[4]; /* face to display the menu title */
3093 int faces[4], buffers_num_deleted = 0;
3094 struct frame *sf = SELECTED_FRAME ();
3095 Lisp_Object saved_echo_area_message, selectface;
3096
3097 /* Just in case we got here without a mouse present... */
3098 if (have_mouse <= 0)
3099 return XM_IA_SELECT;
3100 /* Don't allow non-positive x0 and y0, lest the menu will wrap
3101 around the display. */
3102 if (x0 <= 0)
3103 x0 = 1;
3104 if (y0 <= 0)
3105 y0 = 1;
3106
3107 /* We will process all the mouse events directly, so we had
3108 better prevent dos_rawgetc from stealing them from us. */
3109 mouse_preempted++;
3110
3111 state = alloca (menu->panecount * sizeof (struct IT_menu_state));
3112 screensize = screen_size * 2;
3113 faces[0]
3114 = lookup_derived_face (sf, intern ("msdos-menu-passive-face"),
3115 DEFAULT_FACE_ID, 1);
3116 faces[1]
3117 = lookup_derived_face (sf, intern ("msdos-menu-active-face"),
3118 DEFAULT_FACE_ID, 1);
3119 selectface = intern ("msdos-menu-select-face");
3120 faces[2] = lookup_derived_face (sf, selectface,
3121 faces[0], 1);
3122 faces[3] = lookup_derived_face (sf, selectface,
3123 faces[1], 1);
3124
3125 /* Make sure the menu title is always displayed with
3126 `msdos-menu-active-face', no matter where the mouse pointer is. */
3127 for (i = 0; i < 4; i++)
3128 title_faces[i] = faces[3];
3129
3130 statecount = 1;
3131
3132 /* Don't let the title for the "Buffers" popup menu include a
3133 digit (which is ugly).
3134
3135 This is a terrible kludge, but I think the "Buffers" case is
3136 the only one where the title includes a number, so it doesn't
3137 seem to be necessary to make this more general. */
3138 if (strncmp (menu->text[0], "Buffers 1", 9) == 0)
3139 {
3140 menu->text[0][7] = '\0';
3141 buffers_num_deleted = 1;
3142 }
3143
3144 /* We need to save the current echo area message, so that we could
3145 restore it below, before we exit. See the commentary below,
3146 before the call to message_with_string. */
3147 saved_echo_area_message = Fcurrent_message ();
3148 state[0].menu = menu;
3149 mouse_off ();
3150 ScreenRetrieve (state[0].screen_behind = xmalloc (screensize));
3151
3152 /* Turn off the cursor. Otherwise it shows through the menu
3153 panes, which is ugly. */
3154 IT_display_cursor (0);
3155
3156 /* Display the menu title. */
3157 IT_menu_display (menu, y0 - 1, x0 - 1, 1, title_faces, 0);
3158 if (buffers_num_deleted)
3159 menu->text[0][7] = ' ';
3160 if ((onepane = menu->count == 1 && menu->submenu[0]))
3161 {
3162 menu->width = menu->submenu[0]->width;
3163 state[0].menu = menu->submenu[0];
3164 }
3165 else
3166 {
3167 state[0].menu = menu;
3168 }
3169 state[0].x = x0 - 1;
3170 state[0].y = y0;
3171 state[0].pane = onepane;
3172
3173 mouse_last_x = -1; /* A hack that forces display. */
3174 leave = 0;
3175 while (!leave)
3176 {
3177 if (!mouse_visible) mouse_on ();
3178 mouse_check_moved ();
3179 if (sf->mouse_moved)
3180 {
3181 sf->mouse_moved = 0;
3182 result = XM_IA_SELECT;
3183 mouse_get_xy (&x, &y);
3184 for (i = 0; i < statecount; i++)
3185 if (state[i].x <= x && x < state[i].x + state[i].menu->width + 2)
3186 {
3187 int dy = y - state[i].y;
3188 if (0 <= dy && dy < state[i].menu->count)
3189 {
3190 if (!state[i].menu->submenu[dy])
3191 {
3192 if (state[i].menu->panenumber[dy])
3193 result = XM_SUCCESS;
3194 else
3195 result = XM_IA_SELECT;
3196 }
3197 *pane = state[i].pane - 1;
3198 *selidx = dy;
3199 /* We hit some part of a menu, so drop extra menus that
3200 have been opened. That does not include an open and
3201 active submenu. */
3202 if (i != statecount - 2
3203 || state[i].menu->submenu[dy] != state[i+1].menu)
3204 while (i != statecount - 1)
3205 {
3206 statecount--;
3207 mouse_off ();
3208 ScreenUpdate (state[statecount].screen_behind);
3209 if (screen_virtual_segment)
3210 dosv_refresh_virtual_screen (0, screen_size);
3211 xfree (state[statecount].screen_behind);
3212 }
3213 if (i == statecount - 1 && state[i].menu->submenu[dy])
3214 {
3215 IT_menu_display (state[i].menu,
3216 state[i].y,
3217 state[i].x,
3218 state[i].pane,
3219 faces, 1);
3220 state[statecount].menu = state[i].menu->submenu[dy];
3221 state[statecount].pane = state[i].menu->panenumber[dy];
3222 mouse_off ();
3223 ScreenRetrieve (state[statecount].screen_behind
3224 = xmalloc (screensize));
3225 state[statecount].x
3226 = state[i].x + state[i].menu->width + 2;
3227 state[statecount].y = y;
3228 statecount++;
3229 }
3230 }
3231 }
3232 IT_menu_display (state[statecount - 1].menu,
3233 state[statecount - 1].y,
3234 state[statecount - 1].x,
3235 state[statecount - 1].pane,
3236 faces, 1);
3237 }
3238 else
3239 {
3240 if ((menu_help_message || prev_menu_help_message)
3241 && menu_help_message != prev_menu_help_message)
3242 {
3243 help_callback (menu_help_message,
3244 menu_help_paneno, menu_help_itemno);
3245 IT_display_cursor (0);
3246 prev_menu_help_message = menu_help_message;
3247 }
3248 /* We are busy-waiting for the mouse to move, so let's be nice
3249 to other Windows applications by releasing our time slice. */
3250 __dpmi_yield ();
3251 }
3252 for (b = 0; b < mouse_button_count && !leave; b++)
3253 {
3254 /* Only leave if user both pressed and released the mouse, and in
3255 that order. This avoids popping down the menu pane unless
3256 the user is really done with it. */
3257 if (mouse_pressed (b, &x, &y))
3258 {
3259 while (mouse_button_depressed (b, &x, &y))
3260 __dpmi_yield ();
3261 leave = 1;
3262 }
3263 (void) mouse_released (b, &x, &y);
3264 }
3265 }
3266
3267 mouse_off ();
3268 ScreenUpdate (state[0].screen_behind);
3269 if (screen_virtual_segment)
3270 dosv_refresh_virtual_screen (0, screen_size);
3271
3272 /* We have a situation here. ScreenUpdate has just restored the
3273 screen contents as it was before we started drawing this menu.
3274 That includes any echo area message that could have been
3275 displayed back then. (In reality, that echo area message will
3276 almost always be the ``keystroke echo'' that echoes the sequence
3277 of menu items chosen by the user.) However, if the menu had some
3278 help messages, then displaying those messages caused Emacs to
3279 forget about the original echo area message. So when
3280 ScreenUpdate restored it, it created a discrepancy between the
3281 actual screen contents and what Emacs internal data structures
3282 know about it.
3283
3284 To avoid this conflict, we force Emacs to restore the original
3285 echo area message as we found it when we entered this function.
3286 The irony of this is that we then erase the restored message
3287 right away, so the only purpose of restoring it is so that
3288 erasing it works correctly... */
3289 if (! NILP (saved_echo_area_message))
3290 message_with_string ("%s", saved_echo_area_message, 0);
3291 message1 (0);
3292 while (statecount--)
3293 xfree (state[statecount].screen_behind);
3294 IT_display_cursor (1); /* Turn cursor back on. */
3295 /* Clean up any mouse events that are waiting inside Emacs event queue.
3296 These events are likely to be generated before the menu was even
3297 displayed, probably because the user pressed and released the button
3298 (which invoked the menu) too quickly. If we don't remove these events,
3299 Emacs will process them after we return and surprise the user. */
3300 discard_mouse_events ();
3301 mouse_clear_clicks ();
3302 if (!kbd_buffer_events_waiting ())
3303 clear_input_pending ();
3304 /* Allow mouse events generation by dos_rawgetc. */
3305 mouse_preempted--;
3306 return result;
3307 }
3308
3309 /* Dispose of a menu. */
3310
3311 void
3312 XMenuDestroy (Display *foo, XMenu *menu)
3313 {
3314 int i;
3315 if (menu->allocated)
3316 {
3317 for (i = 0; i < menu->count; i++)
3318 if (menu->submenu[i])
3319 XMenuDestroy (foo, menu->submenu[i]);
3320 xfree (menu->text);
3321 xfree (menu->submenu);
3322 xfree (menu->panenumber);
3323 xfree (menu->help_text);
3324 }
3325 xfree (menu);
3326 menu_help_message = prev_menu_help_message = NULL;
3327 }
3328
3329 int
3330 x_pixel_width (struct frame *f)
3331 {
3332 return FRAME_COLS (f);
3333 }
3334
3335 int
3336 x_pixel_height (struct frame *f)
3337 {
3338 return FRAME_LINES (f);
3339 }
3340 #endif /* !HAVE_X_WINDOWS */
3341 \f
3342 /* ----------------------- DOS / UNIX conversion --------------------- */
3343
3344 void msdos_downcase_filename (unsigned char *);
3345
3346 /* Destructively turn backslashes into slashes. */
3347
3348 void
3349 dostounix_filename (char *p, int ignore)
3350 {
3351 msdos_downcase_filename (p);
3352
3353 while (*p)
3354 {
3355 if (*p == '\\')
3356 *p = '/';
3357 p++;
3358 }
3359 }
3360
3361 /* Destructively turn slashes into backslashes. */
3362
3363 void
3364 unixtodos_filename (char *p)
3365 {
3366 if (p[1] == ':' && *p >= 'A' && *p <= 'Z')
3367 {
3368 *p += 'a' - 'A';
3369 p += 2;
3370 }
3371
3372 while (*p)
3373 {
3374 if (*p == '/')
3375 *p = '\\';
3376 p++;
3377 }
3378 }
3379
3380 /* Get the default directory for a given drive. 0=def, 1=A, 2=B, ... */
3381
3382 int
3383 getdefdir (int drive, char *dst)
3384 {
3385 char in_path[4], *p = in_path, e = errno;
3386
3387 /* Generate "X:." (when drive is X) or "." (when drive is 0). */
3388 if (drive != 0)
3389 {
3390 *p++ = drive + 'A' - 1;
3391 *p++ = ':';
3392 }
3393
3394 *p++ = '.';
3395 *p = '\0';
3396 errno = 0;
3397 _fixpath (in_path, dst);
3398 /* _fixpath can set errno to ENOSYS on non-LFN systems because
3399 it queries the LFN support, so ignore that error. */
3400 if ((errno && errno != ENOSYS) || *dst == '\0')
3401 return 0;
3402
3403 msdos_downcase_filename (dst);
3404
3405 errno = e;
3406 return 1;
3407 }
3408
3409 char *
3410 emacs_root_dir (void)
3411 {
3412 static char root_dir[4];
3413
3414 sprintf (root_dir, "%c:/", 'A' + getdisk ());
3415 root_dir[0] = tolower (root_dir[0]);
3416 return root_dir;
3417 }
3418
3419 /* Remove all CR's that are followed by a LF. */
3420
3421 int
3422 crlf_to_lf (int n, unsigned char *buf)
3423 {
3424 unsigned char *np = buf, *startp = buf, *endp = buf + n;
3425
3426 if (n == 0)
3427 return n;
3428 while (buf < endp - 1)
3429 {
3430 if (*buf == 0x0d)
3431 {
3432 if (*(++buf) != 0x0a)
3433 *np++ = 0x0d;
3434 }
3435 else
3436 *np++ = *buf++;
3437 }
3438 if (buf < endp)
3439 *np++ = *buf++;
3440 return np - startp;
3441 }
3442
3443 DEFUN ("msdos-long-file-names", Fmsdos_long_file_names, Smsdos_long_file_names,
3444 0, 0, 0,
3445 doc: /* Return non-nil if long file names are supported on MS-DOS. */)
3446 (void)
3447 {
3448 return (_USE_LFN ? Qt : Qnil);
3449 }
3450
3451 /* Convert alphabetic characters in a filename to lower-case. */
3452
3453 void
3454 msdos_downcase_filename (unsigned char *p)
3455 {
3456 /* Always lower-case drive letters a-z, even if the filesystem
3457 preserves case in filenames.
3458 This is so MSDOS filenames could be compared by string comparison
3459 functions that are case-sensitive. Even case-preserving filesystems
3460 do not distinguish case in drive letters. */
3461 if (p[1] == ':' && *p >= 'A' && *p <= 'Z')
3462 {
3463 *p += 'a' - 'A';
3464 p += 2;
3465 }
3466
3467 /* Under LFN we expect to get pathnames in their true case. */
3468 if (NILP (Fmsdos_long_file_names ()))
3469 for ( ; *p; p++)
3470 if (*p >= 'A' && *p <= 'Z')
3471 *p += 'a' - 'A';
3472 }
3473
3474 DEFUN ("msdos-downcase-filename", Fmsdos_downcase_filename, Smsdos_downcase_filename,
3475 1, 1, 0,
3476 doc: /* Convert alphabetic characters in FILENAME to lower case and return that.
3477 When long filenames are supported, doesn't change FILENAME.
3478 If FILENAME is not a string, returns nil.
3479 The argument object is never altered--the value is a copy. */)
3480 (Lisp_Object filename)
3481 {
3482 Lisp_Object tem;
3483
3484 if (! STRINGP (filename))
3485 return Qnil;
3486
3487 tem = Fcopy_sequence (filename);
3488 msdos_downcase_filename (SDATA (tem));
3489 return tem;
3490 }
3491 \f
3492 /* The Emacs root directory as determined by init_environment. */
3493
3494 static char emacsroot[MAXPATHLEN];
3495
3496 char *
3497 rootrelativepath (char *rel)
3498 {
3499 static char result[MAXPATHLEN + 10];
3500
3501 strcpy (result, emacsroot);
3502 strcat (result, "/");
3503 strcat (result, rel);
3504 return result;
3505 }
3506
3507 /* Define a lot of environment variables if not already defined. Don't
3508 remove anything unless you know what you're doing -- lots of code will
3509 break if one or more of these are missing. */
3510
3511 void
3512 init_environment (int argc, char **argv, int skip_args)
3513 {
3514 char *s, *t, *root;
3515 int len, i;
3516 static const char * const tempdirs[] = {
3517 "$TMPDIR", "$TEMP", "$TMP", "c:/"
3518 };
3519 const int imax = sizeof (tempdirs) / sizeof (tempdirs[0]);
3520
3521 /* Make sure they have a usable $TMPDIR. Many Emacs functions use
3522 temporary files and assume "/tmp" if $TMPDIR is unset, which
3523 will break on DOS/Windows. Refuse to work if we cannot find
3524 a directory, not even "c:/", usable for that purpose. */
3525 for (i = 0; i < imax ; i++)
3526 {
3527 const char *tmp = tempdirs[i];
3528 char buf[FILENAME_MAX];
3529
3530 if (*tmp == '$')
3531 {
3532 int tmp_len;
3533
3534 tmp = getenv (tmp + 1);
3535 if (!tmp)
3536 continue;
3537
3538 /* Some lusers set TMPDIR=e:, probably because some losing
3539 programs cannot handle multiple slashes if they use e:/.
3540 e: fails in `access' below, so we interpret e: as e:/. */
3541 tmp_len = strlen (tmp);
3542 if (tmp[tmp_len - 1] != '/' && tmp[tmp_len - 1] != '\\')
3543 {
3544 strcpy (buf, tmp);
3545 buf[tmp_len++] = '/', buf[tmp_len] = 0;
3546 tmp = buf;
3547 }
3548 }
3549
3550 /* Note that `access' can lie to us if the directory resides on a
3551 read-only filesystem, like CD-ROM or a write-protected floppy.
3552 The only way to be really sure is to actually create a file and
3553 see if it succeeds. But I think that's too much to ask. */
3554 if (tmp && access (tmp, D_OK) == 0)
3555 {
3556 setenv ("TMPDIR", tmp, 1);
3557 break;
3558 }
3559 }
3560 if (i >= imax)
3561 cmd_error_internal
3562 (Fcons (Qerror,
3563 Fcons (build_string ("no usable temporary directories found!!"),
3564 Qnil)),
3565 "While setting TMPDIR: ");
3566
3567 /* Note the startup time, so we know not to clear the screen if we
3568 exit immediately; see IT_reset_terminal_modes.
3569 (Yes, I know `clock' returns zero the first time it's called, but
3570 I do this anyway, in case some wiseguy changes that at some point.) */
3571 startup_time = clock ();
3572
3573 /* Find our root from argv[0]. Assuming argv[0] is, say,
3574 "c:/emacs/bin/emacs.exe" our root will be "c:/emacs". */
3575 root = alloca (MAXPATHLEN + 20);
3576 _fixpath (argv[0], root);
3577 msdos_downcase_filename (root);
3578 len = strlen (root);
3579 while (len > 0 && root[len] != '/' && root[len] != ':')
3580 len--;
3581 root[len] = '\0';
3582 if (len > 4
3583 && (strcmp (root + len - 4, "/bin") == 0
3584 || strcmp (root + len - 4, "/src") == 0)) /* under a debugger */
3585 root[len - 4] = '\0';
3586 else
3587 strcpy (root, "c:/emacs"); /* let's be defensive */
3588 len = strlen (root);
3589 strcpy (emacsroot, root);
3590
3591 /* We default HOME to our root. */
3592 setenv ("HOME", root, 0);
3593
3594 /* We default EMACSPATH to root + "/bin". */
3595 strcpy (root + len, "/bin");
3596 setenv ("EMACSPATH", root, 0);
3597
3598 /* I don't expect anybody to ever use other terminals so the internal
3599 terminal is the default. */
3600 setenv ("TERM", "internal", 0);
3601
3602 #ifdef HAVE_X_WINDOWS
3603 /* Emacs expects DISPLAY to be set. */
3604 setenv ("DISPLAY", "unix:0.0", 0);
3605 #endif
3606
3607 /* SHELL is a bit tricky -- COMSPEC is the closest we come, but we must
3608 downcase it and mirror the backslashes. */
3609 s = getenv ("COMSPEC");
3610 if (!s) s = "c:/command.com";
3611 t = alloca (strlen (s) + 1);
3612 strcpy (t, s);
3613 dostounix_filename (t, 0);
3614 setenv ("SHELL", t, 0);
3615
3616 /* PATH is also downcased and backslashes mirrored. */
3617 s = getenv ("PATH");
3618 if (!s) s = "";
3619 t = alloca (strlen (s) + 3);
3620 /* Current directory is always considered part of MsDos's path but it is
3621 not normally mentioned. Now it is. */
3622 strcat (strcpy (t, ".;"), s);
3623 dostounix_filename (t, 0); /* Not a single file name, but this should work. */
3624 setenv ("PATH", t, 1);
3625
3626 /* In some sense all dos users have root privileges, so... */
3627 setenv ("USER", "root", 0);
3628 setenv ("NAME", getenv ("USER"), 0);
3629
3630 /* Time zone determined from country code. To make this possible, the
3631 country code may not span more than one time zone. In other words,
3632 in the USA, you lose. */
3633 if (!getenv ("TZ"))
3634 switch (dos_country_code)
3635 {
3636 case 31: /* Belgium */
3637 case 32: /* The Netherlands */
3638 case 33: /* France */
3639 case 34: /* Spain */
3640 case 36: /* Hungary */
3641 case 38: /* Yugoslavia (or what's left of it?) */
3642 case 39: /* Italy */
3643 case 41: /* Switzerland */
3644 case 42: /* Tjekia */
3645 case 45: /* Denmark */
3646 case 46: /* Sweden */
3647 case 47: /* Norway */
3648 case 48: /* Poland */
3649 case 49: /* Germany */
3650 /* Daylight saving from last Sunday in March to last Sunday in
3651 September, both at 2AM. */
3652 setenv ("TZ", "MET-01METDST-02,M3.5.0/02:00,M9.5.0/02:00", 0);
3653 break;
3654 case 44: /* United Kingdom */
3655 case 351: /* Portugal */
3656 case 354: /* Iceland */
3657 setenv ("TZ", "GMT+00", 0);
3658 break;
3659 case 81: /* Japan */
3660 case 82: /* Korea */
3661 setenv ("TZ", "JST-09", 0);
3662 break;
3663 case 90: /* Turkey */
3664 case 358: /* Finland */
3665 setenv ("TZ", "EET-02", 0);
3666 break;
3667 case 972: /* Israel */
3668 /* This is an approximation. (For exact rules, use the
3669 `zoneinfo/israel' file which comes with DJGPP, but you need
3670 to install it in `/usr/share/zoneinfo/' directory first.) */
3671 setenv ("TZ", "IST-02IDT-03,M4.1.6/00:00,M9.5.6/01:00", 0);
3672 break;
3673 }
3674 tzset ();
3675 }
3676
3677 \f
3678
3679 static int break_stat; /* BREAK check mode status. */
3680 static int stdin_stat; /* stdin IOCTL status. */
3681
3682 /* Turn off Dos' Ctrl-C checking and inhibit interpretation of
3683 control chars by DOS. Determine the keyboard type. */
3684
3685 int
3686 dos_ttraw (struct tty_display_info *tty)
3687 {
3688 union REGS inregs, outregs;
3689 static int first_time = 1;
3690
3691 /* If we are called for the initial terminal, it's too early to do
3692 anything, and termscript isn't set up. */
3693 if (tty->terminal->type == output_initial)
3694 return 2;
3695
3696 break_stat = getcbrk ();
3697 setcbrk (0);
3698
3699 if (first_time)
3700 {
3701 inregs.h.ah = 0xc0;
3702 int86 (0x15, &inregs, &outregs);
3703 extended_kbd = (!outregs.x.cflag) && (outregs.h.ah == 0);
3704
3705 have_mouse = 0;
3706
3707 if (1
3708 #ifdef HAVE_X_WINDOWS
3709 && inhibit_window_system
3710 #endif
3711 )
3712 {
3713 inregs.x.ax = 0x0021;
3714 int86 (0x33, &inregs, &outregs);
3715 have_mouse = (outregs.x.ax & 0xffff) == 0xffff;
3716 if (!have_mouse)
3717 {
3718 /* Reportedly, the above doesn't work for some mouse drivers. There
3719 is an additional detection method that should work, but might be
3720 a little slower. Use that as an alternative. */
3721 inregs.x.ax = 0x0000;
3722 int86 (0x33, &inregs, &outregs);
3723 have_mouse = (outregs.x.ax & 0xffff) == 0xffff;
3724 }
3725 if (have_mouse)
3726 mouse_button_count = outregs.x.bx;
3727
3728 #ifndef HAVE_X_WINDOWS
3729 /* Save the cursor shape used outside Emacs. */
3730 outside_cursor = _farpeekw (_dos_ds, 0x460);
3731 #endif
3732 }
3733
3734 first_time = 0;
3735
3736 stdin_stat = setmode (fileno (stdin), O_BINARY);
3737 return (stdin_stat != -1);
3738 }
3739 else
3740 return (setmode (fileno (stdin), O_BINARY) != -1);
3741 }
3742
3743 /* Restore status of standard input and Ctrl-C checking. */
3744
3745 int
3746 dos_ttcooked (void)
3747 {
3748 union REGS inregs, outregs;
3749
3750 setcbrk (break_stat);
3751 mouse_off ();
3752
3753 #ifndef HAVE_X_WINDOWS
3754 /* Restore the cursor shape we found on startup. */
3755 if (outside_cursor)
3756 {
3757 inregs.h.ah = 1;
3758 inregs.x.cx = outside_cursor;
3759 int86 (0x10, &inregs, &outregs);
3760 }
3761 #endif
3762
3763 return (setmode (fileno (stdin), stdin_stat) != -1);
3764 }
3765
3766 \f
3767 /* Run command as specified by ARGV in directory DIR.
3768 The command is run with input from TEMPIN, output to
3769 file TEMPOUT and stderr to TEMPERR. */
3770
3771 int
3772 run_msdos_command (unsigned char **argv, const char *working_dir,
3773 int tempin, int tempout, int temperr, char **envv)
3774 {
3775 char *saveargv1, *saveargv2, *lowcase_argv0, *pa, *pl;
3776 char oldwd[MAXPATHLEN + 1]; /* Fixed size is safe on MSDOS. */
3777 int msshell, result = -1, inbak, outbak, errbak, x, y;
3778 Lisp_Object cmd;
3779
3780 /* Get current directory as MSDOS cwd is not per-process. */
3781 getwd (oldwd);
3782
3783 /* If argv[0] is the shell, it might come in any lettercase.
3784 Since `Fmember' is case-sensitive, we need to downcase
3785 argv[0], even if we are on case-preserving filesystems. */
3786 lowcase_argv0 = alloca (strlen (argv[0]) + 1);
3787 for (pa = argv[0], pl = lowcase_argv0; *pa; pl++)
3788 {
3789 *pl = *pa++;
3790 if (*pl >= 'A' && *pl <= 'Z')
3791 *pl += 'a' - 'A';
3792 }
3793 *pl = '\0';
3794
3795 cmd = Ffile_name_nondirectory (build_string (lowcase_argv0));
3796 msshell = !NILP (Fmember (cmd, Fsymbol_value (intern ("msdos-shells"))))
3797 && !strcmp ("-c", argv[1]);
3798 if (msshell)
3799 {
3800 saveargv1 = argv[1];
3801 saveargv2 = argv[2];
3802 argv[1] = "/c";
3803 /* We only need to mirror slashes if a DOS shell will be invoked
3804 not via `system' (which does the mirroring itself). Yes, that
3805 means DJGPP v1.x will lose here. */
3806 if (argv[2] && argv[3])
3807 {
3808 char *p = alloca (strlen (argv[2]) + 1);
3809
3810 strcpy (argv[2] = p, saveargv2);
3811 while (*p && isspace (*p))
3812 p++;
3813 while (*p)
3814 {
3815 if (*p == '/')
3816 *p++ = '\\';
3817 else
3818 p++;
3819 }
3820 }
3821 }
3822
3823 chdir (working_dir);
3824 inbak = dup (0);
3825 outbak = dup (1);
3826 errbak = dup (2);
3827 if (inbak < 0 || outbak < 0 || errbak < 0)
3828 goto done; /* Allocation might fail due to lack of descriptors. */
3829
3830 if (have_mouse > 0)
3831 mouse_get_xy (&x, &y);
3832
3833 if (!noninteractive)
3834 dos_ttcooked (); /* do it here while 0 = stdin */
3835
3836 dup2 (tempin, 0);
3837 dup2 (tempout, 1);
3838 dup2 (temperr, 2);
3839
3840 if (msshell && !argv[3])
3841 {
3842 /* MS-DOS native shells are too restrictive. For starters, they
3843 cannot grok commands longer than 126 characters. In DJGPP v2
3844 and later, `system' is much smarter, so we'll call it instead. */
3845
3846 const char *cmnd;
3847
3848 /* A shell gets a single argument--its full command
3849 line--whose original was saved in `saveargv2'. */
3850
3851 /* Don't let them pass empty command lines to `system', since
3852 with some shells it will try to invoke an interactive shell,
3853 which will hang Emacs. */
3854 for (cmnd = saveargv2; *cmnd && isspace (*cmnd); cmnd++)
3855 ;
3856 if (*cmnd)
3857 {
3858 extern char **environ;
3859 char **save_env = environ;
3860 int save_system_flags = __system_flags;
3861
3862 /* Request the most powerful version of `system'. We need
3863 all the help we can get to avoid calling stock DOS shells. */
3864 __system_flags = (__system_redirect
3865 | __system_use_shell
3866 | __system_allow_multiple_cmds
3867 | __system_allow_long_cmds
3868 | __system_handle_null_commands
3869 | __system_emulate_chdir);
3870
3871 environ = envv;
3872 result = system (cmnd);
3873 __system_flags = save_system_flags;
3874 environ = save_env;
3875 }
3876 else
3877 result = 0; /* emulate Unixy shell behavior with empty cmd line */
3878 }
3879 else
3880 result = spawnve (P_WAIT, argv[0], (char **)argv, envv);
3881
3882 dup2 (inbak, 0);
3883 dup2 (outbak, 1);
3884 dup2 (errbak, 2);
3885 emacs_close (inbak);
3886 emacs_close (outbak);
3887 emacs_close (errbak);
3888
3889 if (!noninteractive)
3890 dos_ttraw (CURTTY ());
3891 if (have_mouse > 0)
3892 {
3893 mouse_init ();
3894 mouse_moveto (x, y);
3895 }
3896
3897 /* Some programs might change the meaning of the highest bit of the
3898 text attribute byte, so we get blinking characters instead of the
3899 bright background colors. Restore that. */
3900 if (!noninteractive)
3901 bright_bg ();
3902
3903 done:
3904 chdir (oldwd);
3905 if (msshell)
3906 {
3907 argv[1] = saveargv1;
3908 argv[2] = saveargv2;
3909 }
3910 return result;
3911 }
3912
3913 void
3914 croak (char *badfunc)
3915 {
3916 fprintf (stderr, "%s not yet implemented\r\n", badfunc);
3917 reset_all_sys_modes ();
3918 exit (1);
3919 }
3920 \f
3921 /*
3922 * A few unimplemented functions that we silently ignore.
3923 */
3924 pid_t tcgetpgrp (int fd) { return 0; }
3925 int setpgid (int pid, int pgid) { return 0; }
3926 int setpriority (int x, int y, int z) { return 0; }
3927 pid_t setsid (void) { return 0; }
3928
3929 #if __DJGPP__ == 2 && __DJGPP_MINOR__ < 4
3930 ssize_t
3931 readlink (const char *name, char *dummy1, size_t dummy2)
3932 {
3933 /* `access' is much faster than `stat' on MS-DOS. */
3934 if (access (name, F_OK) == 0)
3935 errno = EINVAL;
3936 return -1;
3937 }
3938 #endif
3939
3940 char *
3941 careadlinkat (int fd, char const *filename,
3942 char *buffer, size_t buffer_size,
3943 struct allocator const *alloc,
3944 ssize_t (*preadlinkat) (int, char const *, char *, size_t))
3945 {
3946 if (!buffer)
3947 {
3948 /* We don't support the fancy auto-allocation feature. */
3949 if (!buffer_size)
3950 errno = ENOSYS;
3951 else
3952 errno = EINVAL;
3953 buffer = NULL;
3954 }
3955 else
3956 {
3957 ssize_t len = preadlinkat (fd, filename, buffer, buffer_size);
3958
3959 if (len < 0 || len == buffer_size)
3960 buffer = NULL;
3961 else
3962 buffer[len + 1] = '\0';
3963 }
3964 return buffer;
3965 }
3966
3967 \f
3968 #if __DJGPP__ == 2 && __DJGPP_MINOR__ < 2
3969
3970 /* Augment DJGPP library POSIX signal functions. This is needed
3971 as of DJGPP v2.01, but might be in the library in later releases. */
3972
3973 #include <libc/bss.h>
3974
3975 /* A counter to know when to re-initialize the static sets. */
3976 static int sigprocmask_count = -1;
3977
3978 /* Which signals are currently blocked (initially none). */
3979 static sigset_t current_mask;
3980
3981 /* Which signals are pending (initially none). */
3982 static sigset_t msdos_pending_signals;
3983
3984 /* Previous handlers to restore when the blocked signals are unblocked. */
3985 typedef void (*sighandler_t)(int);
3986 static sighandler_t prev_handlers[320];
3987
3988 /* A signal handler which just records that a signal occurred
3989 (it will be raised later, if and when the signal is unblocked). */
3990 static void
3991 sig_suspender (int signo)
3992 {
3993 sigaddset (&msdos_pending_signals, signo);
3994 }
3995
3996 int
3997 sigprocmask (int how, const sigset_t *new_set, sigset_t *old_set)
3998 {
3999 int signo;
4000 sigset_t new_mask;
4001
4002 /* If called for the first time, initialize. */
4003 if (sigprocmask_count != __bss_count)
4004 {
4005 sigprocmask_count = __bss_count;
4006 sigemptyset (&msdos_pending_signals);
4007 sigemptyset (&current_mask);
4008 for (signo = 0; signo < 320; signo++)
4009 prev_handlers[signo] = SIG_ERR;
4010 }
4011
4012 if (old_set)
4013 *old_set = current_mask;
4014
4015 if (new_set == 0)
4016 return 0;
4017
4018 if (how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK)
4019 {
4020 errno = EINVAL;
4021 return -1;
4022 }
4023
4024 sigemptyset (&new_mask);
4025
4026 /* DJGPP supports upto 320 signals. */
4027 for (signo = 0; signo < 320; signo++)
4028 {
4029 if (sigismember (&current_mask, signo))
4030 sigaddset (&new_mask, signo);
4031 else if (sigismember (new_set, signo) && how != SIG_UNBLOCK)
4032 {
4033 sigaddset (&new_mask, signo);
4034
4035 /* SIGKILL is silently ignored, as on other platforms. */
4036 if (signo != SIGKILL && prev_handlers[signo] == SIG_ERR)
4037 prev_handlers[signo] = signal (signo, sig_suspender);
4038 }
4039 if (( how == SIG_UNBLOCK
4040 && sigismember (&new_mask, signo)
4041 && sigismember (new_set, signo))
4042 || (how == SIG_SETMASK
4043 && sigismember (&new_mask, signo)
4044 && !sigismember (new_set, signo)))
4045 {
4046 sigdelset (&new_mask, signo);
4047 if (prev_handlers[signo] != SIG_ERR)
4048 {
4049 signal (signo, prev_handlers[signo]);
4050 prev_handlers[signo] = SIG_ERR;
4051 }
4052 if (sigismember (&msdos_pending_signals, signo))
4053 {
4054 sigdelset (&msdos_pending_signals, signo);
4055 raise (signo);
4056 }
4057 }
4058 }
4059 current_mask = new_mask;
4060 return 0;
4061 }
4062
4063 #endif /* not __DJGPP_MINOR__ < 2 */
4064
4065 #ifndef HAVE_SELECT
4066 #include "sysselect.h"
4067
4068 /* This yields the rest of the current time slice to the task manager.
4069 It should be called by any code which knows that it has nothing
4070 useful to do except idle.
4071
4072 I don't use __dpmi_yield here, since versions of library before 2.02
4073 called Int 2Fh/AX=1680h there in a way that would wedge the DOS box
4074 on some versions of Windows 9X. */
4075
4076 void
4077 dos_yield_time_slice (void)
4078 {
4079 _go32_dpmi_registers r;
4080
4081 r.x.ax = 0x1680;
4082 r.x.ss = r.x.sp = r.x.flags = 0;
4083 _go32_dpmi_simulate_int (0x2f, &r);
4084 if (r.h.al == 0x80)
4085 errno = ENOSYS;
4086 }
4087
4088 /* Only event queue is checked. */
4089 /* We don't have to call timer_check here
4090 because wait_reading_process_output takes care of that. */
4091 int
4092 sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
4093 EMACS_TIME *timeout, void *ignored)
4094 {
4095 int check_input;
4096 struct timespec t;
4097
4098 check_input = 0;
4099 if (rfds)
4100 {
4101 check_input = FD_ISSET (0, rfds);
4102 FD_ZERO (rfds);
4103 }
4104 if (wfds)
4105 FD_ZERO (wfds);
4106 if (efds)
4107 FD_ZERO (efds);
4108
4109 if (nfds != 1)
4110 emacs_abort ();
4111
4112 /* If we are looking only for the terminal, with no timeout,
4113 just read it and wait -- that's more efficient. */
4114 if (!timeout)
4115 {
4116 while (!detect_input_pending ())
4117 {
4118 dos_yield_time_slice ();
4119 }
4120 }
4121 else
4122 {
4123 EMACS_TIME clnow, cllast, cldiff;
4124
4125 gettime (&t);
4126 cllast = make_emacs_time (t.tv_sec, t.tv_nsec);
4127
4128 while (!check_input || !detect_input_pending ())
4129 {
4130 gettime (&t);
4131 clnow = make_emacs_time (t.tv_sec, t.tv_nsec);
4132 cldiff = sub_emacs_time (clnow, cllast);
4133 *timeout = sub_emacs_time (*timeout, cldiff);
4134
4135 /* Stop when timeout value crosses zero. */
4136 if (EMACS_TIME_SIGN (*timeout) <= 0)
4137 return 0;
4138 cllast = clnow;
4139 dos_yield_time_slice ();
4140 }
4141 }
4142
4143 FD_SET (0, rfds);
4144 return 1;
4145 }
4146 #endif
4147
4148 /*
4149 * Define overlaid functions:
4150 *
4151 * chdir -> sys_chdir
4152 * tzset -> init_gettimeofday
4153 * abort -> dos_abort
4154 */
4155
4156 #ifdef chdir
4157 #undef chdir
4158 extern int chdir (const char *);
4159
4160 int
4161 sys_chdir (const char *path)
4162 {
4163 int len = strlen (path);
4164 char *tmp = (char *)path;
4165
4166 if (*tmp && tmp[1] == ':')
4167 {
4168 if (getdisk () != tolower (tmp[0]) - 'a')
4169 setdisk (tolower (tmp[0]) - 'a');
4170 tmp += 2; /* strip drive: KFS 1995-07-06 */
4171 len -= 2;
4172 }
4173
4174 if (len > 1 && (tmp[len - 1] == '/'))
4175 {
4176 char *tmp1 = (char *) alloca (len + 1);
4177 strcpy (tmp1, tmp);
4178 tmp1[len - 1] = 0;
4179 tmp = tmp1;
4180 }
4181 return chdir (tmp);
4182 }
4183 #endif
4184
4185 #ifdef tzset
4186 #undef tzset
4187 extern void tzset (void);
4188
4189 void
4190 init_gettimeofday (void)
4191 {
4192 time_t ltm, gtm;
4193 struct tm *lstm;
4194
4195 tzset ();
4196 ltm = gtm = time (NULL);
4197 ltm = mktime (lstm = localtime (&ltm));
4198 gtm = mktime (gmtime (&gtm));
4199 time_rec.tm_hour = 99; /* force gettimeofday to get date */
4200 time_rec.tm_isdst = lstm->tm_isdst;
4201 dos_timezone_offset = time_rec.tm_gmtoff = (int)(gtm - ltm) / 60;
4202 }
4203 #endif
4204
4205 static void
4206 msdos_abort (void)
4207 {
4208 dos_ttcooked ();
4209 ScreenSetCursor (10, 0);
4210 cputs ("\r\n\nEmacs aborted!\r\n");
4211 #if __DJGPP__ == 2 && __DJGPP_MINOR__ < 2
4212 if (screen_virtual_segment)
4213 dosv_refresh_virtual_screen (2 * 10 * screen_size_X, 4 * screen_size_X);
4214 /* Generate traceback, so we could tell whodunit. */
4215 signal (SIGINT, SIG_DFL);
4216 __asm__ __volatile__ ("movb $0x1b,%al;call ___djgpp_hw_exception");
4217 #else /* __DJGPP_MINOR__ >= 2 */
4218 raise (SIGABRT);
4219 #endif /* __DJGPP_MINOR__ >= 2 */
4220 exit (2);
4221 }
4222
4223 void
4224 msdos_fatal_signal (int sig)
4225 {
4226 if (sig == SIGABRT)
4227 msdos_abort ();
4228 else
4229 raise (sig);
4230 }
4231
4232 void
4233 syms_of_msdos (void)
4234 {
4235 recent_doskeys = Fmake_vector (make_number (NUM_RECENT_DOSKEYS), Qnil);
4236 staticpro (&recent_doskeys);
4237
4238 #ifndef HAVE_X_WINDOWS
4239
4240 /* The following two are from xfns.c: */
4241 DEFSYM (Qreverse, "reverse");
4242
4243 DEFVAR_LISP ("dos-unsupported-char-glyph", Vdos_unsupported_char_glyph,
4244 doc: /* Glyph to display instead of chars not supported by current codepage.
4245 This variable is used only by MS-DOS terminals. */);
4246 Vdos_unsupported_char_glyph = make_number ('\177');
4247
4248 #endif
4249
4250 defsubr (&Srecent_doskeys);
4251 defsubr (&Smsdos_long_file_names);
4252 defsubr (&Smsdos_downcase_filename);
4253 defsubr (&Smsdos_remember_default_colors);
4254 defsubr (&Smsdos_set_mouse_buttons);
4255 }
4256
4257 #endif /* MSDOS */