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