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