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