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