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