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