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