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