Update copyright notice.
[bpt/emacs.git] / 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 "Set the number of mouse buttons to use by Emacs.\n\
193 This is useful with mice that report the number of buttons inconsistently,\n\
194 e.g., if the number of buttons is reported as 3, but Emacs only sees 2 of\n\
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, 0);
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 static int highlight;
378
379 static int screen_size_X;
380 static int screen_size_Y;
381 static int screen_size;
382
383 static int current_pos_X;
384 static int current_pos_Y;
385 static int new_pos_X;
386 static int new_pos_Y;
387
388 static void *startup_screen_buffer;
389 static int startup_screen_size_X;
390 static int startup_screen_size_Y;
391 static int startup_pos_X;
392 static int startup_pos_Y;
393 static unsigned char startup_screen_attrib;
394
395 static clock_t startup_time;
396
397 static int term_setup_done;
398
399 static unsigned short outside_cursor;
400
401 /* Similar to the_only_frame. */
402 struct x_output the_only_x_display;
403
404 /* Support for DOS/V (allows Japanese characters to be displayed on
405 standard, non-Japanese, ATs). Only supported for DJGPP v2 and later. */
406
407 /* Holds the address of the text-mode screen buffer. */
408 static unsigned long screen_old_address = 0;
409 /* Segment and offset of the virtual screen. If 0, DOS/V is NOT loaded. */
410 static unsigned short screen_virtual_segment = 0;
411 static unsigned short screen_virtual_offset = 0;
412 /* A flag to control how to display unibyte 8-bit characters. */
413 extern int unibyte_display_via_language_environment;
414
415 Lisp_Object Qbar;
416
417 #if __DJGPP__ > 1
418 /* Update the screen from a part of relocated DOS/V screen buffer which
419 begins at OFFSET and includes COUNT characters. */
420 static void
421 dosv_refresh_virtual_screen (int offset, int count)
422 {
423 __dpmi_regs regs;
424
425 if (offset < 0 || count < 0) /* paranoia; invalid values crash DOS/V */
426 return;
427
428 regs.h.ah = 0xff; /* update relocated screen */
429 regs.x.es = screen_virtual_segment;
430 regs.x.di = screen_virtual_offset + offset;
431 regs.x.cx = count;
432 __dpmi_int (0x10, &regs);
433 }
434 #endif
435
436 static void
437 dos_direct_output (y, x, buf, len)
438 int y;
439 int x;
440 char *buf;
441 int len;
442 {
443 int t0 = 2 * (x + y * screen_size_X);
444 int t = t0 + (int) ScreenPrimary;
445 int l0 = len;
446
447 #if (__DJGPP__ < 2)
448 while (--len >= 0) {
449 dosmemput (buf++, 1, t);
450 t += 2;
451 }
452 #else
453 /* This is faster. */
454 for (_farsetsel (_dos_ds); --len >= 0; t += 2, buf++)
455 _farnspokeb (t, *buf);
456
457 if (screen_virtual_segment)
458 dosv_refresh_virtual_screen (t0, l0);
459 #endif
460 }
461 #endif
462
463 /* Flash the screen as a substitute for BEEPs. */
464
465 #if (__DJGPP__ < 2)
466 static void
467 do_visible_bell (xorattr)
468 unsigned char xorattr;
469 {
470 asm volatile
471 (" movb $1,%%dl
472 visible_bell_0:
473 movl _ScreenPrimary,%%eax
474 call dosmemsetup
475 movl %%eax,%%ebx
476 movl %1,%%ecx
477 movb %0,%%al
478 incl %%ebx
479 visible_bell_1:
480 xorb %%al,%%gs:(%%ebx)
481 addl $2,%%ebx
482 decl %%ecx
483 jne visible_bell_1
484 decb %%dl
485 jne visible_bell_3
486 visible_bell_2:
487 movzwl %%ax,%%eax
488 movzwl %%ax,%%eax
489 movzwl %%ax,%%eax
490 movzwl %%ax,%%eax
491 decw %%cx
492 jne visible_bell_2
493 jmp visible_bell_0
494 visible_bell_3:"
495 : /* no output */
496 : "m" (xorattr), "g" (screen_size)
497 : "%eax", "%ebx", /* "%gs",*/ "%ecx", "%edx");
498 }
499
500 static void
501 ScreenVisualBell (void)
502 {
503 /* This creates an xor-mask that will swap the default fore- and
504 background colors. */
505 do_visible_bell (((the_only_x_display.foreground_pixel
506 ^ the_only_x_display.background_pixel)
507 * 0x11) & 0x7f);
508 }
509 #endif
510
511 #ifndef HAVE_X_WINDOWS
512
513 static int blink_bit = -1; /* the state of the blink bit at startup */
514
515 /* Enable bright background colors. */
516 static void
517 bright_bg (void)
518 {
519 union REGS regs;
520
521 /* Remember the original state of the blink/bright-background bit.
522 It is stored at 0040:0065h in the BIOS data area. */
523 if (blink_bit == -1)
524 blink_bit = (_farpeekb (_dos_ds, 0x465) & 0x20) == 0x20;
525
526 regs.h.bl = 0;
527 regs.x.ax = 0x1003;
528 int86 (0x10, &regs, &regs);
529 }
530
531 /* Disable bright background colors (and enable blinking) if we found
532 the video system in that state at startup. */
533 static void
534 maybe_enable_blinking (void)
535 {
536 if (blink_bit == 1)
537 {
538 union REGS regs;
539
540 regs.h.bl = 1;
541 regs.x.ax = 0x1003;
542 int86 (0x10, &regs, &regs);
543 }
544 }
545
546 /* Return non-zero if the system has a VGA adapter. */
547 static int
548 vga_installed (void)
549 {
550 union REGS regs;
551
552 regs.x.ax = 0x1a00;
553 int86 (0x10, &regs, &regs);
554 if (regs.h.al == 0x1a && regs.h.bl > 5 && regs.h.bl < 13)
555 return 1;
556 return 0;
557 }
558
559 /* Set the screen dimensions so that it can show no less than
560 ROWS x COLS frame. */
561
562 void
563 dos_set_window_size (rows, cols)
564 int *rows, *cols;
565 {
566 char video_name[30];
567 Lisp_Object video_mode;
568 int video_mode_value;
569 int have_vga = 0;
570 union REGS regs;
571 int current_rows = ScreenRows (), current_cols = ScreenCols ();
572
573 if (*rows == current_rows && *cols == current_cols)
574 return;
575
576 mouse_off ();
577 have_vga = vga_installed ();
578
579 /* If the user specified a special video mode for these dimensions,
580 use that mode. */
581 sprintf (video_name, "screen-dimensions-%dx%d", *rows, *cols);
582 video_mode = XSYMBOL (Fintern_soft (build_string (video_name),
583 Qnil))-> value;
584
585 if (INTEGERP (video_mode)
586 && (video_mode_value = XINT (video_mode)) > 0)
587 {
588 regs.x.ax = video_mode_value;
589 int86 (0x10, &regs, &regs);
590
591 if (have_mouse)
592 {
593 /* Must hardware-reset the mouse, or else it won't update
594 its notion of screen dimensions for some non-standard
595 video modes. This is *painfully* slow... */
596 regs.x.ax = 0;
597 int86 (0x33, &regs, &regs);
598 }
599 }
600
601 /* Find one of the dimensions supported by standard EGA/VGA
602 which gives us at least the required dimensions. */
603
604 #if __DJGPP__ > 1
605
606 else
607 {
608 static struct {
609 int rows;
610 int need_vga;
611 } std_dimension[] = {
612 {25, 0},
613 {28, 1},
614 {35, 0},
615 {40, 1},
616 {43, 0},
617 {50, 1}
618 };
619 int i = 0;
620
621 while (i < sizeof (std_dimension) / sizeof (std_dimension[0]))
622 {
623 if (std_dimension[i].need_vga <= have_vga
624 && std_dimension[i].rows >= *rows)
625 {
626 if (std_dimension[i].rows != current_rows
627 || *cols != current_cols)
628 _set_screen_lines (std_dimension[i].rows);
629 break;
630 }
631 i++;
632 }
633 }
634
635 #else /* not __DJGPP__ > 1 */
636
637 else if (*rows <= 25)
638 {
639 if (current_rows != 25 || current_cols != 80)
640 {
641 regs.x.ax = 3;
642 int86 (0x10, &regs, &regs);
643 regs.x.ax = 0x1101;
644 regs.h.bl = 0;
645 int86 (0x10, &regs, &regs);
646 regs.x.ax = 0x1200;
647 regs.h.bl = 32;
648 int86 (0x10, &regs, &regs);
649 regs.x.ax = 3;
650 int86 (0x10, &regs, &regs);
651 }
652 }
653 else if (*rows <= 50)
654 if (have_vga && (current_rows != 50 || current_cols != 80)
655 || *rows <= 43 && (current_rows != 43 || current_cols != 80))
656 {
657 regs.x.ax = 3;
658 int86 (0x10, &regs, &regs);
659 regs.x.ax = 0x1112;
660 regs.h.bl = 0;
661 int86 (0x10, &regs, &regs);
662 regs.x.ax = 0x1200;
663 regs.h.bl = 32;
664 int86 (0x10, &regs, &regs);
665 regs.x.ax = 0x0100;
666 regs.x.cx = 7;
667 int86 (0x10, &regs, &regs);
668 }
669 #endif /* not __DJGPP__ > 1 */
670
671 if (have_mouse)
672 {
673 mouse_init ();
674 mouse_on ();
675 }
676
677 /* Tell the caller what dimensions have been REALLY set. */
678 *rows = ScreenRows ();
679 *cols = ScreenCols ();
680
681 /* Update Emacs' notion of screen dimensions. */
682 screen_size_X = *cols;
683 screen_size_Y = *rows;
684 screen_size = *cols * *rows;
685
686 #if __DJGPP__ > 1
687 /* If the dimensions changed, the mouse highlight info is invalid. */
688 if (current_rows != *rows || current_cols != *cols)
689 {
690 struct frame *f = SELECTED_FRAME();
691 struct display_info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
692 Lisp_Object window = dpyinfo->mouse_face_window;
693
694 if (! NILP (window) && XFRAME (XWINDOW (window)->frame) == f)
695 {
696 dpyinfo->mouse_face_beg_row = dpyinfo->mouse_face_beg_col = -1;
697 dpyinfo->mouse_face_end_row = dpyinfo->mouse_face_end_col = -1;
698 dpyinfo->mouse_face_window = Qnil;
699 }
700 }
701 #endif
702
703 /* Enable bright background colors. */
704 bright_bg ();
705
706 /* FIXME: I'm not sure the above will run at all on DOS/V. But let's
707 be defensive anyway. */
708 if (screen_virtual_segment)
709 dosv_refresh_virtual_screen (0, *cols * *rows);
710 }
711
712 /* If we write a character in the position where the mouse is,
713 the mouse cursor may need to be refreshed. */
714
715 static void
716 mouse_off_maybe ()
717 {
718 int x, y;
719
720 if (!mouse_visible)
721 return;
722
723 mouse_get_xy (&x, &y);
724 if (y != new_pos_Y || x < new_pos_X)
725 return;
726
727 mouse_off ();
728 }
729
730 #define DEFAULT_CURSOR_START (-1)
731 #define DEFAULT_CURSOR_WIDTH (-1)
732 #define BOX_CURSOR_WIDTH (-32)
733
734 /* Set cursor to begin at scan line START_LINE in the character cell
735 and extend for WIDTH scan lines. Scan lines are counted from top
736 of the character cell, starting from zero. */
737 static void
738 msdos_set_cursor_shape (struct frame *f, int start_line, int width)
739 {
740 #if __DJGPP__ > 1
741 unsigned desired_cursor;
742 __dpmi_regs regs;
743 int max_line, top_line, bot_line;
744
745 /* Avoid the costly BIOS call if F isn't the currently selected
746 frame. Allow for NULL as unconditionally meaning the selected
747 frame. */
748 if (f && f != SELECTED_FRAME())
749 return;
750
751 /* The character cell size in scan lines is stored at 40:85 in the
752 BIOS data area. */
753 max_line = _farpeekw (_dos_ds, 0x485) - 1;
754 switch (max_line)
755 {
756 default: /* this relies on CGA cursor emulation being ON! */
757 case 7:
758 bot_line = 7;
759 break;
760 case 9:
761 bot_line = 9;
762 break;
763 case 13:
764 bot_line = 12;
765 break;
766 case 15:
767 bot_line = 14;
768 break;
769 }
770
771 if (width < 0)
772 {
773 if (width == BOX_CURSOR_WIDTH)
774 {
775 top_line = 0;
776 bot_line = max_line;
777 }
778 else if (start_line != DEFAULT_CURSOR_START)
779 {
780 top_line = start_line;
781 bot_line = top_line - width - 1;
782 }
783 else if (width != DEFAULT_CURSOR_WIDTH)
784 {
785 top_line = 0;
786 bot_line = -1 - width;
787 }
788 else
789 top_line = bot_line + 1;
790 }
791 else if (width == 0)
792 {
793 /* [31, 0] seems to DTRT for all screen sizes. */
794 top_line = 31;
795 bot_line = 0;
796 }
797 else /* WIDTH is positive */
798 {
799 if (start_line != DEFAULT_CURSOR_START)
800 bot_line = start_line;
801 top_line = bot_line - (width - 1);
802 }
803
804 /* If the current cursor shape is already what they want, we are
805 history here. */
806 desired_cursor = ((top_line & 0x1f) << 8) | (bot_line & 0x1f);
807 if (desired_cursor == _farpeekw (_dos_ds, 0x460))
808 return;
809
810 regs.h.ah = 1;
811 regs.x.cx = desired_cursor;
812 __dpmi_int (0x10, &regs);
813 #endif /* __DJGPP__ > 1 */
814 }
815
816 static void
817 IT_set_cursor_type (struct frame *f, Lisp_Object cursor_type)
818 {
819 if (EQ (cursor_type, Qbar))
820 {
821 /* Just BAR means the normal EGA/VGA cursor. */
822 msdos_set_cursor_shape (f, DEFAULT_CURSOR_START, DEFAULT_CURSOR_WIDTH);
823 }
824 else if (CONSP (cursor_type) && EQ (XCAR (cursor_type), Qbar))
825 {
826 Lisp_Object bar_parms = XCDR (cursor_type);
827 int width;
828
829 if (INTEGERP (bar_parms))
830 {
831 /* Feature: negative WIDTH means cursor at the top
832 of the character cell, zero means invisible cursor. */
833 width = XINT (bar_parms);
834 msdos_set_cursor_shape (f, width >= 0 ? DEFAULT_CURSOR_START : 0,
835 width);
836 }
837 else if (CONSP (bar_parms)
838 && INTEGERP (XCAR (bar_parms))
839 && INTEGERP (XCDR (bar_parms)))
840 {
841 int start_line = XINT (XCDR (bar_parms));
842
843 width = XINT (XCAR (bar_parms));
844 msdos_set_cursor_shape (f, start_line, width);
845 }
846 }
847 else
848 /* Treat anything unknown as "box cursor". This includes nil, so
849 that a frame which doesn't specify a cursor type gets a box,
850 which is the default in Emacs. */
851 msdos_set_cursor_shape (f, 0, BOX_CURSOR_WIDTH);
852 }
853
854 static void
855 IT_ring_bell (void)
856 {
857 if (visible_bell)
858 {
859 mouse_off ();
860 ScreenVisualBell ();
861 }
862 else
863 {
864 union REGS inregs, outregs;
865 inregs.h.ah = 2;
866 inregs.h.dl = 7;
867 intdos (&inregs, &outregs);
868 }
869 }
870
871 /* Given a face id FACE, extract the face parameters to be used for
872 display until the face changes. The face parameters (actually, its
873 color) are used to construct the video attribute byte for each
874 glyph during the construction of the buffer that is then blitted to
875 the video RAM. */
876 static void
877 IT_set_face (int face)
878 {
879 struct frame *sf = SELECTED_FRAME();
880 struct face *fp = FACE_FROM_ID (sf, face);
881 struct face *dfp = FACE_FROM_ID (sf, DEFAULT_FACE_ID);
882 unsigned long fg, bg, dflt_fg, dflt_bg;
883
884 if (!fp)
885 {
886 fp = dfp;
887 /* The default face for the frame should always be realized and
888 cached. */
889 if (!fp)
890 abort ();
891 }
892 screen_face = face;
893 fg = fp->foreground;
894 bg = fp->background;
895 dflt_fg = dfp->foreground;
896 dflt_bg = dfp->background;
897
898 /* Don't use invalid colors. In particular, FACE_TTY_DEFAULT_*
899 colors mean use the colors of the default face, except that if
900 highlight is on, invert the foreground and the background. Note
901 that we assume all 16 colors to be available for the background,
902 since Emacs switches on this mode (and loses the blinking
903 attribute) at startup. */
904 if (fg == FACE_TTY_DEFAULT_COLOR || fg == FACE_TTY_DEFAULT_FG_COLOR)
905 fg = FRAME_FOREGROUND_PIXEL (sf);
906 else if (fg == FACE_TTY_DEFAULT_BG_COLOR)
907 fg = FRAME_BACKGROUND_PIXEL (sf);
908 if (bg == FACE_TTY_DEFAULT_COLOR || bg == FACE_TTY_DEFAULT_BG_COLOR)
909 bg = FRAME_BACKGROUND_PIXEL (sf);
910 else if (bg == FACE_TTY_DEFAULT_FG_COLOR)
911 bg = FRAME_FOREGROUND_PIXEL (sf);
912
913 /* Make sure highlighted lines really stand out, come what may. */
914 if ((highlight || fp->tty_reverse_p)
915 && (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%s: %d/%d[FG:%d/BG:%d]>", face,
932 highlight ? "H" : "", 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 bitmap areas 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 IT_set_face (0);
1805 mouse_off ();
1806 ScreenClear ();
1807 if (screen_virtual_segment)
1808 dosv_refresh_virtual_screen (0, screen_size);
1809 new_pos_X = new_pos_Y = 0;
1810 }
1811
1812 static void
1813 IT_clear_to_end (void)
1814 {
1815 if (termscript)
1816 fprintf (termscript, "<CLR:EOS>");
1817
1818 while (new_pos_Y < screen_size_Y) {
1819 new_pos_X = 0;
1820 IT_clear_end_of_line (screen_size_X);
1821 new_pos_Y++;
1822 }
1823 }
1824
1825 static void
1826 IT_cursor_to (int y, int x)
1827 {
1828 if (termscript)
1829 fprintf (termscript, "\n<XY=%dx%d>", x, y);
1830 new_pos_X = x;
1831 new_pos_Y = y;
1832 }
1833
1834 static int cursor_cleared;
1835
1836 static void
1837 IT_display_cursor (int on)
1838 {
1839 if (on && cursor_cleared)
1840 {
1841 ScreenSetCursor (current_pos_Y, current_pos_X);
1842 cursor_cleared = 0;
1843 }
1844 else if (!on && !cursor_cleared)
1845 {
1846 ScreenSetCursor (-1, -1);
1847 cursor_cleared = 1;
1848 }
1849 }
1850
1851 /* Emacs calls cursor-movement functions a lot when it updates the
1852 display (probably a legacy of old terminals where you cannot
1853 update a screen line without first moving the cursor there).
1854 However, cursor movement is expensive on MSDOS (it calls a slow
1855 BIOS function and requires 2 mode switches), while actual screen
1856 updates access the video memory directly and don't depend on
1857 cursor position. To avoid slowing down the redisplay, we cheat:
1858 all functions that move the cursor only set internal variables
1859 which record the cursor position, whereas the cursor is only
1860 moved to its final position whenever screen update is complete.
1861
1862 `IT_cmgoto' is called from the keyboard reading loop and when the
1863 frame update is complete. This means that we are ready for user
1864 input, so we update the cursor position to show where the point is,
1865 and also make the mouse pointer visible.
1866
1867 Special treatment is required when the cursor is in the echo area,
1868 to put the cursor at the end of the text displayed there. */
1869
1870 static void
1871 IT_cmgoto (FRAME_PTR f)
1872 {
1873 /* Only set the cursor to where it should be if the display is
1874 already in sync with the window contents. */
1875 int update_cursor_pos = 1; /* MODIFF == unchanged_modified; */
1876
1877 /* FIXME: This needs to be rewritten for the new redisplay, or
1878 removed. */
1879 #if 0
1880 static int previous_pos_X = -1;
1881
1882 update_cursor_pos = 1; /* temporary!!! */
1883
1884 /* If the display is in sync, forget any previous knowledge about
1885 cursor position. This is primarily for unexpected events like
1886 C-g in the minibuffer. */
1887 if (update_cursor_pos && previous_pos_X >= 0)
1888 previous_pos_X = -1;
1889 /* If we are in the echo area, put the cursor at the
1890 end of the echo area message. */
1891 if (!update_cursor_pos
1892 && XFASTINT (XWINDOW (FRAME_MINIBUF_WINDOW (f))->top) <= new_pos_Y)
1893 {
1894 int tem_X = current_pos_X, dummy;
1895
1896 if (echo_area_glyphs)
1897 {
1898 tem_X = echo_area_glyphs_length;
1899 /* Save current cursor position, to be restored after the
1900 echo area message is erased. Only remember one level
1901 of previous cursor position. */
1902 if (previous_pos_X == -1)
1903 ScreenGetCursor (&dummy, &previous_pos_X);
1904 }
1905 else if (previous_pos_X >= 0)
1906 {
1907 /* We wind up here after the echo area message is erased.
1908 Restore the cursor position we remembered above. */
1909 tem_X = previous_pos_X;
1910 previous_pos_X = -1;
1911 }
1912
1913 if (current_pos_X != tem_X)
1914 {
1915 new_pos_X = tem_X;
1916 update_cursor_pos = 1;
1917 }
1918 }
1919 #endif
1920
1921 if (update_cursor_pos
1922 && (current_pos_X != new_pos_X || current_pos_Y != new_pos_Y))
1923 {
1924 ScreenSetCursor (current_pos_Y = new_pos_Y, current_pos_X = new_pos_X);
1925 if (termscript)
1926 fprintf (termscript, "\n<CURSOR:%dx%d>", current_pos_X, current_pos_Y);
1927 }
1928
1929 /* Maybe cursor is invisible, so make it visible. */
1930 IT_display_cursor (1);
1931
1932 /* Mouse pointer should be always visible if we are waiting for
1933 keyboard input. */
1934 if (!mouse_visible)
1935 mouse_on ();
1936 }
1937
1938 static void
1939 IT_reassert_line_highlight (int new, int vpos)
1940 {
1941 highlight = new;
1942 }
1943
1944 static void
1945 IT_change_line_highlight (int new_highlight, int y, int vpos, int first_unused_hpos)
1946 {
1947 highlight = new_highlight;
1948 IT_cursor_to (vpos, 0);
1949 IT_clear_end_of_line (first_unused_hpos);
1950 }
1951
1952 static void
1953 IT_update_begin (struct frame *f)
1954 {
1955 struct display_info *display_info = FRAME_X_DISPLAY_INFO (f);
1956 struct frame *mouse_face_frame = display_info->mouse_face_mouse_frame;
1957
1958 highlight = 0;
1959
1960 BLOCK_INPUT;
1961
1962 if (f && f == mouse_face_frame)
1963 {
1964 /* Don't do highlighting for mouse motion during the update. */
1965 display_info->mouse_face_defer = 1;
1966
1967 /* If F needs to be redrawn, simply forget about any prior mouse
1968 highlighting. */
1969 if (FRAME_GARBAGED_P (f))
1970 display_info->mouse_face_window = Qnil;
1971
1972 /* Can we tell that this update does not affect the window
1973 where the mouse highlight is? If so, no need to turn off.
1974 Likewise, don't do anything if none of the enabled rows
1975 contains glyphs highlighted in mouse face. */
1976 if (!NILP (display_info->mouse_face_window)
1977 && WINDOWP (display_info->mouse_face_window))
1978 {
1979 struct window *w = XWINDOW (display_info->mouse_face_window);
1980 int i;
1981
1982 /* If the mouse highlight is in the window that was deleted
1983 (e.g., if it was popped by completion), clear highlight
1984 unconditionally. */
1985 if (NILP (w->buffer))
1986 display_info->mouse_face_window = Qnil;
1987 else
1988 {
1989 for (i = 0; i < w->desired_matrix->nrows; ++i)
1990 if (MATRIX_ROW_ENABLED_P (w->desired_matrix, i)
1991 && MATRIX_ROW (w->current_matrix, i)->mouse_face_p)
1992 break;
1993 }
1994
1995 if (NILP (w->buffer) || i < w->desired_matrix->nrows)
1996 clear_mouse_face (display_info);
1997 }
1998 }
1999 else if (mouse_face_frame && !FRAME_LIVE_P (mouse_face_frame))
2000 {
2001 /* If the frame with mouse highlight was deleted, invalidate the
2002 highlight info. */
2003 display_info->mouse_face_beg_row = display_info->mouse_face_beg_col = -1;
2004 display_info->mouse_face_end_row = display_info->mouse_face_end_col = -1;
2005 display_info->mouse_face_window = Qnil;
2006 display_info->mouse_face_deferred_gc = 0;
2007 display_info->mouse_face_mouse_frame = NULL;
2008 }
2009
2010 UNBLOCK_INPUT;
2011 }
2012
2013 static void
2014 IT_update_end (struct frame *f)
2015 {
2016 highlight = 0;
2017 FRAME_X_DISPLAY_INFO (f)->mouse_face_defer = 0;
2018 }
2019
2020 Lisp_Object Qcursor_type;
2021
2022 static void
2023 IT_frame_up_to_date (struct frame *f)
2024 {
2025 struct display_info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
2026 Lisp_Object new_cursor, frame_desired_cursor;
2027 struct window *sw;
2028
2029 if (dpyinfo->mouse_face_deferred_gc
2030 || (f && f == dpyinfo->mouse_face_mouse_frame))
2031 {
2032 BLOCK_INPUT;
2033 if (dpyinfo->mouse_face_mouse_frame)
2034 IT_note_mouse_highlight (dpyinfo->mouse_face_mouse_frame,
2035 dpyinfo->mouse_face_mouse_x,
2036 dpyinfo->mouse_face_mouse_y);
2037 dpyinfo->mouse_face_deferred_gc = 0;
2038 UNBLOCK_INPUT;
2039 }
2040
2041 /* Set the cursor type to whatever they wanted. In a minibuffer
2042 window, we want the cursor to appear only if we are reading input
2043 from this window, and we want the cursor to be taken from the
2044 frame parameters. For the selected window, we use either its
2045 buffer-local value or the value from the frame parameters if the
2046 buffer doesn't define its local value for the cursor type. */
2047 sw = XWINDOW (f->selected_window);
2048 frame_desired_cursor = Fcdr (Fassq (Qcursor_type, f->param_alist));
2049 if (cursor_in_echo_area
2050 && FRAME_HAS_MINIBUF_P (f)
2051 && EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window)
2052 && sw == XWINDOW (echo_area_window))
2053 new_cursor = frame_desired_cursor;
2054 else
2055 {
2056 struct buffer *b = XBUFFER (sw->buffer);
2057
2058 if (EQ (b->cursor_type, Qt))
2059 new_cursor = frame_desired_cursor;
2060 else if (NILP (b->cursor_type)) /* nil means no cursor */
2061 new_cursor = Fcons (Qbar, make_number (0));
2062 else
2063 new_cursor = b->cursor_type;
2064 }
2065
2066 IT_set_cursor_type (f, new_cursor);
2067
2068 IT_cmgoto (f); /* position cursor when update is done */
2069 }
2070
2071 /* Copy LEN glyphs displayed on a single line whose vertical position
2072 is YPOS, beginning at horizontal position XFROM to horizontal
2073 position XTO, by moving blocks in the video memory. Used by
2074 functions that insert and delete glyphs. */
2075 static void
2076 IT_copy_glyphs (int xfrom, int xto, size_t len, int ypos)
2077 {
2078 /* The offsets of source and destination relative to the
2079 conventional memorty selector. */
2080 int from = 2 * (xfrom + screen_size_X * ypos) + ScreenPrimary;
2081 int to = 2 * (xto + screen_size_X * ypos) + ScreenPrimary;
2082
2083 if (from == to || len <= 0)
2084 return;
2085
2086 _farsetsel (_dos_ds);
2087
2088 /* The source and destination might overlap, so we need to move
2089 glyphs non-destructively. */
2090 if (from > to)
2091 {
2092 for ( ; len; from += 2, to += 2, len--)
2093 _farnspokew (to, _farnspeekw (from));
2094 }
2095 else
2096 {
2097 from += (len - 1) * 2;
2098 to += (len - 1) * 2;
2099 for ( ; len; from -= 2, to -= 2, len--)
2100 _farnspokew (to, _farnspeekw (from));
2101 }
2102 if (screen_virtual_segment)
2103 dosv_refresh_virtual_screen (ypos * screen_size_X * 2, screen_size_X);
2104 }
2105
2106 /* Insert and delete glyphs. */
2107 static void
2108 IT_insert_glyphs (start, len)
2109 register struct glyph *start;
2110 register int len;
2111 {
2112 int shift_by_width = screen_size_X - (new_pos_X + len);
2113
2114 /* Shift right the glyphs from the nominal cursor position to the
2115 end of this line. */
2116 IT_copy_glyphs (new_pos_X, new_pos_X + len, shift_by_width, new_pos_Y);
2117
2118 /* Now write the glyphs to be inserted. */
2119 IT_write_glyphs (start, len);
2120 }
2121
2122 static void
2123 IT_delete_glyphs (n)
2124 register int n;
2125 {
2126 abort ();
2127 }
2128
2129 /* set-window-configuration on window.c needs this. */
2130 void
2131 x_set_menu_bar_lines (f, value, oldval)
2132 struct frame *f;
2133 Lisp_Object value, oldval;
2134 {
2135 set_menu_bar_lines (f, value, oldval);
2136 }
2137
2138 /* This was copied from xfaces.c */
2139
2140 extern Lisp_Object Qbackground_color;
2141 extern Lisp_Object Qforeground_color;
2142 Lisp_Object Qreverse;
2143 extern Lisp_Object Qtitle;
2144
2145 /* IT_set_terminal_modes is called when emacs is started,
2146 resumed, and whenever the screen is redrawn! */
2147
2148 static void
2149 IT_set_terminal_modes (void)
2150 {
2151 if (termscript)
2152 fprintf (termscript, "\n<SET_TERM>");
2153 highlight = 0;
2154
2155 screen_size_X = ScreenCols ();
2156 screen_size_Y = ScreenRows ();
2157 screen_size = screen_size_X * screen_size_Y;
2158
2159 new_pos_X = new_pos_Y = 0;
2160 current_pos_X = current_pos_Y = -1;
2161
2162 if (term_setup_done)
2163 return;
2164 term_setup_done = 1;
2165
2166 startup_screen_size_X = screen_size_X;
2167 startup_screen_size_Y = screen_size_Y;
2168 startup_screen_attrib = ScreenAttrib;
2169
2170 #if __DJGPP__ > 1
2171 /* Is DOS/V (or any other RSIS software which relocates
2172 the screen) installed? */
2173 {
2174 unsigned short es_value;
2175 __dpmi_regs regs;
2176
2177 regs.h.ah = 0xfe; /* get relocated screen address */
2178 if (ScreenPrimary == 0xb0000UL || ScreenPrimary == 0xb8000UL)
2179 regs.x.es = (ScreenPrimary >> 4) & 0xffff;
2180 else if (screen_old_address) /* already switched to Japanese mode once */
2181 regs.x.es = (screen_old_address >> 4) & 0xffff;
2182 else
2183 regs.x.es = ScreenMode () == 7 ? 0xb000 : 0xb800;
2184 regs.x.di = 0;
2185 es_value = regs.x.es;
2186 __dpmi_int (0x10, &regs);
2187
2188 if (regs.x.es != es_value)
2189 {
2190 /* screen_old_address is only set if ScreenPrimary does NOT
2191 already point to the relocated buffer address returned by
2192 the Int 10h/AX=FEh call above. DJGPP v2.02 and later sets
2193 ScreenPrimary to that address at startup under DOS/V. */
2194 if (regs.x.es != (ScreenPrimary >> 4) & 0xffff)
2195 screen_old_address = ScreenPrimary;
2196 screen_virtual_segment = regs.x.es;
2197 screen_virtual_offset = regs.x.di;
2198 ScreenPrimary = (screen_virtual_segment << 4) + screen_virtual_offset;
2199 }
2200 }
2201 #endif /* __DJGPP__ > 1 */
2202
2203 ScreenGetCursor (&startup_pos_Y, &startup_pos_X);
2204 ScreenRetrieve (startup_screen_buffer = xmalloc (screen_size * 2));
2205
2206 if (termscript)
2207 fprintf (termscript, "<SCREEN SAVED (dimensions=%dx%d)>\n",
2208 screen_size_X, screen_size_Y);
2209
2210 bright_bg ();
2211 }
2212
2213 /* IT_reset_terminal_modes is called when emacs is
2214 suspended or killed. */
2215
2216 static void
2217 IT_reset_terminal_modes (void)
2218 {
2219 int display_row_start = (int) ScreenPrimary;
2220 int saved_row_len = startup_screen_size_X * 2;
2221 int update_row_len = ScreenCols () * 2;
2222 int current_rows = ScreenRows ();
2223 int to_next_row = update_row_len;
2224 unsigned char *saved_row = startup_screen_buffer;
2225 int cursor_pos_X = ScreenCols () - 1;
2226 int cursor_pos_Y = ScreenRows () - 1;
2227
2228 if (termscript)
2229 fprintf (termscript, "\n<RESET_TERM>");
2230
2231 highlight = 0;
2232
2233 if (!term_setup_done)
2234 return;
2235
2236 mouse_off ();
2237
2238 /* Leave the video system in the same state as we found it,
2239 as far as the blink/bright-background bit is concerned. */
2240 maybe_enable_blinking ();
2241
2242 /* We have a situation here.
2243 We cannot just do ScreenUpdate(startup_screen_buffer) because
2244 the luser could have changed screen dimensions inside Emacs
2245 and failed (or didn't want) to restore them before killing
2246 Emacs. ScreenUpdate() uses the *current* screen dimensions and
2247 thus will happily use memory outside what was allocated for
2248 `startup_screen_buffer'.
2249 Thus we only restore as much as the current screen dimensions
2250 can hold, and clear the rest (if the saved screen is smaller than
2251 the current) with the color attribute saved at startup. The cursor
2252 is also restored within the visible dimensions. */
2253
2254 ScreenAttrib = startup_screen_attrib;
2255
2256 /* Don't restore the screen if we are exiting less than 2 seconds
2257 after startup: we might be crashing, and the screen might show
2258 some vital clues to what's wrong. */
2259 if (clock () - startup_time >= 2*CLOCKS_PER_SEC)
2260 {
2261 ScreenClear ();
2262 if (screen_virtual_segment)
2263 dosv_refresh_virtual_screen (0, screen_size);
2264
2265 if (update_row_len > saved_row_len)
2266 update_row_len = saved_row_len;
2267 if (current_rows > startup_screen_size_Y)
2268 current_rows = startup_screen_size_Y;
2269
2270 if (termscript)
2271 fprintf (termscript, "<SCREEN RESTORED (dimensions=%dx%d)>\n",
2272 update_row_len / 2, current_rows);
2273
2274 while (current_rows--)
2275 {
2276 dosmemput (saved_row, update_row_len, display_row_start);
2277 if (screen_virtual_segment)
2278 dosv_refresh_virtual_screen (display_row_start - ScreenPrimary,
2279 update_row_len / 2);
2280 saved_row += saved_row_len;
2281 display_row_start += to_next_row;
2282 }
2283 }
2284 if (startup_pos_X < cursor_pos_X)
2285 cursor_pos_X = startup_pos_X;
2286 if (startup_pos_Y < cursor_pos_Y)
2287 cursor_pos_Y = startup_pos_Y;
2288
2289 ScreenSetCursor (cursor_pos_Y, cursor_pos_X);
2290 xfree (startup_screen_buffer);
2291
2292 term_setup_done = 0;
2293 }
2294
2295 static void
2296 IT_set_terminal_window (int foo)
2297 {
2298 }
2299
2300 /* Remember the screen colors of the curent frame, to serve as the
2301 default colors for newly-created frames. */
2302
2303 static int initial_screen_colors[2];
2304
2305 DEFUN ("msdos-remember-default-colors", Fmsdos_remember_default_colors,
2306 Smsdos_remember_default_colors, 1, 1, 0,
2307 "Remember the screen colors of the current frame.")
2308 (frame)
2309 Lisp_Object frame;
2310 {
2311 struct frame *f;
2312
2313 CHECK_FRAME (frame, 0);
2314 f= XFRAME (frame);
2315
2316 /* This function is called after applying default-frame-alist to the
2317 initial frame. At that time, if reverse-colors option was
2318 specified in default-frame-alist, it was already applied, and
2319 frame colors are reversed. We need to account for that. */
2320 if (EQ (Fcdr (Fassq (Qreverse, f->param_alist)), Qt))
2321 {
2322 initial_screen_colors[0] = FRAME_BACKGROUND_PIXEL (f);
2323 initial_screen_colors[1] = FRAME_FOREGROUND_PIXEL (f);
2324 }
2325 else
2326 {
2327 initial_screen_colors[0] = FRAME_FOREGROUND_PIXEL (f);
2328 initial_screen_colors[1] = FRAME_BACKGROUND_PIXEL (f);
2329 }
2330 }
2331
2332 void
2333 IT_set_frame_parameters (f, alist)
2334 struct frame *f;
2335 Lisp_Object alist;
2336 {
2337 Lisp_Object tail;
2338 int length = XINT (Flength (alist));
2339 int i, j;
2340 Lisp_Object *parms
2341 = (Lisp_Object *) alloca (length * sizeof (Lisp_Object));
2342 Lisp_Object *values
2343 = (Lisp_Object *) alloca (length * sizeof (Lisp_Object));
2344 /* Do we have to reverse the foreground and background colors? */
2345 int reverse = EQ (Fcdr (Fassq (Qreverse, f->param_alist)), Qt);
2346 int was_reverse = reverse;
2347 int redraw = 0, fg_set = 0, bg_set = 0;
2348 int need_to_reverse;
2349 unsigned long orig_fg;
2350 unsigned long orig_bg;
2351 Lisp_Object frame_bg, frame_fg;
2352 extern Lisp_Object Qdefault, QCforeground, QCbackground;
2353
2354 /* If we are creating a new frame, begin with the original screen colors
2355 used for the initial frame. */
2356 if (alist == Vdefault_frame_alist
2357 && initial_screen_colors[0] != -1 && initial_screen_colors[1] != -1)
2358 {
2359 FRAME_FOREGROUND_PIXEL (f) = initial_screen_colors[0];
2360 FRAME_BACKGROUND_PIXEL (f) = initial_screen_colors[1];
2361 }
2362 orig_fg = FRAME_FOREGROUND_PIXEL (f);
2363 orig_bg = FRAME_BACKGROUND_PIXEL (f);
2364 frame_fg = Fcdr (Fassq (Qforeground_color, f->param_alist));
2365 frame_bg = Fcdr (Fassq (Qbackground_color, f->param_alist));
2366 /* frame_fg and frame_bg could be nil if, for example,
2367 f->param_alist is nil, e.g. if we are called from
2368 Fmake_terminal_frame. */
2369 if (NILP (frame_fg))
2370 frame_fg = build_string (unspecified_fg);
2371 if (NILP (frame_bg))
2372 frame_bg = build_string (unspecified_bg);
2373
2374 /* Extract parm names and values into those vectors. */
2375 i = 0;
2376 for (tail = alist; CONSP (tail); tail = Fcdr (tail))
2377 {
2378 Lisp_Object elt;
2379
2380 elt = Fcar (tail);
2381 parms[i] = Fcar (elt);
2382 CHECK_SYMBOL (parms[i], 1);
2383 values[i] = Fcdr (elt);
2384 i++;
2385 }
2386
2387 j = i;
2388
2389 for (i = 0; i < j; i++)
2390 {
2391 Lisp_Object prop, val;
2392
2393 prop = parms[i];
2394 val = values[i];
2395
2396 if (EQ (prop, Qreverse))
2397 reverse = EQ (val, Qt);
2398 }
2399
2400 need_to_reverse = reverse && !was_reverse;
2401 if (termscript && need_to_reverse)
2402 fprintf (termscript, "<INVERSE-VIDEO>\n");
2403
2404 /* Now process the alist elements in reverse of specified order. */
2405 for (i--; i >= 0; i--)
2406 {
2407 Lisp_Object prop, val;
2408 Lisp_Object frame;
2409
2410 prop = parms[i];
2411 val = values[i];
2412
2413 if (EQ (prop, Qforeground_color))
2414 {
2415 unsigned long new_color = load_color (f, NULL, val, need_to_reverse
2416 ? LFACE_BACKGROUND_INDEX
2417 : LFACE_FOREGROUND_INDEX);
2418 if (new_color != FACE_TTY_DEFAULT_COLOR
2419 && new_color != FACE_TTY_DEFAULT_FG_COLOR
2420 && new_color != FACE_TTY_DEFAULT_BG_COLOR)
2421 {
2422 FRAME_FOREGROUND_PIXEL (f) = new_color;
2423 /* Make sure the foreground of the default face for this
2424 frame is changed as well. */
2425 XSETFRAME (frame, f);
2426 if (need_to_reverse)
2427 {
2428 Finternal_set_lisp_face_attribute (Qdefault, QCbackground,
2429 val, frame);
2430 prop = Qbackground_color;
2431 bg_set = 1;
2432 }
2433 else
2434 {
2435 Finternal_set_lisp_face_attribute (Qdefault, QCforeground,
2436 val, frame);
2437 fg_set = 1;
2438 }
2439 redraw = 1;
2440 if (termscript)
2441 fprintf (termscript, "<FGCOLOR %lu>\n", new_color);
2442 }
2443 }
2444 else if (EQ (prop, Qbackground_color))
2445 {
2446 unsigned long new_color = load_color (f, NULL, val, need_to_reverse
2447 ? LFACE_FOREGROUND_INDEX
2448 : LFACE_BACKGROUND_INDEX);
2449 if (new_color != FACE_TTY_DEFAULT_COLOR
2450 && new_color != FACE_TTY_DEFAULT_FG_COLOR
2451 && new_color != FACE_TTY_DEFAULT_BG_COLOR)
2452 {
2453 FRAME_BACKGROUND_PIXEL (f) = new_color;
2454 /* Make sure the background of the default face for this
2455 frame is changed as well. */
2456 XSETFRAME (frame, f);
2457 if (need_to_reverse)
2458 {
2459 Finternal_set_lisp_face_attribute (Qdefault, QCforeground,
2460 val, frame);
2461 prop = Qforeground_color;
2462 fg_set = 1;
2463 }
2464 else
2465 {
2466 Finternal_set_lisp_face_attribute (Qdefault, QCbackground,
2467 val, frame);
2468 bg_set = 1;
2469 }
2470 redraw = 1;
2471 if (termscript)
2472 fprintf (termscript, "<BGCOLOR %lu>\n", new_color);
2473 }
2474 }
2475 else if (EQ (prop, Qtitle))
2476 {
2477 x_set_title (f, val);
2478 if (termscript)
2479 fprintf (termscript, "<TITLE: %s>\n", XSTRING (val)->data);
2480 }
2481 else if (EQ (prop, Qcursor_type))
2482 {
2483 IT_set_cursor_type (f, val);
2484 if (termscript)
2485 fprintf (termscript, "<CTYPE: %s>\n",
2486 EQ (val, Qbar) || CONSP (val) && EQ (XCAR (val), Qbar)
2487 ? "bar" : "box");
2488 }
2489 store_frame_param (f, prop, val);
2490 }
2491
2492 /* If they specified "reverse", but not the colors, we need to swap
2493 the current frame colors. */
2494 if (need_to_reverse)
2495 {
2496 Lisp_Object frame;
2497
2498 if (!fg_set)
2499 {
2500 XSETFRAME (frame, f);
2501 Finternal_set_lisp_face_attribute (Qdefault, QCforeground,
2502 tty_color_name (f, orig_bg),
2503 frame);
2504 redraw = 1;
2505 }
2506 if (!bg_set)
2507 {
2508 XSETFRAME (frame, f);
2509 Finternal_set_lisp_face_attribute (Qdefault, QCbackground,
2510 tty_color_name (f, orig_fg),
2511 frame);
2512 redraw = 1;
2513 }
2514 }
2515
2516 if (redraw)
2517 {
2518 face_change_count++; /* forces xdisp.c to recompute basic faces */
2519 if (f == SELECTED_FRAME())
2520 redraw_frame (f);
2521 }
2522 }
2523
2524 extern void init_frame_faces (FRAME_PTR);
2525
2526 #endif /* !HAVE_X_WINDOWS */
2527
2528
2529 /* Do we need the internal terminal? */
2530
2531 void
2532 internal_terminal_init ()
2533 {
2534 char *term = getenv ("TERM");
2535 char *colors;
2536 struct frame *sf = SELECTED_FRAME();
2537
2538 #ifdef HAVE_X_WINDOWS
2539 if (!inhibit_window_system)
2540 return;
2541 #endif
2542
2543 internal_terminal
2544 = (!noninteractive) && term && !strcmp (term, "internal");
2545
2546 if (getenv ("EMACSTEST"))
2547 termscript = fopen (getenv ("EMACSTEST"), "wt");
2548
2549 #ifndef HAVE_X_WINDOWS
2550 if (!internal_terminal || inhibit_window_system)
2551 {
2552 sf->output_method = output_termcap;
2553 return;
2554 }
2555
2556 Vwindow_system = intern ("pc");
2557 Vwindow_system_version = make_number (1);
2558 sf->output_method = output_msdos_raw;
2559
2560 /* If Emacs was dumped on DOS/V machine, forget the stale VRAM address. */
2561 screen_old_address = 0;
2562
2563 /* Forget the stale screen colors as well. */
2564 initial_screen_colors[0] = initial_screen_colors[1] = -1;
2565
2566 bzero (&the_only_x_display, sizeof the_only_x_display);
2567 the_only_x_display.background_pixel = 7; /* White */
2568 the_only_x_display.foreground_pixel = 0; /* Black */
2569 bright_bg ();
2570 colors = getenv ("EMACSCOLORS");
2571 if (colors && strlen (colors) >= 2)
2572 {
2573 /* The colors use 4 bits each (we enable bright background). */
2574 if (isdigit (colors[0]))
2575 colors[0] -= '0';
2576 else if (isxdigit (colors[0]))
2577 colors[0] -= (isupper (colors[0]) ? 'A' : 'a') - 10;
2578 if (colors[0] >= 0 && colors[0] < 16)
2579 the_only_x_display.foreground_pixel = colors[0];
2580 if (isdigit (colors[1]))
2581 colors[1] -= '0';
2582 else if (isxdigit (colors[1]))
2583 colors[1] -= (isupper (colors[1]) ? 'A' : 'a') - 10;
2584 if (colors[1] >= 0 && colors[1] < 16)
2585 the_only_x_display.background_pixel = colors[1];
2586 }
2587 the_only_x_display.line_height = 1;
2588 the_only_x_display.font = (XFontStruct *)1; /* must *not* be zero */
2589 the_only_x_display.display_info.mouse_face_mouse_frame = NULL;
2590 the_only_x_display.display_info.mouse_face_deferred_gc = 0;
2591 the_only_x_display.display_info.mouse_face_beg_row =
2592 the_only_x_display.display_info.mouse_face_beg_col = -1;
2593 the_only_x_display.display_info.mouse_face_end_row =
2594 the_only_x_display.display_info.mouse_face_end_col = -1;
2595 the_only_x_display.display_info.mouse_face_face_id = DEFAULT_FACE_ID;
2596 the_only_x_display.display_info.mouse_face_window = Qnil;
2597 the_only_x_display.display_info.mouse_face_mouse_x =
2598 the_only_x_display.display_info.mouse_face_mouse_y = 0;
2599 the_only_x_display.display_info.mouse_face_defer = 0;
2600
2601 init_frame_faces (sf);
2602
2603 ring_bell_hook = IT_ring_bell;
2604 insert_glyphs_hook = IT_insert_glyphs;
2605 delete_glyphs_hook = IT_delete_glyphs;
2606 write_glyphs_hook = IT_write_glyphs;
2607 cursor_to_hook = raw_cursor_to_hook = IT_cursor_to;
2608 clear_to_end_hook = IT_clear_to_end;
2609 clear_end_of_line_hook = IT_clear_end_of_line;
2610 clear_frame_hook = IT_clear_screen;
2611 change_line_highlight_hook = IT_change_line_highlight;
2612 update_begin_hook = IT_update_begin;
2613 update_end_hook = IT_update_end;
2614 reassert_line_highlight_hook = IT_reassert_line_highlight;
2615 frame_up_to_date_hook = IT_frame_up_to_date;
2616
2617 /* These hooks are called by term.c without being checked. */
2618 set_terminal_modes_hook = IT_set_terminal_modes;
2619 reset_terminal_modes_hook = IT_reset_terminal_modes;
2620 set_terminal_window_hook = IT_set_terminal_window;
2621 char_ins_del_ok = 0;
2622 #endif
2623 }
2624
2625 dos_get_saved_screen (screen, rows, cols)
2626 char **screen;
2627 int *rows;
2628 int *cols;
2629 {
2630 #ifndef HAVE_X_WINDOWS
2631 *screen = startup_screen_buffer;
2632 *cols = startup_screen_size_X;
2633 *rows = startup_screen_size_Y;
2634 return *screen != (char *)0;
2635 #else
2636 return 0;
2637 #endif
2638 }
2639
2640 #ifndef HAVE_X_WINDOWS
2641
2642 /* We are not X, but we can emulate it well enough for our needs... */
2643 void
2644 check_x (void)
2645 {
2646 if (! FRAME_MSDOS_P (SELECTED_FRAME()))
2647 error ("Not running under a window system");
2648 }
2649
2650 #endif
2651
2652 \f
2653 /* ----------------------- Keyboard control ----------------------
2654 *
2655 * Keymaps reflect the following keyboard layout:
2656 *
2657 * 0 1 2 3 4 5 6 7 8 9 10 11 12 BS
2658 * TAB 15 16 17 18 19 20 21 22 23 24 25 26 (41)
2659 * CLOK 30 31 32 33 34 35 36 37 38 39 40 (41) RET
2660 * SH () 45 46 47 48 49 50 51 52 53 54 SHIFT
2661 * SPACE
2662 */
2663
2664 #define Ignore 0x0000
2665 #define Normal 0x0000 /* normal key - alt changes scan-code */
2666 #define FctKey 0x1000 /* func key if c == 0, else c */
2667 #define Special 0x2000 /* func key even if c != 0 */
2668 #define ModFct 0x3000 /* special if mod-keys, else 'c' */
2669 #define Map 0x4000 /* alt scan-code, map to unshift/shift key */
2670 #define KeyPad 0x5000 /* map to insert/kp-0 depending on c == 0xe0 */
2671 #define Grey 0x6000 /* Grey keypad key */
2672
2673 #define Alt 0x0100 /* alt scan-code */
2674 #define Ctrl 0x0200 /* ctrl scan-code */
2675 #define Shift 0x0400 /* shift scan-code */
2676
2677 static int extended_kbd; /* 101 (102) keyboard present. */
2678
2679 struct kbd_translate {
2680 unsigned char sc;
2681 unsigned char ch;
2682 unsigned short code;
2683 };
2684
2685 struct dos_keyboard_map
2686 {
2687 char *unshifted;
2688 char *shifted;
2689 char *alt_gr;
2690 struct kbd_translate *translate_table;
2691 };
2692
2693
2694 static struct dos_keyboard_map us_keyboard = {
2695 /* 0 1 2 3 4 5 */
2696 /* 01234567890123456789012345678901234567890 12345678901234 */
2697 "`1234567890-= qwertyuiop[] asdfghjkl;'\\ zxcvbnm,./ ",
2698 /* 0123456789012345678901234567890123456789 012345678901234 */
2699 "~!@#$%^&*()_+ QWERTYUIOP{} ASDFGHJKL:\"| ZXCVBNM<>? ",
2700 0, /* no Alt-Gr key */
2701 0 /* no translate table */
2702 };
2703
2704 static struct dos_keyboard_map fr_keyboard = {
2705 /* 0 1 2 3 4 5 */
2706 /* 012 3456789012345678901234567890123456789012345678901234 */
2707 "ý&\82\",(-\8a_\80\85)= azertyuiop^$ qsdfghjklm\97* wxcvbnm;:! ",
2708 /* 0123456789012345678901234567890123456789012345678901234 */
2709 " 1234567890ø+ AZERTYUIOPù\9c QSDFGHJKLM%æ WXCVBN?./õ ",
2710 /* 01234567 89012345678901234567890123456789012345678901234 */
2711 " ~#{[|`\\^@]} Ï ",
2712 0 /* no translate table */
2713 };
2714
2715 /*
2716 * Italian keyboard support, country code 39.
2717 * '<' 56:3c*0000
2718 * '>' 56:3e*0000
2719 * added also {,},` as, respectively, AltGr-8, AltGr-9, AltGr-'
2720 * Donated by Stefano Brozzi <brozzis@mag00.cedi.unipr.it>
2721 */
2722
2723 static struct kbd_translate it_kbd_translate_table[] = {
2724 { 0x56, 0x3c, Normal | 13 },
2725 { 0x56, 0x3e, Normal | 27 },
2726 { 0, 0, 0 }
2727 };
2728 static struct dos_keyboard_map it_keyboard = {
2729 /* 0 1 2 3 4 5 */
2730 /* 0 123456789012345678901234567890123456789012345678901234 */
2731 "\\1234567890'\8d< qwertyuiop\8a+> asdfghjkl\95\85\97 zxcvbnm,.- ",
2732 /* 01 23456789012345678901234567890123456789012345678901234 */
2733 "|!\"\9c$%&/()=?^> QWERTYUIOP\82* ASDFGHJKL\87øõ ZXCVBNM;:_ ",
2734 /* 0123456789012345678901234567890123456789012345678901234 */
2735 " {}~` [] @# ",
2736 it_kbd_translate_table
2737 };
2738
2739 static struct dos_keyboard_map dk_keyboard = {
2740 /* 0 1 2 3 4 5 */
2741 /* 0123456789012345678901234567890123456789012345678901234 */
2742 "«1234567890+| qwertyuiop\86~ asdfghjkl\91\9b' zxcvbnm,.- ",
2743 /* 01 23456789012345678901234567890123456789012345678901234 */
2744 "õ!\"#$%&/()=?` QWERTYUIOP\8f^ ASDFGHJKL\92\9d* ZXCVBNM;:_ ",
2745 /* 0123456789012345678901234567890123456789012345678901234 */
2746 " @\9c$ {[]} | ",
2747 0 /* no translate table */
2748 };
2749
2750 static struct kbd_translate jp_kbd_translate_table[] = {
2751 { 0x73, 0x5c, Normal | 0 },
2752 { 0x73, 0x5f, Normal | 0 },
2753 { 0x73, 0x1c, Map | 0 },
2754 { 0x7d, 0x5c, Normal | 13 },
2755 { 0x7d, 0x7c, Normal | 13 },
2756 { 0x7d, 0x1c, Map | 13 },
2757 { 0, 0, 0 }
2758 };
2759 static struct dos_keyboard_map jp_keyboard = {
2760 /* 0 1 2 3 4 5 */
2761 /* 0123456789012 345678901234567890123456789012345678901234 */
2762 "\\1234567890-^\\ qwertyuiop@[ asdfghjkl;:] zxcvbnm,./ ",
2763 /* 01 23456789012345678901234567890123456789012345678901234 */
2764 "_!\"#$%&'()~=~| QWERTYUIOP`{ ASDFGHJKL+*} ZXCVBNM<>? ",
2765 0, /* no Alt-Gr key */
2766 jp_kbd_translate_table
2767 };
2768
2769 static struct keyboard_layout_list
2770 {
2771 int country_code;
2772 struct dos_keyboard_map *keyboard_map;
2773 } keyboard_layout_list[] =
2774 {
2775 1, &us_keyboard,
2776 33, &fr_keyboard,
2777 39, &it_keyboard,
2778 45, &dk_keyboard,
2779 81, &jp_keyboard
2780 };
2781
2782 static struct dos_keyboard_map *keyboard;
2783 static int keyboard_map_all;
2784 static int international_keyboard;
2785
2786 int
2787 dos_set_keyboard (code, always)
2788 int code;
2789 int always;
2790 {
2791 int i;
2792 _go32_dpmi_registers regs;
2793
2794 /* See if Keyb.Com is installed (for international keyboard support).
2795 Note: calling Int 2Fh via int86 wedges the DOS box on some versions
2796 of Windows 9X! So don't do that! */
2797 regs.x.ax = 0xad80;
2798 regs.x.ss = regs.x.sp = regs.x.flags = 0;
2799 _go32_dpmi_simulate_int (0x2f, &regs);
2800 if (regs.h.al == 0xff)
2801 international_keyboard = 1;
2802
2803 /* Initialize to US settings, for countries that don't have their own. */
2804 keyboard = keyboard_layout_list[0].keyboard_map;
2805 keyboard_map_all = always;
2806 dos_keyboard_layout = 1;
2807
2808 for (i = 0; i < (sizeof (keyboard_layout_list)/sizeof (struct keyboard_layout_list)); i++)
2809 if (code == keyboard_layout_list[i].country_code)
2810 {
2811 keyboard = keyboard_layout_list[i].keyboard_map;
2812 keyboard_map_all = always;
2813 dos_keyboard_layout = code;
2814 return 1;
2815 }
2816 return 0;
2817 }
2818 \f
2819 static struct
2820 {
2821 unsigned char char_code; /* normal code */
2822 unsigned char meta_code; /* M- code */
2823 unsigned char keypad_code; /* keypad code */
2824 unsigned char editkey_code; /* edit key */
2825 } keypad_translate_map[] = {
2826 '0', '0', 0xb0, /* kp-0 */ 0x63, /* insert */
2827 '1', '1', 0xb1, /* kp-1 */ 0x57, /* end */
2828 '2', '2', 0xb2, /* kp-2 */ 0x54, /* down */
2829 '3', '3', 0xb3, /* kp-3 */ 0x56, /* next */
2830 '4', '4', 0xb4, /* kp-4 */ 0x51, /* left */
2831 '5', '5', 0xb5, /* kp-5 */ 0xb5, /* kp-5 */
2832 '6', '6', 0xb6, /* kp-6 */ 0x53, /* right */
2833 '7', '7', 0xb7, /* kp-7 */ 0x50, /* home */
2834 '8', '8', 0xb8, /* kp-8 */ 0x52, /* up */
2835 '9', '9', 0xb9, /* kp-9 */ 0x55, /* prior */
2836 '.', '-', 0xae, /* kp-decimal */ 0xff /* delete */
2837 };
2838
2839 static struct
2840 {
2841 unsigned char char_code; /* normal code */
2842 unsigned char keypad_code; /* keypad code */
2843 } grey_key_translate_map[] = {
2844 '/', 0xaf, /* kp-decimal */
2845 '*', 0xaa, /* kp-multiply */
2846 '-', 0xad, /* kp-subtract */
2847 '+', 0xab, /* kp-add */
2848 '\r', 0x8d /* kp-enter */
2849 };
2850
2851 static unsigned short
2852 ibmpc_translate_map[] =
2853 {
2854 /* --------------- 00 to 0f --------------- */
2855 Normal | 0xff, /* Ctrl Break + Alt-NNN */
2856 Alt | ModFct | 0x1b, /* Escape */
2857 Normal | 1, /* '1' */
2858 Normal | 2, /* '2' */
2859 Normal | 3, /* '3' */
2860 Normal | 4, /* '4' */
2861 Normal | 5, /* '5' */
2862 Normal | 6, /* '6' */
2863 Normal | 7, /* '7' */
2864 Normal | 8, /* '8' */
2865 Normal | 9, /* '9' */
2866 Normal | 10, /* '0' */
2867 Normal | 11, /* '-' */
2868 Normal | 12, /* '=' */
2869 Special | 0x08, /* Backspace */
2870 ModFct | 0x74, /* Tab/Backtab */
2871
2872 /* --------------- 10 to 1f --------------- */
2873 Map | 15, /* 'q' */
2874 Map | 16, /* 'w' */
2875 Map | 17, /* 'e' */
2876 Map | 18, /* 'r' */
2877 Map | 19, /* 't' */
2878 Map | 20, /* 'y' */
2879 Map | 21, /* 'u' */
2880 Map | 22, /* 'i' */
2881 Map | 23, /* 'o' */
2882 Map | 24, /* 'p' */
2883 Map | 25, /* '[' */
2884 Map | 26, /* ']' */
2885 ModFct | 0x0d, /* Return */
2886 Ignore, /* Ctrl */
2887 Map | 30, /* 'a' */
2888 Map | 31, /* 's' */
2889
2890 /* --------------- 20 to 2f --------------- */
2891 Map | 32, /* 'd' */
2892 Map | 33, /* 'f' */
2893 Map | 34, /* 'g' */
2894 Map | 35, /* 'h' */
2895 Map | 36, /* 'j' */
2896 Map | 37, /* 'k' */
2897 Map | 38, /* 'l' */
2898 Map | 39, /* ';' */
2899 Map | 40, /* '\'' */
2900 Map | 0, /* '`' */
2901 Ignore, /* Left shift */
2902 Map | 41, /* '\\' */
2903 Map | 45, /* 'z' */
2904 Map | 46, /* 'x' */
2905 Map | 47, /* 'c' */
2906 Map | 48, /* 'v' */
2907
2908 /* --------------- 30 to 3f --------------- */
2909 Map | 49, /* 'b' */
2910 Map | 50, /* 'n' */
2911 Map | 51, /* 'm' */
2912 Map | 52, /* ',' */
2913 Map | 53, /* '.' */
2914 Map | 54, /* '/' */
2915 Ignore, /* Right shift */
2916 Grey | 1, /* Grey * */
2917 Ignore, /* Alt */
2918 Normal | 55, /* ' ' */
2919 Ignore, /* Caps Lock */
2920 FctKey | 0xbe, /* F1 */
2921 FctKey | 0xbf, /* F2 */
2922 FctKey | 0xc0, /* F3 */
2923 FctKey | 0xc1, /* F4 */
2924 FctKey | 0xc2, /* F5 */
2925
2926 /* --------------- 40 to 4f --------------- */
2927 FctKey | 0xc3, /* F6 */
2928 FctKey | 0xc4, /* F7 */
2929 FctKey | 0xc5, /* F8 */
2930 FctKey | 0xc6, /* F9 */
2931 FctKey | 0xc7, /* F10 */
2932 Ignore, /* Num Lock */
2933 Ignore, /* Scroll Lock */
2934 KeyPad | 7, /* Home */
2935 KeyPad | 8, /* Up */
2936 KeyPad | 9, /* Page Up */
2937 Grey | 2, /* Grey - */
2938 KeyPad | 4, /* Left */
2939 KeyPad | 5, /* Keypad 5 */
2940 KeyPad | 6, /* Right */
2941 Grey | 3, /* Grey + */
2942 KeyPad | 1, /* End */
2943
2944 /* --------------- 50 to 5f --------------- */
2945 KeyPad | 2, /* Down */
2946 KeyPad | 3, /* Page Down */
2947 KeyPad | 0, /* Insert */
2948 KeyPad | 10, /* Delete */
2949 Shift | FctKey | 0xbe, /* (Shift) F1 */
2950 Shift | FctKey | 0xbf, /* (Shift) F2 */
2951 Shift | FctKey | 0xc0, /* (Shift) F3 */
2952 Shift | FctKey | 0xc1, /* (Shift) F4 */
2953 Shift | FctKey | 0xc2, /* (Shift) F5 */
2954 Shift | FctKey | 0xc3, /* (Shift) F6 */
2955 Shift | FctKey | 0xc4, /* (Shift) F7 */
2956 Shift | FctKey | 0xc5, /* (Shift) F8 */
2957 Shift | FctKey | 0xc6, /* (Shift) F9 */
2958 Shift | FctKey | 0xc7, /* (Shift) F10 */
2959 Ctrl | FctKey | 0xbe, /* (Ctrl) F1 */
2960 Ctrl | FctKey | 0xbf, /* (Ctrl) F2 */
2961
2962 /* --------------- 60 to 6f --------------- */
2963 Ctrl | FctKey | 0xc0, /* (Ctrl) F3 */
2964 Ctrl | FctKey | 0xc1, /* (Ctrl) F4 */
2965 Ctrl | FctKey | 0xc2, /* (Ctrl) F5 */
2966 Ctrl | FctKey | 0xc3, /* (Ctrl) F6 */
2967 Ctrl | FctKey | 0xc4, /* (Ctrl) F7 */
2968 Ctrl | FctKey | 0xc5, /* (Ctrl) F8 */
2969 Ctrl | FctKey | 0xc6, /* (Ctrl) F9 */
2970 Ctrl | FctKey | 0xc7, /* (Ctrl) F10 */
2971 Alt | FctKey | 0xbe, /* (Alt) F1 */
2972 Alt | FctKey | 0xbf, /* (Alt) F2 */
2973 Alt | FctKey | 0xc0, /* (Alt) F3 */
2974 Alt | FctKey | 0xc1, /* (Alt) F4 */
2975 Alt | FctKey | 0xc2, /* (Alt) F5 */
2976 Alt | FctKey | 0xc3, /* (Alt) F6 */
2977 Alt | FctKey | 0xc4, /* (Alt) F7 */
2978 Alt | FctKey | 0xc5, /* (Alt) F8 */
2979
2980 /* --------------- 70 to 7f --------------- */
2981 Alt | FctKey | 0xc6, /* (Alt) F9 */
2982 Alt | FctKey | 0xc7, /* (Alt) F10 */
2983 Ctrl | FctKey | 0x6d, /* (Ctrl) Sys Rq */
2984 Ctrl | KeyPad | 4, /* (Ctrl) Left */
2985 Ctrl | KeyPad | 6, /* (Ctrl) Right */
2986 Ctrl | KeyPad | 1, /* (Ctrl) End */
2987 Ctrl | KeyPad | 3, /* (Ctrl) Page Down */
2988 Ctrl | KeyPad | 7, /* (Ctrl) Home */
2989 Alt | Map | 1, /* '1' */
2990 Alt | Map | 2, /* '2' */
2991 Alt | Map | 3, /* '3' */
2992 Alt | Map | 4, /* '4' */
2993 Alt | Map | 5, /* '5' */
2994 Alt | Map | 6, /* '6' */
2995 Alt | Map | 7, /* '7' */
2996 Alt | Map | 8, /* '8' */
2997
2998 /* --------------- 80 to 8f --------------- */
2999 Alt | Map | 9, /* '9' */
3000 Alt | Map | 10, /* '0' */
3001 Alt | Map | 11, /* '-' */
3002 Alt | Map | 12, /* '=' */
3003 Ctrl | KeyPad | 9, /* (Ctrl) Page Up */
3004 FctKey | 0xc8, /* F11 */
3005 FctKey | 0xc9, /* F12 */
3006 Shift | FctKey | 0xc8, /* (Shift) F11 */
3007 Shift | FctKey | 0xc9, /* (Shift) F12 */
3008 Ctrl | FctKey | 0xc8, /* (Ctrl) F11 */
3009 Ctrl | FctKey | 0xc9, /* (Ctrl) F12 */
3010 Alt | FctKey | 0xc8, /* (Alt) F11 */
3011 Alt | FctKey | 0xc9, /* (Alt) F12 */
3012 Ctrl | KeyPad | 8, /* (Ctrl) Up */
3013 Ctrl | Grey | 2, /* (Ctrl) Grey - */
3014 Ctrl | KeyPad | 5, /* (Ctrl) Keypad 5 */
3015
3016 /* --------------- 90 to 9f --------------- */
3017 Ctrl | Grey | 3, /* (Ctrl) Grey + */
3018 Ctrl | KeyPad | 2, /* (Ctrl) Down */
3019 Ctrl | KeyPad | 0, /* (Ctrl) Insert */
3020 Ctrl | KeyPad | 10, /* (Ctrl) Delete */
3021 Ctrl | FctKey | 0x09, /* (Ctrl) Tab */
3022 Ctrl | Grey | 0, /* (Ctrl) Grey / */
3023 Ctrl | Grey | 1, /* (Ctrl) Grey * */
3024 Alt | FctKey | 0x50, /* (Alt) Home */
3025 Alt | FctKey | 0x52, /* (Alt) Up */
3026 Alt | FctKey | 0x55, /* (Alt) Page Up */
3027 Ignore, /* NO KEY */
3028 Alt | FctKey | 0x51, /* (Alt) Left */
3029 Ignore, /* NO KEY */
3030 Alt | FctKey | 0x53, /* (Alt) Right */
3031 Ignore, /* NO KEY */
3032 Alt | FctKey | 0x57, /* (Alt) End */
3033
3034 /* --------------- a0 to af --------------- */
3035 Alt | KeyPad | 2, /* (Alt) Down */
3036 Alt | KeyPad | 3, /* (Alt) Page Down */
3037 Alt | KeyPad | 0, /* (Alt) Insert */
3038 Alt | KeyPad | 10, /* (Alt) Delete */
3039 Alt | Grey | 0, /* (Alt) Grey / */
3040 Alt | FctKey | 0x09, /* (Alt) Tab */
3041 Alt | Grey | 4 /* (Alt) Keypad Enter */
3042 };
3043 \f
3044 /* These bit-positions corresponds to values returned by BIOS */
3045 #define SHIFT_P 0x0003 /* two bits! */
3046 #define CTRL_P 0x0004
3047 #define ALT_P 0x0008
3048 #define SCRLOCK_P 0x0010
3049 #define NUMLOCK_P 0x0020
3050 #define CAPSLOCK_P 0x0040
3051 #define ALT_GR_P 0x0800
3052 #define SUPER_P 0x4000 /* pseudo */
3053 #define HYPER_P 0x8000 /* pseudo */
3054
3055 static int
3056 dos_get_modifiers (keymask)
3057 int *keymask;
3058 {
3059 union REGS regs;
3060 int mask;
3061 int modifiers = 0;
3062
3063 /* Calculate modifier bits */
3064 regs.h.ah = extended_kbd ? 0x12 : 0x02;
3065 int86 (0x16, &regs, &regs);
3066
3067 if (!extended_kbd)
3068 {
3069 mask = regs.h.al & (SHIFT_P | CTRL_P | ALT_P |
3070 SCRLOCK_P | NUMLOCK_P | CAPSLOCK_P);
3071 }
3072 else
3073 {
3074 mask = regs.h.al & (SHIFT_P |
3075 SCRLOCK_P | NUMLOCK_P | CAPSLOCK_P);
3076
3077 /* Do not break international keyboard support. */
3078 /* When Keyb.Com is loaded, the right Alt key is */
3079 /* used for accessing characters like { and } */
3080 if (regs.h.ah & 2) /* Left ALT pressed ? */
3081 mask |= ALT_P;
3082
3083 if ((regs.h.ah & 8) != 0) /* Right ALT pressed ? */
3084 {
3085 mask |= ALT_GR_P;
3086 if (dos_hyper_key == 1)
3087 {
3088 mask |= HYPER_P;
3089 modifiers |= hyper_modifier;
3090 }
3091 else if (dos_super_key == 1)
3092 {
3093 mask |= SUPER_P;
3094 modifiers |= super_modifier;
3095 }
3096 else if (!international_keyboard)
3097 {
3098 /* If Keyb.Com is NOT installed, let Right Alt behave
3099 like the Left Alt. */
3100 mask &= ~ALT_GR_P;
3101 mask |= ALT_P;
3102 }
3103 }
3104
3105 if (regs.h.ah & 1) /* Left CTRL pressed ? */
3106 mask |= CTRL_P;
3107
3108 if (regs.h.ah & 4) /* Right CTRL pressed ? */
3109 {
3110 if (dos_hyper_key == 2)
3111 {
3112 mask |= HYPER_P;
3113 modifiers |= hyper_modifier;
3114 }
3115 else if (dos_super_key == 2)
3116 {
3117 mask |= SUPER_P;
3118 modifiers |= super_modifier;
3119 }
3120 else
3121 mask |= CTRL_P;
3122 }
3123 }
3124
3125 if (mask & SHIFT_P)
3126 modifiers |= shift_modifier;
3127 if (mask & CTRL_P)
3128 modifiers |= ctrl_modifier;
3129 if (mask & ALT_P)
3130 modifiers |= meta_modifier;
3131
3132 if (keymask)
3133 *keymask = mask;
3134 return modifiers;
3135 }
3136
3137 #define NUM_RECENT_DOSKEYS (100)
3138 int recent_doskeys_index; /* Index for storing next element into recent_doskeys */
3139 int total_doskeys; /* Total number of elements stored into recent_doskeys */
3140 Lisp_Object recent_doskeys; /* A vector, holding the last 100 keystrokes */
3141
3142 DEFUN ("recent-doskeys", Frecent_doskeys, Srecent_doskeys, 0, 0, 0,
3143 "Return vector of last 100 keyboard input values seen in dos_rawgetc.\n\
3144 Each input key receives two values in this vector: first the ASCII code,\n\
3145 and then the scan code.")
3146 ()
3147 {
3148 Lisp_Object *keys = XVECTOR (recent_doskeys)->contents;
3149 Lisp_Object val;
3150
3151 if (total_doskeys < NUM_RECENT_DOSKEYS)
3152 return Fvector (total_doskeys, keys);
3153 else
3154 {
3155 val = Fvector (NUM_RECENT_DOSKEYS, keys);
3156 bcopy (keys + recent_doskeys_index,
3157 XVECTOR (val)->contents,
3158 (NUM_RECENT_DOSKEYS - recent_doskeys_index) * sizeof (Lisp_Object));
3159 bcopy (keys,
3160 XVECTOR (val)->contents + NUM_RECENT_DOSKEYS - recent_doskeys_index,
3161 recent_doskeys_index * sizeof (Lisp_Object));
3162 return val;
3163 }
3164 }
3165
3166 /* Get a char from keyboard. Function keys are put into the event queue. */
3167 static int
3168 dos_rawgetc ()
3169 {
3170 struct input_event event;
3171 union REGS regs;
3172
3173 #ifndef HAVE_X_WINDOWS
3174 /* Maybe put the cursor where it should be. */
3175 IT_cmgoto (SELECTED_FRAME());
3176 #endif
3177
3178 /* The following condition is equivalent to `kbhit ()', except that
3179 it uses the bios to do its job. This pleases DESQview/X. */
3180 while ((regs.h.ah = extended_kbd ? 0x11 : 0x01),
3181 int86 (0x16, &regs, &regs),
3182 (regs.x.flags & 0x40) == 0)
3183 {
3184 union REGS regs;
3185 register unsigned char c;
3186 int sc, code = -1, mask, kp_mode;
3187 int modifiers;
3188
3189 regs.h.ah = extended_kbd ? 0x10 : 0x00;
3190 int86 (0x16, &regs, &regs);
3191 c = regs.h.al;
3192 sc = regs.h.ah;
3193
3194 total_doskeys += 2;
3195 XVECTOR (recent_doskeys)->contents[recent_doskeys_index++]
3196 = make_number (c);
3197 if (recent_doskeys_index == NUM_RECENT_DOSKEYS)
3198 recent_doskeys_index = 0;
3199 XVECTOR (recent_doskeys)->contents[recent_doskeys_index++]
3200 = make_number (sc);
3201 if (recent_doskeys_index == NUM_RECENT_DOSKEYS)
3202 recent_doskeys_index = 0;
3203
3204 modifiers = dos_get_modifiers (&mask);
3205
3206 #ifndef HAVE_X_WINDOWS
3207 if (!NILP (Vdos_display_scancodes))
3208 {
3209 char buf[11];
3210 sprintf (buf, "%02x:%02x*%04x",
3211 (unsigned) (sc&0xff), (unsigned) c, mask);
3212 dos_direct_output (screen_size_Y - 2, screen_size_X - 12, buf, 10);
3213 }
3214 #endif
3215
3216 if (sc == 0xe0)
3217 {
3218 switch (c)
3219 {
3220 case 10: /* Ctrl Grey Enter */
3221 code = Ctrl | Grey | 4;
3222 break;
3223 case 13: /* Grey Enter */
3224 code = Grey | 4;
3225 break;
3226 case '/': /* Grey / */
3227 code = Grey | 0;
3228 break;
3229 default:
3230 continue;
3231 };
3232 c = 0;
3233 }
3234 else
3235 {
3236 /* Try the keyboard-private translation table first. */
3237 if (keyboard->translate_table)
3238 {
3239 struct kbd_translate *p = keyboard->translate_table;
3240
3241 while (p->sc)
3242 {
3243 if (p->sc == sc && p->ch == c)
3244 {
3245 code = p->code;
3246 break;
3247 }
3248 p++;
3249 }
3250 }
3251 /* If the private table didn't translate it, use the general
3252 one. */
3253 if (code == -1)
3254 {
3255 if (sc >= (sizeof (ibmpc_translate_map) / sizeof (short)))
3256 continue;
3257 if ((code = ibmpc_translate_map[sc]) == Ignore)
3258 continue;
3259 }
3260 }
3261
3262 if (c == 0)
3263 {
3264 /* We only look at the keyboard Ctrl/Shift/Alt keys when
3265 Emacs is ready to read a key. Therefore, if they press
3266 `Alt-x' when Emacs is busy, by the time we get to
3267 `dos_get_modifiers', they might have already released the
3268 Alt key, and Emacs gets just `x', which is BAD.
3269 However, for keys with the `Map' property set, the ASCII
3270 code returns zero iff Alt is pressed. So, when we DON'T
3271 have to support international_keyboard, we don't have to
3272 distinguish between the left and right Alt keys, and we
3273 can set the META modifier for any keys with the `Map'
3274 property if they return zero ASCII code (c = 0). */
3275 if ( (code & Alt)
3276 || ( (code & 0xf000) == Map && !international_keyboard))
3277 modifiers |= meta_modifier;
3278 if (code & Ctrl)
3279 modifiers |= ctrl_modifier;
3280 if (code & Shift)
3281 modifiers |= shift_modifier;
3282 }
3283
3284 switch (code & 0xf000)
3285 {
3286 case ModFct:
3287 if (c && !(mask & (SHIFT_P | ALT_P | CTRL_P | HYPER_P | SUPER_P)))
3288 return c;
3289 c = 0; /* Special */
3290
3291 case FctKey:
3292 if (c != 0)
3293 return c;
3294
3295 case Special:
3296 code |= 0xff00;
3297 break;
3298
3299 case Normal:
3300 if (sc == 0)
3301 {
3302 if (c == 0) /* ctrl-break */
3303 continue;
3304 return c; /* ALT-nnn */
3305 }
3306 if (!keyboard_map_all)
3307 {
3308 if (c != ' ')
3309 return c;
3310 code = c;
3311 break;
3312 }
3313
3314 case Map:
3315 if (c && !(mask & ALT_P) && !((mask & SHIFT_P) && (mask & CTRL_P)))
3316 if (!keyboard_map_all)
3317 return c;
3318
3319 code &= 0xff;
3320 if (mask & ALT_P && code <= 10 && code > 0 && dos_keypad_mode & 0x200)
3321 mask |= SHIFT_P; /* ALT-1 => M-! etc. */
3322
3323 if (mask & SHIFT_P)
3324 {
3325 code = keyboard->shifted[code];
3326 mask -= SHIFT_P;
3327 modifiers &= ~shift_modifier;
3328 }
3329 else
3330 if ((mask & ALT_GR_P) && keyboard->alt_gr && keyboard->alt_gr[code] != ' ')
3331 code = keyboard->alt_gr[code];
3332 else
3333 code = keyboard->unshifted[code];
3334 break;
3335
3336 case KeyPad:
3337 code &= 0xff;
3338 if (c == 0xe0) /* edit key */
3339 kp_mode = 3;
3340 else
3341 if ((mask & (NUMLOCK_P|CTRL_P|SHIFT_P|ALT_P)) == NUMLOCK_P) /* numlock on */
3342 kp_mode = dos_keypad_mode & 0x03;
3343 else
3344 kp_mode = (dos_keypad_mode >> 4) & 0x03;
3345
3346 switch (kp_mode)
3347 {
3348 case 0:
3349 if (code == 10 && dos_decimal_point)
3350 return dos_decimal_point;
3351 return keypad_translate_map[code].char_code;
3352
3353 case 1:
3354 code = 0xff00 | keypad_translate_map[code].keypad_code;
3355 break;
3356
3357 case 2:
3358 code = keypad_translate_map[code].meta_code;
3359 modifiers = meta_modifier;
3360 break;
3361
3362 case 3:
3363 code = 0xff00 | keypad_translate_map[code].editkey_code;
3364 break;
3365 }
3366 break;
3367
3368 case Grey:
3369 code &= 0xff;
3370 kp_mode = ((mask & (NUMLOCK_P|CTRL_P|SHIFT_P|ALT_P)) == NUMLOCK_P) ? 0x04 : 0x40;
3371 if (dos_keypad_mode & kp_mode)
3372 code = 0xff00 | grey_key_translate_map[code].keypad_code;
3373 else
3374 code = grey_key_translate_map[code].char_code;
3375 break;
3376 }
3377
3378 make_event:
3379 if (code == 0)
3380 continue;
3381
3382 if (code >= 0x100)
3383 event.kind = non_ascii_keystroke;
3384 else
3385 event.kind = ascii_keystroke;
3386 event.code = code;
3387 event.modifiers = modifiers;
3388 event.frame_or_window = selected_frame;
3389 event.arg = Qnil;
3390 event.timestamp = event_timestamp ();
3391 kbd_buffer_store_event (&event);
3392 }
3393
3394 if (have_mouse > 0 && !mouse_preempted)
3395 {
3396 int but, press, x, y, ok;
3397 int mouse_prev_x = mouse_last_x, mouse_prev_y = mouse_last_y;
3398
3399 /* Check for mouse movement *before* buttons. */
3400 mouse_check_moved ();
3401
3402 /* If the mouse moved from the spot of its last sighting, we
3403 might need to update mouse highlight. */
3404 if (mouse_last_x != mouse_prev_x || mouse_last_y != mouse_prev_y)
3405 {
3406 previous_help_echo = help_echo;
3407 help_echo = help_echo_object = help_echo_window = Qnil;
3408 help_echo_pos = -1;
3409 IT_note_mouse_highlight (SELECTED_FRAME(),
3410 mouse_last_x, mouse_last_y);
3411 /* If the contents of the global variable help_echo has
3412 changed, generate a HELP_EVENT. */
3413 if (!NILP (help_echo) || !NILP (previous_help_echo))
3414 {
3415 /* HELP_EVENT takes 2 events in the event loop. */
3416 event.kind = HELP_EVENT;
3417 event.frame_or_window = selected_frame;
3418 event.arg = help_echo_object;
3419 event.x = make_number (help_echo_pos);
3420 event.timestamp = event_timestamp ();
3421 event.code = 0;
3422 kbd_buffer_store_event (&event);
3423 if (WINDOWP (help_echo_window))
3424 event.frame_or_window = help_echo_window;
3425 event.arg = help_echo;
3426 event.code = 1;
3427 kbd_buffer_store_event (&event);
3428 }
3429 }
3430
3431 for (but = 0; but < NUM_MOUSE_BUTTONS; but++)
3432 for (press = 0; press < 2; press++)
3433 {
3434 int button_num = but;
3435
3436 if (press)
3437 ok = mouse_pressed (but, &x, &y);
3438 else
3439 ok = mouse_released (but, &x, &y);
3440 if (ok)
3441 {
3442 /* Allow a simultaneous press/release of Mouse-1 and
3443 Mouse-2 to simulate Mouse-3 on two-button mice. */
3444 if (mouse_button_count == 2 && but < 2)
3445 {
3446 int x2, y2; /* don't clobber original coordinates */
3447
3448 /* If only one button is pressed, wait 100 msec and
3449 check again. This way, Speedy Gonzales isn't
3450 punished, while the slow get their chance. */
3451 if (press && mouse_pressed (1-but, &x2, &y2)
3452 || !press && mouse_released (1-but, &x2, &y2))
3453 button_num = 2;
3454 else
3455 {
3456 delay (100);
3457 if (press && mouse_pressed (1-but, &x2, &y2)
3458 || !press && mouse_released (1-but, &x2, &y2))
3459 button_num = 2;
3460 }
3461 }
3462
3463 event.kind = mouse_click;
3464 event.code = button_num;
3465 event.modifiers = dos_get_modifiers (0)
3466 | (press ? down_modifier : up_modifier);
3467 event.x = x;
3468 event.y = y;
3469 event.frame_or_window = selected_frame;
3470 event.arg = Qnil;
3471 event.timestamp = event_timestamp ();
3472 kbd_buffer_store_event (&event);
3473 }
3474 }
3475 }
3476
3477 return -1;
3478 }
3479
3480 static int prev_get_char = -1;
3481
3482 /* Return 1 if a key is ready to be read without suspending execution. */
3483
3484 dos_keysns ()
3485 {
3486 if (prev_get_char != -1)
3487 return 1;
3488 else
3489 return ((prev_get_char = dos_rawgetc ()) != -1);
3490 }
3491
3492 /* Read a key. Return -1 if no key is ready. */
3493
3494 dos_keyread ()
3495 {
3496 if (prev_get_char != -1)
3497 {
3498 int c = prev_get_char;
3499 prev_get_char = -1;
3500 return c;
3501 }
3502 else
3503 return dos_rawgetc ();
3504 }
3505 \f
3506 #ifndef HAVE_X_WINDOWS
3507 /* See xterm.c for more info. */
3508 void
3509 pixel_to_glyph_coords (f, pix_x, pix_y, x, y, bounds, noclip)
3510 FRAME_PTR f;
3511 register int pix_x, pix_y;
3512 register int *x, *y;
3513 XRectangle *bounds;
3514 int noclip;
3515 {
3516 if (bounds) abort ();
3517
3518 /* Ignore clipping. */
3519
3520 *x = pix_x;
3521 *y = pix_y;
3522 }
3523
3524 void
3525 glyph_to_pixel_coords (f, x, y, pix_x, pix_y)
3526 FRAME_PTR f;
3527 register int x, y;
3528 register int *pix_x, *pix_y;
3529 {
3530 *pix_x = x;
3531 *pix_y = y;
3532 }
3533 \f
3534 /* Simulation of X's menus. Nothing too fancy here -- just make it work
3535 for now.
3536
3537 Actually, I don't know the meaning of all the parameters of the functions
3538 here -- I only know how they are called by xmenu.c. I could of course
3539 grab the nearest Xlib manual (down the hall, second-to-last door on the
3540 left), but I don't think it's worth the effort. */
3541
3542 /* These hold text of the current and the previous menu help messages. */
3543 static char *menu_help_message, *prev_menu_help_message;
3544 /* Pane number and item number of the menu item which generated the
3545 last menu help message. */
3546 static int menu_help_paneno, menu_help_itemno;
3547
3548 static XMenu *
3549 IT_menu_create ()
3550 {
3551 XMenu *menu;
3552
3553 menu = (XMenu *) xmalloc (sizeof (XMenu));
3554 menu->allocated = menu->count = menu->panecount = menu->width = 0;
3555 return menu;
3556 }
3557
3558 /* Allocate some (more) memory for MENU ensuring that there is room for one
3559 for item. */
3560
3561 static void
3562 IT_menu_make_room (XMenu *menu)
3563 {
3564 if (menu->allocated == 0)
3565 {
3566 int count = menu->allocated = 10;
3567 menu->text = (char **) xmalloc (count * sizeof (char *));
3568 menu->submenu = (XMenu **) xmalloc (count * sizeof (XMenu *));
3569 menu->panenumber = (int *) xmalloc (count * sizeof (int));
3570 menu->help_text = (char **) xmalloc (count * sizeof (char *));
3571 }
3572 else if (menu->allocated == menu->count)
3573 {
3574 int count = menu->allocated = menu->allocated + 10;
3575 menu->text
3576 = (char **) xrealloc (menu->text, count * sizeof (char *));
3577 menu->submenu
3578 = (XMenu **) xrealloc (menu->submenu, count * sizeof (XMenu *));
3579 menu->panenumber
3580 = (int *) xrealloc (menu->panenumber, count * sizeof (int));
3581 menu->help_text
3582 = (char **) xrealloc (menu->help_text, count * sizeof (char *));
3583 }
3584 }
3585
3586 /* Search the given menu structure for a given pane number. */
3587
3588 static XMenu *
3589 IT_menu_search_pane (XMenu *menu, int pane)
3590 {
3591 int i;
3592 XMenu *try;
3593
3594 for (i = 0; i < menu->count; i++)
3595 if (menu->submenu[i])
3596 {
3597 if (pane == menu->panenumber[i])
3598 return menu->submenu[i];
3599 if ((try = IT_menu_search_pane (menu->submenu[i], pane)))
3600 return try;
3601 }
3602 return (XMenu *) 0;
3603 }
3604
3605 /* Determine how much screen space a given menu needs. */
3606
3607 static void
3608 IT_menu_calc_size (XMenu *menu, int *width, int *height)
3609 {
3610 int i, h2, w2, maxsubwidth, maxheight;
3611
3612 maxsubwidth = 0;
3613 maxheight = menu->count;
3614 for (i = 0; i < menu->count; i++)
3615 {
3616 if (menu->submenu[i])
3617 {
3618 IT_menu_calc_size (menu->submenu[i], &w2, &h2);
3619 if (w2 > maxsubwidth) maxsubwidth = w2;
3620 if (i + h2 > maxheight) maxheight = i + h2;
3621 }
3622 }
3623 *width = menu->width + maxsubwidth;
3624 *height = maxheight;
3625 }
3626
3627 /* Display MENU at (X,Y) using FACES. */
3628
3629 static void
3630 IT_menu_display (XMenu *menu, int y, int x, int pn, int *faces, int disp_help)
3631 {
3632 int i, j, face, width;
3633 struct glyph *text, *p;
3634 char *q;
3635 int mx, my;
3636 int enabled, mousehere;
3637 int row, col;
3638 struct frame *sf = SELECTED_FRAME();
3639
3640 menu_help_message = NULL;
3641
3642 width = menu->width;
3643 text = (struct glyph *) xmalloc ((width + 2) * sizeof (struct glyph));
3644 ScreenGetCursor (&row, &col);
3645 mouse_get_xy (&mx, &my);
3646 IT_update_begin (sf);
3647 for (i = 0; i < menu->count; i++)
3648 {
3649 int max_width = width + 2;
3650
3651 IT_cursor_to (y + i, x);
3652 enabled
3653 = (!menu->submenu[i] && menu->panenumber[i]) || (menu->submenu[i]);
3654 mousehere = (y + i == my && x <= mx && mx < x + width + 2);
3655 face = faces[enabled + mousehere * 2];
3656 /* The following if clause means that we display the menu help
3657 strings even if the menu item is currently disabled. */
3658 if (disp_help && enabled + mousehere * 2 >= 2)
3659 {
3660 menu_help_message = menu->help_text[i];
3661 menu_help_paneno = pn - 1;
3662 menu_help_itemno = i;
3663 }
3664 p = text;
3665 SET_CHAR_GLYPH (*p, ' ', face, 0);
3666 p++;
3667 for (j = 0, q = menu->text[i]; *q; j++)
3668 {
3669 if (*q > 26)
3670 {
3671 SET_CHAR_GLYPH (*p, *q++, face, 0);
3672 p++;
3673 }
3674 else /* make '^x' */
3675 {
3676 SET_CHAR_GLYPH (*p, '^', face, 0);
3677 p++;
3678 j++;
3679 SET_CHAR_GLYPH (*p, *q++ + 64, face, 0);
3680 p++;
3681 }
3682 }
3683 /* Don't let the menu text overflow into the next screen row. */
3684 if (x + max_width > screen_size_X)
3685 {
3686 max_width = screen_size_X - x;
3687 text[max_width - 1].u.ch = '$'; /* indicate it's truncated */
3688 }
3689 for (; j < max_width - 2; j++, p++)
3690 SET_CHAR_GLYPH (*p, ' ', face, 0);
3691
3692 SET_CHAR_GLYPH (*p, menu->submenu[i] ? 16 : ' ', face, 0);
3693 p++;
3694 IT_write_glyphs (text, max_width);
3695 }
3696 IT_update_end (sf);
3697 IT_cursor_to (row, col);
3698 xfree (text);
3699 }
3700 \f
3701 /* --------------------------- X Menu emulation ---------------------- */
3702
3703 /* Report availability of menus. */
3704
3705 int
3706 have_menus_p ()
3707 {
3708 return 1;
3709 }
3710
3711 /* Create a brand new menu structure. */
3712
3713 XMenu *
3714 XMenuCreate (Display *foo1, Window foo2, char *foo3)
3715 {
3716 return IT_menu_create ();
3717 }
3718
3719 /* Create a new pane and place it on the outer-most level. It is not
3720 clear that it should be placed out there, but I don't know what else
3721 to do. */
3722
3723 int
3724 XMenuAddPane (Display *foo, XMenu *menu, char *txt, int enable)
3725 {
3726 int len;
3727 char *p;
3728
3729 if (!enable)
3730 abort ();
3731
3732 IT_menu_make_room (menu);
3733 menu->submenu[menu->count] = IT_menu_create ();
3734 menu->text[menu->count] = txt;
3735 menu->panenumber[menu->count] = ++menu->panecount;
3736 menu->help_text[menu->count] = NULL;
3737 menu->count++;
3738
3739 /* Adjust length for possible control characters (which will
3740 be written as ^x). */
3741 for (len = strlen (txt), p = txt; *p; p++)
3742 if (*p < 27)
3743 len++;
3744
3745 if (len > menu->width)
3746 menu->width = len;
3747
3748 return menu->panecount;
3749 }
3750
3751 /* Create a new item in a menu pane. */
3752
3753 int
3754 XMenuAddSelection (Display *bar, XMenu *menu, int pane,
3755 int foo, char *txt, int enable, char *help_text)
3756 {
3757 int len;
3758 char *p;
3759
3760 if (pane)
3761 if (!(menu = IT_menu_search_pane (menu, pane)))
3762 return XM_FAILURE;
3763 IT_menu_make_room (menu);
3764 menu->submenu[menu->count] = (XMenu *) 0;
3765 menu->text[menu->count] = txt;
3766 menu->panenumber[menu->count] = enable;
3767 menu->help_text[menu->count] = help_text;
3768 menu->count++;
3769
3770 /* Adjust length for possible control characters (which will
3771 be written as ^x). */
3772 for (len = strlen (txt), p = txt; *p; p++)
3773 if (*p < 27)
3774 len++;
3775
3776 if (len > menu->width)
3777 menu->width = len;
3778
3779 return XM_SUCCESS;
3780 }
3781
3782 /* Decide where the menu would be placed if requested at (X,Y). */
3783
3784 void
3785 XMenuLocate (Display *foo0, XMenu *menu, int foo1, int foo2, int x, int y,
3786 int *ulx, int *uly, int *width, int *height)
3787 {
3788 IT_menu_calc_size (menu, width, height);
3789 *ulx = x + 1;
3790 *uly = y;
3791 *width += 2;
3792 }
3793
3794 struct IT_menu_state
3795 {
3796 void *screen_behind;
3797 XMenu *menu;
3798 int pane;
3799 int x, y;
3800 };
3801
3802
3803 /* Display menu, wait for user's response, and return that response. */
3804
3805 int
3806 XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx,
3807 int x0, int y0, unsigned ButtonMask, char **txt,
3808 void (*help_callback)(char *, int, int))
3809 {
3810 struct IT_menu_state *state;
3811 int statecount;
3812 int x, y, i, b;
3813 int screensize;
3814 int faces[4];
3815 Lisp_Object selectface;
3816 int leave, result, onepane;
3817 int title_faces[4]; /* face to display the menu title */
3818 int buffers_num_deleted = 0;
3819 struct frame *sf = SELECTED_FRAME();
3820 Lisp_Object saved_echo_area_message;
3821
3822 /* Just in case we got here without a mouse present... */
3823 if (have_mouse <= 0)
3824 return XM_IA_SELECT;
3825 /* Don't allow non-positive x0 and y0, lest the menu will wrap
3826 around the display. */
3827 if (x0 <= 0)
3828 x0 = 1;
3829 if (y0 <= 0)
3830 y0 = 1;
3831
3832 /* We will process all the mouse events directly, so we had
3833 better prevent dos_rawgetc from stealing them from us. */
3834 mouse_preempted++;
3835
3836 state = alloca (menu->panecount * sizeof (struct IT_menu_state));
3837 screensize = screen_size * 2;
3838 faces[0]
3839 = lookup_derived_face (sf, intern ("msdos-menu-passive-face"),
3840 0, DEFAULT_FACE_ID);
3841 faces[1]
3842 = lookup_derived_face (sf, intern ("msdos-menu-active-face"),
3843 0, DEFAULT_FACE_ID);
3844 selectface = intern ("msdos-menu-select-face");
3845 faces[2] = lookup_derived_face (sf, selectface,
3846 0, faces[0]);
3847 faces[3] = lookup_derived_face (sf, selectface,
3848 0, faces[1]);
3849
3850 /* Make sure the menu title is always displayed with
3851 `msdos-menu-active-face', no matter where the mouse pointer is. */
3852 for (i = 0; i < 4; i++)
3853 title_faces[i] = faces[3];
3854
3855 statecount = 1;
3856
3857 /* Don't let the title for the "Buffers" popup menu include a
3858 digit (which is ugly).
3859
3860 This is a terrible kludge, but I think the "Buffers" case is
3861 the only one where the title includes a number, so it doesn't
3862 seem to be necessary to make this more general. */
3863 if (strncmp (menu->text[0], "Buffers 1", 9) == 0)
3864 {
3865 menu->text[0][7] = '\0';
3866 buffers_num_deleted = 1;
3867 }
3868
3869 /* We need to save the current echo area message, so that we could
3870 restore it below, before we exit. See the commentary below,
3871 before the call to message_with_string. */
3872 saved_echo_area_message = Fcurrent_message ();
3873 state[0].menu = menu;
3874 mouse_off ();
3875 ScreenRetrieve (state[0].screen_behind = xmalloc (screensize));
3876
3877 /* Turn off the cursor. Otherwise it shows through the menu
3878 panes, which is ugly. */
3879 IT_display_cursor (0);
3880
3881 /* Display the menu title. */
3882 IT_menu_display (menu, y0 - 1, x0 - 1, 1, title_faces, 0);
3883 if (buffers_num_deleted)
3884 menu->text[0][7] = ' ';
3885 if ((onepane = menu->count == 1 && menu->submenu[0]))
3886 {
3887 menu->width = menu->submenu[0]->width;
3888 state[0].menu = menu->submenu[0];
3889 }
3890 else
3891 {
3892 state[0].menu = menu;
3893 }
3894 state[0].x = x0 - 1;
3895 state[0].y = y0;
3896 state[0].pane = onepane;
3897
3898 mouse_last_x = -1; /* A hack that forces display. */
3899 leave = 0;
3900 while (!leave)
3901 {
3902 if (!mouse_visible) mouse_on ();
3903 mouse_check_moved ();
3904 if (sf->mouse_moved)
3905 {
3906 sf->mouse_moved = 0;
3907 result = XM_IA_SELECT;
3908 mouse_get_xy (&x, &y);
3909 for (i = 0; i < statecount; i++)
3910 if (state[i].x <= x && x < state[i].x + state[i].menu->width + 2)
3911 {
3912 int dy = y - state[i].y;
3913 if (0 <= dy && dy < state[i].menu->count)
3914 {
3915 if (!state[i].menu->submenu[dy])
3916 if (state[i].menu->panenumber[dy])
3917 result = XM_SUCCESS;
3918 else
3919 result = XM_IA_SELECT;
3920 *pane = state[i].pane - 1;
3921 *selidx = dy;
3922 /* We hit some part of a menu, so drop extra menus that
3923 have been opened. That does not include an open and
3924 active submenu. */
3925 if (i != statecount - 2
3926 || state[i].menu->submenu[dy] != state[i+1].menu)
3927 while (i != statecount - 1)
3928 {
3929 statecount--;
3930 mouse_off ();
3931 ScreenUpdate (state[statecount].screen_behind);
3932 if (screen_virtual_segment)
3933 dosv_refresh_virtual_screen (0, screen_size);
3934 xfree (state[statecount].screen_behind);
3935 }
3936 if (i == statecount - 1 && state[i].menu->submenu[dy])
3937 {
3938 IT_menu_display (state[i].menu,
3939 state[i].y,
3940 state[i].x,
3941 state[i].pane,
3942 faces, 1);
3943 state[statecount].menu = state[i].menu->submenu[dy];
3944 state[statecount].pane = state[i].menu->panenumber[dy];
3945 mouse_off ();
3946 ScreenRetrieve (state[statecount].screen_behind
3947 = xmalloc (screensize));
3948 state[statecount].x
3949 = state[i].x + state[i].menu->width + 2;
3950 state[statecount].y = y;
3951 statecount++;
3952 }
3953 }
3954 }
3955 IT_menu_display (state[statecount - 1].menu,
3956 state[statecount - 1].y,
3957 state[statecount - 1].x,
3958 state[statecount - 1].pane,
3959 faces, 1);
3960 }
3961 else
3962 {
3963 if ((menu_help_message || prev_menu_help_message)
3964 && menu_help_message != prev_menu_help_message)
3965 {
3966 help_callback (menu_help_message,
3967 menu_help_paneno, menu_help_itemno);
3968 IT_display_cursor (0);
3969 prev_menu_help_message = menu_help_message;
3970 }
3971 /* We are busy-waiting for the mouse to move, so let's be nice
3972 to other Windows applications by releasing our time slice. */
3973 __dpmi_yield ();
3974 }
3975 for (b = 0; b < mouse_button_count && !leave; b++)
3976 {
3977 /* Only leave if user both pressed and released the mouse, and in
3978 that order. This avoids popping down the menu pane unless
3979 the user is really done with it. */
3980 if (mouse_pressed (b, &x, &y))
3981 {
3982 while (mouse_button_depressed (b, &x, &y))
3983 __dpmi_yield ();
3984 leave = 1;
3985 }
3986 (void) mouse_released (b, &x, &y);
3987 }
3988 }
3989
3990 mouse_off ();
3991 ScreenUpdate (state[0].screen_behind);
3992 if (screen_virtual_segment)
3993 dosv_refresh_virtual_screen (0, screen_size);
3994
3995 /* We have a situation here. ScreenUpdate has just restored the
3996 screen contents as it was before we started drawing this menu.
3997 That includes any echo area message that could have been
3998 displayed back then. (In reality, that echo area message will
3999 almost always be the ``keystroke echo'' that echoes the sequence
4000 of menu items chosen by the user.) However, if the menu had some
4001 help messages, then displaying those messages caused Emacs to
4002 forget about the original echo area message. So when
4003 ScreenUpdate restored it, it created a discrepancy between the
4004 actual screen contents and what Emacs internal data structures
4005 know about it.
4006
4007 To avoid this conflict, we force Emacs to restore the original
4008 echo area message as we found it when we entered this function.
4009 The irony of this is that we then erase the restored message
4010 right away, so the only purpose of restoring it is so that
4011 erasing it works correctly... */
4012 if (! NILP (saved_echo_area_message))
4013 message_with_string ("%s", saved_echo_area_message, 0);
4014 message (0);
4015 while (statecount--)
4016 xfree (state[statecount].screen_behind);
4017 IT_display_cursor (1); /* turn cursor back on */
4018 /* Clean up any mouse events that are waiting inside Emacs event queue.
4019 These events are likely to be generated before the menu was even
4020 displayed, probably because the user pressed and released the button
4021 (which invoked the menu) too quickly. If we don't remove these events,
4022 Emacs will process them after we return and surprise the user. */
4023 discard_mouse_events ();
4024 mouse_clear_clicks ();
4025 if (!kbd_buffer_events_waiting (1))
4026 clear_input_pending ();
4027 /* Allow mouse events generation by dos_rawgetc. */
4028 mouse_preempted--;
4029 return result;
4030 }
4031
4032 /* Dispose of a menu. */
4033
4034 void
4035 XMenuDestroy (Display *foo, XMenu *menu)
4036 {
4037 int i;
4038 if (menu->allocated)
4039 {
4040 for (i = 0; i < menu->count; i++)
4041 if (menu->submenu[i])
4042 XMenuDestroy (foo, menu->submenu[i]);
4043 xfree (menu->text);
4044 xfree (menu->submenu);
4045 xfree (menu->panenumber);
4046 xfree (menu->help_text);
4047 }
4048 xfree (menu);
4049 menu_help_message = prev_menu_help_message = NULL;
4050 }
4051
4052 int
4053 x_pixel_width (struct frame *f)
4054 {
4055 return FRAME_WIDTH (f);
4056 }
4057
4058 int
4059 x_pixel_height (struct frame *f)
4060 {
4061 return FRAME_HEIGHT (f);
4062 }
4063 #endif /* !HAVE_X_WINDOWS */
4064 \f
4065 /* ----------------------- DOS / UNIX conversion --------------------- */
4066
4067 void msdos_downcase_filename (unsigned char *);
4068
4069 /* Destructively turn backslashes into slashes. */
4070
4071 void
4072 dostounix_filename (p)
4073 register char *p;
4074 {
4075 msdos_downcase_filename (p);
4076
4077 while (*p)
4078 {
4079 if (*p == '\\')
4080 *p = '/';
4081 p++;
4082 }
4083 }
4084
4085 /* Destructively turn slashes into backslashes. */
4086
4087 void
4088 unixtodos_filename (p)
4089 register char *p;
4090 {
4091 if (p[1] == ':' && *p >= 'A' && *p <= 'Z')
4092 {
4093 *p += 'a' - 'A';
4094 p += 2;
4095 }
4096
4097 while (*p)
4098 {
4099 if (*p == '/')
4100 *p = '\\';
4101 p++;
4102 }
4103 }
4104
4105 /* Get the default directory for a given drive. 0=def, 1=A, 2=B, ... */
4106
4107 int
4108 getdefdir (drive, dst)
4109 int drive;
4110 char *dst;
4111 {
4112 char in_path[4], *p = in_path;
4113 int e = errno;
4114
4115 /* Generate "X:." (when drive is X) or "." (when drive is 0). */
4116 if (drive != 0)
4117 {
4118 *p++ = drive + 'A' - 1;
4119 *p++ = ':';
4120 }
4121
4122 *p++ = '.';
4123 *p = '\0';
4124 errno = 0;
4125 _fixpath (in_path, dst);
4126 /* _fixpath can set errno to ENOSYS on non-LFN systems because
4127 it queries the LFN support, so ignore that error. */
4128 if ((errno && errno != ENOSYS) || *dst == '\0')
4129 return 0;
4130
4131 msdos_downcase_filename (dst);
4132
4133 errno = e;
4134 return 1;
4135 }
4136
4137 /* Remove all CR's that are followed by a LF. */
4138
4139 int
4140 crlf_to_lf (n, buf)
4141 register int n;
4142 register unsigned char *buf;
4143 {
4144 unsigned char *np = buf;
4145 unsigned char *startp = buf;
4146 unsigned char *endp = buf + n;
4147
4148 if (n == 0)
4149 return n;
4150 while (buf < endp - 1)
4151 {
4152 if (*buf == 0x0d)
4153 {
4154 if (*(++buf) != 0x0a)
4155 *np++ = 0x0d;
4156 }
4157 else
4158 *np++ = *buf++;
4159 }
4160 if (buf < endp)
4161 *np++ = *buf++;
4162 return np - startp;
4163 }
4164
4165 #if defined(__DJGPP__) && __DJGPP__ == 2 && __DJGPP_MINOR__ == 0
4166
4167 /* In DJGPP v2.0, library `write' can call `malloc', which might
4168 cause relocation of the buffer whose address we get in ADDR.
4169 Here is a version of `write' that avoids calling `malloc',
4170 to serve us until such time as the library is fixed.
4171 Actually, what we define here is called `__write', because
4172 `write' is a stub that just jmp's to `__write' (to be
4173 POSIXLY-correct with respect to the global name-space). */
4174
4175 #include <io.h> /* for _write */
4176 #include <libc/dosio.h> /* for __file_handle_modes[] */
4177
4178 static char xbuf[64 * 1024]; /* DOS cannot write more in one chunk */
4179
4180 #define XBUF_END (xbuf + sizeof (xbuf) - 1)
4181
4182 int
4183 __write (int handle, const void *buffer, size_t count)
4184 {
4185 if (count == 0)
4186 return 0;
4187
4188 if(__file_handle_modes[handle] & O_BINARY)
4189 return _write (handle, buffer, count);
4190 else
4191 {
4192 char *xbp = xbuf;
4193 const char *bp = buffer;
4194 int total_written = 0;
4195 int nmoved = 0, ncr = 0;
4196
4197 while (count)
4198 {
4199 /* The next test makes sure there's space for at least 2 more
4200 characters in xbuf[], so both CR and LF can be put there. */
4201 if (xbp < XBUF_END)
4202 {
4203 if (*bp == '\n')
4204 {
4205 ncr++;
4206 *xbp++ = '\r';
4207 }
4208 *xbp++ = *bp++;
4209 nmoved++;
4210 count--;
4211 }
4212 if (xbp >= XBUF_END || !count)
4213 {
4214 size_t to_write = nmoved + ncr;
4215 int written = _write (handle, xbuf, to_write);
4216
4217 if (written == -1)
4218 return -1;
4219 else
4220 total_written += nmoved; /* CRs aren't counted in ret value */
4221
4222 /* If some, but not all were written (disk full?), return
4223 an estimate of the total written bytes not counting CRs. */
4224 if (written < to_write)
4225 return total_written - (to_write - written) * nmoved/to_write;
4226
4227 nmoved = 0;
4228 ncr = 0;
4229 xbp = xbuf;
4230 }
4231 }
4232 return total_written;
4233 }
4234 }
4235
4236 /* A low-level file-renaming function which works around Windows 95 bug.
4237 This is pulled directly out of DJGPP v2.01 library sources, and only
4238 used when you compile with DJGPP v2.0. */
4239
4240 #include <io.h>
4241
4242 int _rename(const char *old, const char *new)
4243 {
4244 __dpmi_regs r;
4245 int olen = strlen(old) + 1;
4246 int i;
4247 int use_lfn = _USE_LFN;
4248 char tempfile[FILENAME_MAX];
4249 const char *orig = old;
4250 int lfn_fd = -1;
4251
4252 r.x.dx = __tb_offset;
4253 r.x.di = __tb_offset + olen;
4254 r.x.ds = r.x.es = __tb_segment;
4255
4256 if (use_lfn)
4257 {
4258 /* Windows 95 bug: for some filenames, when you rename
4259 file -> file~ (as in Emacs, to leave a backup), the
4260 short 8+3 alias doesn't change, which effectively
4261 makes OLD and NEW the same file. We must rename
4262 through a temporary file to work around this. */
4263
4264 char *pbase = 0, *p;
4265 static char try_char[] = "abcdefghijklmnopqrstuvwxyz012345789";
4266 int idx = sizeof(try_char) - 1;
4267
4268 /* Generate a temporary name. Can't use `tmpnam', since $TMPDIR
4269 might point to another drive, which will fail the DOS call. */
4270 strcpy(tempfile, old);
4271 for (p = tempfile; *p; p++) /* ensure temporary is on the same drive */
4272 if (*p == '/' || *p == '\\' || *p == ':')
4273 pbase = p;
4274 if (pbase)
4275 pbase++;
4276 else
4277 pbase = tempfile;
4278 strcpy(pbase, "X$$djren$$.$$temp$$");
4279
4280 do
4281 {
4282 if (idx <= 0)
4283 return -1;
4284 *pbase = try_char[--idx];
4285 } while (_chmod(tempfile, 0) != -1);
4286
4287 r.x.ax = 0x7156;
4288 _put_path2(tempfile, olen);
4289 _put_path(old);
4290 __dpmi_int(0x21, &r);
4291 if (r.x.flags & 1)
4292 {
4293 errno = __doserr_to_errno(r.x.ax);
4294 return -1;
4295 }
4296
4297 /* Now create a file with the original name. This will
4298 ensure that NEW will always have a 8+3 alias
4299 different from that of OLD. (Seems to be required
4300 when NameNumericTail in the Registry is set to 0.) */
4301 lfn_fd = _creat(old, 0);
4302
4303 olen = strlen(tempfile) + 1;
4304 old = tempfile;
4305 r.x.di = __tb_offset + olen;
4306 }
4307
4308 for (i=0; i<2; i++)
4309 {
4310 if(use_lfn)
4311 r.x.ax = 0x7156;
4312 else
4313 r.h.ah = 0x56;
4314 _put_path2(new, olen);
4315 _put_path(old);
4316 __dpmi_int(0x21, &r);
4317 if(r.x.flags & 1)
4318 {
4319 if (r.x.ax == 5 && i == 0) /* access denied */
4320 remove(new); /* and try again */
4321 else
4322 {
4323 errno = __doserr_to_errno(r.x.ax);
4324
4325 /* Restore to original name if we renamed it to temporary. */
4326 if (use_lfn)
4327 {
4328 if (lfn_fd != -1)
4329 {
4330 _close (lfn_fd);
4331 remove (orig);
4332 }
4333 _put_path2(orig, olen);
4334 _put_path(tempfile);
4335 r.x.ax = 0x7156;
4336 __dpmi_int(0x21, &r);
4337 }
4338 return -1;
4339 }
4340 }
4341 else
4342 break;
4343 }
4344
4345 /* Success. Delete the file possibly created to work
4346 around the Windows 95 bug. */
4347 if (lfn_fd != -1)
4348 return (_close (lfn_fd) == 0) ? remove (orig) : -1;
4349 return 0;
4350 }
4351
4352 #endif /* __DJGPP__ == 2 && __DJGPP_MINOR__ == 0 */
4353
4354 DEFUN ("msdos-long-file-names", Fmsdos_long_file_names, Smsdos_long_file_names,
4355 0, 0, 0,
4356 "Return non-nil if long file names are supported on MSDOS.")
4357 ()
4358 {
4359 return (_USE_LFN ? Qt : Qnil);
4360 }
4361
4362 /* Convert alphabetic characters in a filename to lower-case. */
4363
4364 void
4365 msdos_downcase_filename (p)
4366 register unsigned char *p;
4367 {
4368 /* Always lower-case drive letters a-z, even if the filesystem
4369 preserves case in filenames.
4370 This is so MSDOS filenames could be compared by string comparison
4371 functions that are case-sensitive. Even case-preserving filesystems
4372 do not distinguish case in drive letters. */
4373 if (p[1] == ':' && *p >= 'A' && *p <= 'Z')
4374 {
4375 *p += 'a' - 'A';
4376 p += 2;
4377 }
4378
4379 /* Under LFN we expect to get pathnames in their true case. */
4380 if (NILP (Fmsdos_long_file_names ()))
4381 for ( ; *p; p++)
4382 if (*p >= 'A' && *p <= 'Z')
4383 *p += 'a' - 'A';
4384 }
4385
4386 DEFUN ("msdos-downcase-filename", Fmsdos_downcase_filename, Smsdos_downcase_filename,
4387 1, 1, 0,
4388 "Convert alphabetic characters in FILENAME to lower case and return that.\n\
4389 When long filenames are supported, doesn't change FILENAME.\n\
4390 If FILENAME is not a string, returns nil.\n\
4391 The argument object is never altered--the value is a copy.")
4392 (filename)
4393 Lisp_Object filename;
4394 {
4395 Lisp_Object tem;
4396
4397 if (! STRINGP (filename))
4398 return Qnil;
4399
4400 tem = Fcopy_sequence (filename);
4401 msdos_downcase_filename (XSTRING (tem)->data);
4402 return tem;
4403 }
4404 \f
4405 /* The Emacs root directory as determined by init_environment. */
4406
4407 static char emacsroot[MAXPATHLEN];
4408
4409 char *
4410 rootrelativepath (rel)
4411 char *rel;
4412 {
4413 static char result[MAXPATHLEN + 10];
4414
4415 strcpy (result, emacsroot);
4416 strcat (result, "/");
4417 strcat (result, rel);
4418 return result;
4419 }
4420
4421 /* Define a lot of environment variables if not already defined. Don't
4422 remove anything unless you know what you're doing -- lots of code will
4423 break if one or more of these are missing. */
4424
4425 void
4426 init_environment (argc, argv, skip_args)
4427 int argc;
4428 char **argv;
4429 int skip_args;
4430 {
4431 char *s, *t, *root;
4432 int len;
4433 static const char * const tempdirs[] = {
4434 "$TMPDIR", "$TEMP", "$TMP", "c:/"
4435 };
4436 int i;
4437 const int imax = sizeof (tempdirs) / sizeof (tempdirs[0]);
4438
4439 /* Make sure they have a usable $TMPDIR. Many Emacs functions use
4440 temporary files and assume "/tmp" if $TMPDIR is unset, which
4441 will break on DOS/Windows. Refuse to work if we cannot find
4442 a directory, not even "c:/", usable for that purpose. */
4443 for (i = 0; i < imax ; i++)
4444 {
4445 const char *tmp = tempdirs[i];
4446
4447 if (*tmp == '$')
4448 tmp = getenv (tmp + 1);
4449 /* Note that `access' can lie to us if the directory resides on a
4450 read-only filesystem, like CD-ROM or a write-protected floppy.
4451 The only way to be really sure is to actually create a file and
4452 see if it succeeds. But I think that's too much to ask. */
4453 if (tmp && access (tmp, D_OK) == 0)
4454 {
4455 setenv ("TMPDIR", tmp, 1);
4456 break;
4457 }
4458 }
4459 if (i >= imax)
4460 cmd_error_internal
4461 (Fcons (Qerror,
4462 Fcons (build_string ("no usable temporary directories found!!"),
4463 Qnil)),
4464 "While setting TMPDIR: ");
4465
4466 /* Note the startup time, so we know not to clear the screen if we
4467 exit immediately; see IT_reset_terminal_modes.
4468 (Yes, I know `clock' returns zero the first time it's called, but
4469 I do this anyway, in case some wiseguy changes that at some point.) */
4470 startup_time = clock ();
4471
4472 /* Find our root from argv[0]. Assuming argv[0] is, say,
4473 "c:/emacs/bin/emacs.exe" our root will be "c:/emacs". */
4474 root = alloca (MAXPATHLEN + 20);
4475 _fixpath (argv[0], root);
4476 msdos_downcase_filename (root);
4477 len = strlen (root);
4478 while (len > 0 && root[len] != '/' && root[len] != ':')
4479 len--;
4480 root[len] = '\0';
4481 if (len > 4
4482 && (strcmp (root + len - 4, "/bin") == 0
4483 || strcmp (root + len - 4, "/src") == 0)) /* under a debugger */
4484 root[len - 4] = '\0';
4485 else
4486 strcpy (root, "c:/emacs"); /* let's be defensive */
4487 len = strlen (root);
4488 strcpy (emacsroot, root);
4489
4490 /* We default HOME to our root. */
4491 setenv ("HOME", root, 0);
4492
4493 /* We default EMACSPATH to root + "/bin". */
4494 strcpy (root + len, "/bin");
4495 setenv ("EMACSPATH", root, 0);
4496
4497 /* I don't expect anybody to ever use other terminals so the internal
4498 terminal is the default. */
4499 setenv ("TERM", "internal", 0);
4500
4501 #ifdef HAVE_X_WINDOWS
4502 /* Emacs expects DISPLAY to be set. */
4503 setenv ("DISPLAY", "unix:0.0", 0);
4504 #endif
4505
4506 /* SHELL is a bit tricky -- COMSPEC is the closest we come, but we must
4507 downcase it and mirror the backslashes. */
4508 s = getenv ("COMSPEC");
4509 if (!s) s = "c:/command.com";
4510 t = alloca (strlen (s) + 1);
4511 strcpy (t, s);
4512 dostounix_filename (t);
4513 setenv ("SHELL", t, 0);
4514
4515 /* PATH is also downcased and backslashes mirrored. */
4516 s = getenv ("PATH");
4517 if (!s) s = "";
4518 t = alloca (strlen (s) + 3);
4519 /* Current directory is always considered part of MsDos's path but it is
4520 not normally mentioned. Now it is. */
4521 strcat (strcpy (t, ".;"), s);
4522 dostounix_filename (t); /* Not a single file name, but this should work. */
4523 setenv ("PATH", t, 1);
4524
4525 /* In some sense all dos users have root privileges, so... */
4526 setenv ("USER", "root", 0);
4527 setenv ("NAME", getenv ("USER"), 0);
4528
4529 /* Time zone determined from country code. To make this possible, the
4530 country code may not span more than one time zone. In other words,
4531 in the USA, you lose. */
4532 if (!getenv ("TZ"))
4533 switch (dos_country_code)
4534 {
4535 case 31: /* Belgium */
4536 case 32: /* The Netherlands */
4537 case 33: /* France */
4538 case 34: /* Spain */
4539 case 36: /* Hungary */
4540 case 38: /* Yugoslavia (or what's left of it?) */
4541 case 39: /* Italy */
4542 case 41: /* Switzerland */
4543 case 42: /* Tjekia */
4544 case 45: /* Denmark */
4545 case 46: /* Sweden */
4546 case 47: /* Norway */
4547 case 48: /* Poland */
4548 case 49: /* Germany */
4549 /* Daylight saving from last Sunday in March to last Sunday in
4550 September, both at 2AM. */
4551 setenv ("TZ", "MET-01METDST-02,M3.5.0/02:00,M9.5.0/02:00", 0);
4552 break;
4553 case 44: /* United Kingdom */
4554 case 351: /* Portugal */
4555 case 354: /* Iceland */
4556 setenv ("TZ", "GMT+00", 0);
4557 break;
4558 case 81: /* Japan */
4559 case 82: /* Korea */
4560 setenv ("TZ", "JST-09", 0);
4561 break;
4562 case 90: /* Turkey */
4563 case 358: /* Finland */
4564 setenv ("TZ", "EET-02", 0);
4565 break;
4566 case 972: /* Israel */
4567 /* This is an approximation. (For exact rules, use the
4568 `zoneinfo/israel' file which comes with DJGPP, but you need
4569 to install it in `/usr/share/zoneinfo/' directory first.) */
4570 setenv ("TZ", "IST-02IDT-03,M4.1.6/00:00,M9.5.6/01:00", 0);
4571 break;
4572 }
4573 tzset ();
4574 }
4575
4576 \f
4577
4578 static int break_stat; /* BREAK check mode status. */
4579 static int stdin_stat; /* stdin IOCTL status. */
4580
4581 #if __DJGPP__ < 2
4582
4583 /* These must be global. */
4584 static _go32_dpmi_seginfo ctrl_break_vector;
4585 static _go32_dpmi_registers ctrl_break_regs;
4586 static int ctrlbreakinstalled = 0;
4587
4588 /* Interrupt level detection of Ctrl-Break. Don't do anything fancy here! */
4589
4590 void
4591 ctrl_break_func (regs)
4592 _go32_dpmi_registers *regs;
4593 {
4594 Vquit_flag = Qt;
4595 }
4596
4597 void
4598 install_ctrl_break_check ()
4599 {
4600 if (!ctrlbreakinstalled)
4601 {
4602 /* Don't press Ctrl-Break if you don't have either DPMI or Emacs
4603 was compiler with Djgpp 1.11 maintenance level 5 or later! */
4604 ctrlbreakinstalled = 1;
4605 ctrl_break_vector.pm_offset = (int) ctrl_break_func;
4606 _go32_dpmi_allocate_real_mode_callback_iret (&ctrl_break_vector,
4607 &ctrl_break_regs);
4608 _go32_dpmi_set_real_mode_interrupt_vector (0x1b, &ctrl_break_vector);
4609 }
4610 }
4611
4612 #endif /* __DJGPP__ < 2 */
4613
4614 /* Turn off Dos' Ctrl-C checking and inhibit interpretation of
4615 control chars by DOS. Determine the keyboard type. */
4616
4617 int
4618 dos_ttraw ()
4619 {
4620 union REGS inregs, outregs;
4621 static int first_time = 1;
4622
4623 break_stat = getcbrk ();
4624 setcbrk (0);
4625 #if __DJGPP__ < 2
4626 install_ctrl_break_check ();
4627 #endif
4628
4629 if (first_time)
4630 {
4631 inregs.h.ah = 0xc0;
4632 int86 (0x15, &inregs, &outregs);
4633 extended_kbd = (!outregs.x.cflag) && (outregs.h.ah == 0);
4634
4635 have_mouse = 0;
4636
4637 if (internal_terminal
4638 #ifdef HAVE_X_WINDOWS
4639 && inhibit_window_system
4640 #endif
4641 )
4642 {
4643 inregs.x.ax = 0x0021;
4644 int86 (0x33, &inregs, &outregs);
4645 have_mouse = (outregs.x.ax & 0xffff) == 0xffff;
4646 if (!have_mouse)
4647 {
4648 /* Reportedly, the above doesn't work for some mouse drivers. There
4649 is an additional detection method that should work, but might be
4650 a little slower. Use that as an alternative. */
4651 inregs.x.ax = 0x0000;
4652 int86 (0x33, &inregs, &outregs);
4653 have_mouse = (outregs.x.ax & 0xffff) == 0xffff;
4654 }
4655
4656 if (have_mouse)
4657 {
4658 have_mouse = 1; /* enable mouse */
4659 mouse_visible = 0;
4660 mouse_setup_buttons (outregs.x.bx);
4661 mouse_position_hook = &mouse_get_pos;
4662 mouse_init ();
4663 }
4664
4665 #ifndef HAVE_X_WINDOWS
4666 #if __DJGPP__ >= 2
4667 /* Save the cursor shape used outside Emacs. */
4668 outside_cursor = _farpeekw (_dos_ds, 0x460);
4669 #endif
4670 #endif
4671 }
4672
4673 first_time = 0;
4674
4675 #if __DJGPP__ >= 2
4676
4677 stdin_stat = setmode (fileno (stdin), O_BINARY);
4678 return (stdin_stat != -1);
4679 }
4680 else
4681 return (setmode (fileno (stdin), O_BINARY) != -1);
4682
4683 #else /* __DJGPP__ < 2 */
4684
4685 }
4686
4687 /* I think it is wrong to overwrite `stdin_stat' every time
4688 but the first one this function is called, but I don't
4689 want to change the way it used to work in v1.x.--EZ */
4690
4691 inregs.x.ax = 0x4400; /* Get IOCTL status. */
4692 inregs.x.bx = 0x00; /* 0 = stdin. */
4693 intdos (&inregs, &outregs);
4694 stdin_stat = outregs.h.dl;
4695
4696 inregs.x.dx = stdin_stat | 0x0020; /* raw mode */
4697 inregs.x.ax = 0x4401; /* Set IOCTL status */
4698 intdos (&inregs, &outregs);
4699 return !outregs.x.cflag;
4700
4701 #endif /* __DJGPP__ < 2 */
4702 }
4703
4704 /* Restore status of standard input and Ctrl-C checking. */
4705
4706 int
4707 dos_ttcooked ()
4708 {
4709 union REGS inregs, outregs;
4710
4711 setcbrk (break_stat);
4712 mouse_off ();
4713
4714 #if __DJGPP__ >= 2
4715
4716 #ifndef HAVE_X_WINDOWS
4717 /* Restore the cursor shape we found on startup. */
4718 if (outside_cursor)
4719 {
4720 inregs.h.ah = 1;
4721 inregs.x.cx = outside_cursor;
4722 int86 (0x10, &inregs, &outregs);
4723 }
4724 #endif
4725
4726 return (setmode (fileno (stdin), stdin_stat) != -1);
4727
4728 #else /* not __DJGPP__ >= 2 */
4729
4730 inregs.x.ax = 0x4401; /* Set IOCTL status. */
4731 inregs.x.bx = 0x00; /* 0 = stdin. */
4732 inregs.x.dx = stdin_stat;
4733 intdos (&inregs, &outregs);
4734 return !outregs.x.cflag;
4735
4736 #endif /* not __DJGPP__ >= 2 */
4737 }
4738
4739 \f
4740 /* Run command as specified by ARGV in directory DIR.
4741 The command is run with input from TEMPIN, output to
4742 file TEMPOUT and stderr to TEMPERR. */
4743
4744 int
4745 run_msdos_command (argv, working_dir, tempin, tempout, temperr, envv)
4746 unsigned char **argv;
4747 const char *working_dir;
4748 int tempin, tempout, temperr;
4749 char **envv;
4750 {
4751 char *saveargv1, *saveargv2, *lowcase_argv0, *pa, *pl;
4752 char oldwd[MAXPATHLEN + 1]; /* Fixed size is safe on MSDOS. */
4753 int msshell, result = -1;
4754 int inbak, outbak, errbak;
4755 int x, y;
4756 Lisp_Object cmd;
4757
4758 /* Get current directory as MSDOS cwd is not per-process. */
4759 getwd (oldwd);
4760
4761 /* If argv[0] is the shell, it might come in any lettercase.
4762 Since `Fmember' is case-sensitive, we need to downcase
4763 argv[0], even if we are on case-preserving filesystems. */
4764 lowcase_argv0 = alloca (strlen (argv[0]) + 1);
4765 for (pa = argv[0], pl = lowcase_argv0; *pa; pl++)
4766 {
4767 *pl = *pa++;
4768 if (*pl >= 'A' && *pl <= 'Z')
4769 *pl += 'a' - 'A';
4770 }
4771 *pl = '\0';
4772
4773 cmd = Ffile_name_nondirectory (build_string (lowcase_argv0));
4774 msshell = !NILP (Fmember (cmd, Fsymbol_value (intern ("msdos-shells"))))
4775 && !strcmp ("-c", argv[1]);
4776 if (msshell)
4777 {
4778 saveargv1 = argv[1];
4779 saveargv2 = argv[2];
4780 argv[1] = "/c";
4781 /* We only need to mirror slashes if a DOS shell will be invoked
4782 not via `system' (which does the mirroring itself). Yes, that
4783 means DJGPP v1.x will lose here. */
4784 if (argv[2] && argv[3])
4785 {
4786 char *p = alloca (strlen (argv[2]) + 1);
4787
4788 strcpy (argv[2] = p, saveargv2);
4789 while (*p && isspace (*p))
4790 p++;
4791 while (*p)
4792 {
4793 if (*p == '/')
4794 *p++ = '\\';
4795 else
4796 p++;
4797 }
4798 }
4799 }
4800
4801 chdir (working_dir);
4802 inbak = dup (0);
4803 outbak = dup (1);
4804 errbak = dup (2);
4805 if (inbak < 0 || outbak < 0 || errbak < 0)
4806 goto done; /* Allocation might fail due to lack of descriptors. */
4807
4808 if (have_mouse > 0)
4809 mouse_get_xy (&x, &y);
4810
4811 dos_ttcooked (); /* do it here while 0 = stdin */
4812
4813 dup2 (tempin, 0);
4814 dup2 (tempout, 1);
4815 dup2 (temperr, 2);
4816
4817 #if __DJGPP__ > 1
4818
4819 if (msshell && !argv[3])
4820 {
4821 /* MS-DOS native shells are too restrictive. For starters, they
4822 cannot grok commands longer than 126 characters. In DJGPP v2
4823 and later, `system' is much smarter, so we'll call it instead. */
4824
4825 const char *cmnd;
4826
4827 /* A shell gets a single argument--its full command
4828 line--whose original was saved in `saveargv2'. */
4829
4830 /* Don't let them pass empty command lines to `system', since
4831 with some shells it will try to invoke an interactive shell,
4832 which will hang Emacs. */
4833 for (cmnd = saveargv2; *cmnd && isspace (*cmnd); cmnd++)
4834 ;
4835 if (*cmnd)
4836 {
4837 extern char **environ;
4838 char **save_env = environ;
4839 int save_system_flags = __system_flags;
4840
4841 /* Request the most powerful version of `system'. We need
4842 all the help we can get to avoid calling stock DOS shells. */
4843 __system_flags = (__system_redirect
4844 | __system_use_shell
4845 | __system_allow_multiple_cmds
4846 | __system_allow_long_cmds
4847 | __system_handle_null_commands
4848 | __system_emulate_chdir);
4849
4850 environ = envv;
4851 result = system (cmnd);
4852 __system_flags = save_system_flags;
4853 environ = save_env;
4854 }
4855 else
4856 result = 0; /* emulate Unixy shell behavior with empty cmd line */
4857 }
4858 else
4859
4860 #endif /* __DJGPP__ > 1 */
4861
4862 result = spawnve (P_WAIT, argv[0], argv, envv);
4863
4864 dup2 (inbak, 0);
4865 dup2 (outbak, 1);
4866 dup2 (errbak, 2);
4867 emacs_close (inbak);
4868 emacs_close (outbak);
4869 emacs_close (errbak);
4870
4871 dos_ttraw ();
4872 if (have_mouse > 0)
4873 {
4874 mouse_init ();
4875 mouse_moveto (x, y);
4876 }
4877
4878 /* Some programs might change the meaning of the highest bit of the
4879 text attribute byte, so we get blinking characters instead of the
4880 bright background colors. Restore that. */
4881 bright_bg ();
4882
4883 done:
4884 chdir (oldwd);
4885 if (msshell)
4886 {
4887 argv[1] = saveargv1;
4888 argv[2] = saveargv2;
4889 }
4890 return result;
4891 }
4892
4893 croak (badfunc)
4894 char *badfunc;
4895 {
4896 fprintf (stderr, "%s not yet implemented\r\n", badfunc);
4897 reset_sys_modes ();
4898 exit (1);
4899 }
4900 \f
4901 #if __DJGPP__ < 2
4902
4903 /* ------------------------- Compatibility functions -------------------
4904 * gethostname
4905 * gettimeofday
4906 */
4907
4908 /* Hostnames for a pc are not really funny,
4909 but they are used in change log so we emulate the best we can. */
4910
4911 gethostname (p, size)
4912 char *p;
4913 int size;
4914 {
4915 char *q = egetenv ("HOSTNAME");
4916
4917 if (!q) q = "pc";
4918 strcpy (p, q);
4919 return 0;
4920 }
4921
4922 /* When time zones are set from Ms-Dos too many C-libraries are playing
4923 tricks with time values. We solve this by defining our own version
4924 of `gettimeofday' bypassing GO32. Our version needs to be initialized
4925 once and after each call to `tzset' with TZ changed. That is
4926 accomplished by aliasing tzset to init_gettimeofday. */
4927
4928 static struct tm time_rec;
4929
4930 int
4931 gettimeofday (struct timeval *tp, struct timezone *tzp)
4932 {
4933 if (tp)
4934 {
4935 struct time t;
4936 struct tm tm;
4937
4938 gettime (&t);
4939 if (t.ti_hour < time_rec.tm_hour) /* midnight wrap */
4940 {
4941 struct date d;
4942 getdate (&d);
4943 time_rec.tm_year = d.da_year - 1900;
4944 time_rec.tm_mon = d.da_mon - 1;
4945 time_rec.tm_mday = d.da_day;
4946 }
4947
4948 time_rec.tm_hour = t.ti_hour;
4949 time_rec.tm_min = t.ti_min;
4950 time_rec.tm_sec = t.ti_sec;
4951
4952 tm = time_rec;
4953 tm.tm_gmtoff = dos_timezone_offset;
4954
4955 tp->tv_sec = mktime (&tm); /* may modify tm */
4956 tp->tv_usec = t.ti_hund * (1000000 / 100);
4957 }
4958 /* Ignore tzp; it's obsolescent. */
4959 return 0;
4960 }
4961
4962 #endif /* __DJGPP__ < 2 */
4963
4964 /*
4965 * A list of unimplemented functions that we silently ignore.
4966 */
4967
4968 #if __DJGPP__ < 2
4969 unsigned alarm (s) unsigned s; {}
4970 fork () { return 0; }
4971 int kill (x, y) int x, y; { return -1; }
4972 nice (p) int p; {}
4973 void volatile pause () {}
4974 sigsetmask (x) int x; { return 0; }
4975 sigblock (mask) int mask; { return 0; }
4976 #endif
4977
4978 void request_sigio (void) {}
4979 setpgrp () {return 0; }
4980 setpriority (x,y,z) int x,y,z; { return 0; }
4981 void unrequest_sigio (void) {}
4982
4983 #if __DJGPP__ > 1
4984
4985 #ifdef POSIX_SIGNALS
4986
4987 /* Augment DJGPP library POSIX signal functions. This is needed
4988 as of DJGPP v2.01, but might be in the library in later releases. */
4989
4990 #include <libc/bss.h>
4991
4992 /* A counter to know when to re-initialize the static sets. */
4993 static int sigprocmask_count = -1;
4994
4995 /* Which signals are currently blocked (initially none). */
4996 static sigset_t current_mask;
4997
4998 /* Which signals are pending (initially none). */
4999 static sigset_t pending_signals;
5000
5001 /* Previous handlers to restore when the blocked signals are unblocked. */
5002 typedef void (*sighandler_t)(int);
5003 static sighandler_t prev_handlers[320];
5004
5005 /* A signal handler which just records that a signal occured
5006 (it will be raised later, if and when the signal is unblocked). */
5007 static void
5008 sig_suspender (signo)
5009 int signo;
5010 {
5011 sigaddset (&pending_signals, signo);
5012 }
5013
5014 int
5015 sigprocmask (how, new_set, old_set)
5016 int how;
5017 const sigset_t *new_set;
5018 sigset_t *old_set;
5019 {
5020 int signo;
5021 sigset_t new_mask;
5022
5023 /* If called for the first time, initialize. */
5024 if (sigprocmask_count != __bss_count)
5025 {
5026 sigprocmask_count = __bss_count;
5027 sigemptyset (&pending_signals);
5028 sigemptyset (&current_mask);
5029 for (signo = 0; signo < 320; signo++)
5030 prev_handlers[signo] = SIG_ERR;
5031 }
5032
5033 if (old_set)
5034 *old_set = current_mask;
5035
5036 if (new_set == 0)
5037 return 0;
5038
5039 if (how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK)
5040 {
5041 errno = EINVAL;
5042 return -1;
5043 }
5044
5045 sigemptyset (&new_mask);
5046
5047 /* DJGPP supports upto 320 signals. */
5048 for (signo = 0; signo < 320; signo++)
5049 {
5050 if (sigismember (&current_mask, signo))
5051 sigaddset (&new_mask, signo);
5052 else if (sigismember (new_set, signo) && how != SIG_UNBLOCK)
5053 {
5054 sigaddset (&new_mask, signo);
5055
5056 /* SIGKILL is silently ignored, as on other platforms. */
5057 if (signo != SIGKILL && prev_handlers[signo] == SIG_ERR)
5058 prev_handlers[signo] = signal (signo, sig_suspender);
5059 }
5060 if (( how == SIG_UNBLOCK
5061 && sigismember (&new_mask, signo)
5062 && sigismember (new_set, signo))
5063 || (how == SIG_SETMASK
5064 && sigismember (&new_mask, signo)
5065 && !sigismember (new_set, signo)))
5066 {
5067 sigdelset (&new_mask, signo);
5068 if (prev_handlers[signo] != SIG_ERR)
5069 {
5070 signal (signo, prev_handlers[signo]);
5071 prev_handlers[signo] = SIG_ERR;
5072 }
5073 if (sigismember (&pending_signals, signo))
5074 {
5075 sigdelset (&pending_signals, signo);
5076 raise (signo);
5077 }
5078 }
5079 }
5080 current_mask = new_mask;
5081 return 0;
5082 }
5083
5084 #else /* not POSIX_SIGNALS */
5085
5086 sigsetmask (x) int x; { return 0; }
5087 sigblock (mask) int mask; { return 0; }
5088
5089 #endif /* not POSIX_SIGNALS */
5090 #endif /* __DJGPP__ > 1 */
5091
5092 #ifndef HAVE_SELECT
5093 #include "sysselect.h"
5094
5095 #ifndef EMACS_TIME_ZERO_OR_NEG_P
5096 #define EMACS_TIME_ZERO_OR_NEG_P(time) \
5097 ((long)(time).tv_sec < 0 \
5098 || ((time).tv_sec == 0 \
5099 && (long)(time).tv_usec <= 0))
5100 #endif
5101
5102 /* This yields the rest of the current time slice to the task manager.
5103 It should be called by any code which knows that it has nothing
5104 useful to do except idle.
5105
5106 I don't use __dpmi_yield here, since versions of library before 2.02
5107 called Int 2Fh/AX=1680h there in a way that would wedge the DOS box
5108 on some versions of Windows 9X. */
5109
5110 void
5111 dos_yield_time_slice (void)
5112 {
5113 _go32_dpmi_registers r;
5114
5115 r.x.ax = 0x1680;
5116 r.x.ss = r.x.sp = r.x.flags = 0;
5117 _go32_dpmi_simulate_int (0x2f, &r);
5118 if (r.h.al == 0x80)
5119 errno = ENOSYS;
5120 }
5121
5122 /* Only event queue is checked. */
5123 /* We don't have to call timer_check here
5124 because wait_reading_process_input takes care of that. */
5125 int
5126 sys_select (nfds, rfds, wfds, efds, timeout)
5127 int nfds;
5128 SELECT_TYPE *rfds, *wfds, *efds;
5129 EMACS_TIME *timeout;
5130 {
5131 int check_input;
5132 struct time t;
5133
5134 check_input = 0;
5135 if (rfds)
5136 {
5137 check_input = FD_ISSET (0, rfds);
5138 FD_ZERO (rfds);
5139 }
5140 if (wfds)
5141 FD_ZERO (wfds);
5142 if (efds)
5143 FD_ZERO (efds);
5144
5145 if (nfds != 1)
5146 abort ();
5147
5148 /* If we are looking only for the terminal, with no timeout,
5149 just read it and wait -- that's more efficient. */
5150 if (!timeout)
5151 {
5152 while (!detect_input_pending ())
5153 {
5154 dos_yield_time_slice ();
5155 }
5156 }
5157 else
5158 {
5159 EMACS_TIME clnow, cllast, cldiff;
5160
5161 gettime (&t);
5162 EMACS_SET_SECS_USECS (cllast, t.ti_sec, t.ti_hund * 10000L);
5163
5164 while (!check_input || !detect_input_pending ())
5165 {
5166 gettime (&t);
5167 EMACS_SET_SECS_USECS (clnow, t.ti_sec, t.ti_hund * 10000L);
5168 EMACS_SUB_TIME (cldiff, clnow, cllast);
5169
5170 /* When seconds wrap around, we assume that no more than
5171 1 minute passed since last `gettime'. */
5172 if (EMACS_TIME_NEG_P (cldiff))
5173 EMACS_SET_SECS (cldiff, EMACS_SECS (cldiff) + 60);
5174 EMACS_SUB_TIME (*timeout, *timeout, cldiff);
5175
5176 /* Stop when timeout value crosses zero. */
5177 if (EMACS_TIME_ZERO_OR_NEG_P (*timeout))
5178 return 0;
5179 cllast = clnow;
5180 dos_yield_time_slice ();
5181 }
5182 }
5183
5184 FD_SET (0, rfds);
5185 return 1;
5186 }
5187 #endif
5188
5189 /*
5190 * Define overlaid functions:
5191 *
5192 * chdir -> sys_chdir
5193 * tzset -> init_gettimeofday
5194 * abort -> dos_abort
5195 */
5196
5197 #ifdef chdir
5198 #undef chdir
5199 extern int chdir ();
5200
5201 int
5202 sys_chdir (path)
5203 const char* path;
5204 {
5205 int len = strlen (path);
5206 char *tmp = (char *)path;
5207
5208 if (*tmp && tmp[1] == ':')
5209 {
5210 if (getdisk () != tolower (tmp[0]) - 'a')
5211 setdisk (tolower (tmp[0]) - 'a');
5212 tmp += 2; /* strip drive: KFS 1995-07-06 */
5213 len -= 2;
5214 }
5215
5216 if (len > 1 && (tmp[len - 1] == '/'))
5217 {
5218 char *tmp1 = (char *) alloca (len + 1);
5219 strcpy (tmp1, tmp);
5220 tmp1[len - 1] = 0;
5221 tmp = tmp1;
5222 }
5223 return chdir (tmp);
5224 }
5225 #endif
5226
5227 #ifdef tzset
5228 #undef tzset
5229 extern void tzset (void);
5230
5231 void
5232 init_gettimeofday ()
5233 {
5234 time_t ltm, gtm;
5235 struct tm *lstm;
5236
5237 tzset ();
5238 ltm = gtm = time (NULL);
5239 ltm = mktime (lstm = localtime (&ltm));
5240 gtm = mktime (gmtime (&gtm));
5241 time_rec.tm_hour = 99; /* force gettimeofday to get date */
5242 time_rec.tm_isdst = lstm->tm_isdst;
5243 dos_timezone_offset = time_rec.tm_gmtoff = (int)(gtm - ltm) / 60;
5244 }
5245 #endif
5246
5247 #ifdef abort
5248 #undef abort
5249 void
5250 dos_abort (file, line)
5251 char *file;
5252 int line;
5253 {
5254 char buffer1[200], buffer2[400];
5255 int i, j;
5256
5257 sprintf (buffer1, "<EMACS FATAL ERROR IN %s LINE %d>", file, line);
5258 for (i = j = 0; buffer1[i]; i++) {
5259 buffer2[j++] = buffer1[i];
5260 buffer2[j++] = 0x70;
5261 }
5262 dosmemput (buffer2, j, (int)ScreenPrimary);
5263 ScreenSetCursor (2, 0);
5264 abort ();
5265 }
5266 #else
5267 void
5268 abort ()
5269 {
5270 dos_ttcooked ();
5271 ScreenSetCursor (10, 0);
5272 cputs ("\r\n\nEmacs aborted!\r\n");
5273 #if __DJGPP__ > 1
5274 #if __DJGPP__ == 2 && __DJGPP_MINOR__ < 2
5275 if (screen_virtual_segment)
5276 dosv_refresh_virtual_screen (2 * 10 * screen_size_X, 4 * screen_size_X);
5277 /* Generate traceback, so we could tell whodunit. */
5278 signal (SIGINT, SIG_DFL);
5279 __asm__ __volatile__ ("movb $0x1b,%al;call ___djgpp_hw_exception");
5280 #else /* __DJGPP_MINOR__ >= 2 */
5281 raise (SIGABRT);
5282 #endif /* __DJGPP_MINOR__ >= 2 */
5283 #endif
5284 exit (2);
5285 }
5286 #endif
5287
5288 /* The following variables are required so that cus-start.el won't
5289 complain about unbound variables. */
5290 #ifndef HAVE_X_WINDOWS
5291 /* Search path for bitmap files (xfns.c). */
5292 Lisp_Object Vx_bitmap_file_path;
5293 int x_stretch_cursor_p;
5294 #endif
5295 #ifndef subprocesses
5296 /* Nonzero means delete a process right away if it exits (process.c). */
5297 static int delete_exited_processes;
5298 #endif
5299
5300 syms_of_msdos ()
5301 {
5302 recent_doskeys = Fmake_vector (make_number (NUM_RECENT_DOSKEYS), Qnil);
5303 staticpro (&recent_doskeys);
5304 #ifndef HAVE_X_WINDOWS
5305 help_echo = Qnil;
5306 staticpro (&help_echo);
5307 help_echo_object = Qnil;
5308 staticpro (&help_echo_object);
5309 help_echo_window = Qnil;
5310 staticpro (&help_echo_window);
5311 previous_help_echo = Qnil;
5312 staticpro (&previous_help_echo);
5313 help_echo_pos = -1;
5314
5315 DEFVAR_LISP ("x-bitmap-file-path", &Vx_bitmap_file_path,
5316 "List of directories to search for bitmap files for X.");
5317 Vx_bitmap_file_path = decode_env_path ((char *) 0, ".");
5318
5319 DEFVAR_BOOL ("x-stretch-cursor", &x_stretch_cursor_p,
5320 "*Non-nil means draw block cursor as wide as the glyph under it.\n\
5321 For example, if a block cursor is over a tab, it will be drawn as\n\
5322 wide as that tab on the display. (No effect on MS-DOS.)");
5323 x_stretch_cursor_p = 0;
5324
5325 /* The following two are from xfns.c: */
5326 Qbar = intern ("bar");
5327 staticpro (&Qbar);
5328 Qcursor_type = intern ("cursor-type");
5329 staticpro (&Qcursor_type);
5330 Qreverse = intern ("reverse");
5331 staticpro (&Qreverse);
5332
5333 DEFVAR_LISP ("dos-unsupported-char-glyph", &Vdos_unsupported_char_glyph,
5334 "*Glyph to display instead of chars not supported by current codepage.\n\
5335
5336 This variable is used only by MSDOS terminals.");
5337 Vdos_unsupported_char_glyph = '\177';
5338 #endif
5339 #ifndef subprocesses
5340 DEFVAR_BOOL ("delete-exited-processes", &delete_exited_processes,
5341 "*Non-nil means delete processes immediately when they exit.\n\
5342 nil means don't delete them until `list-processes' is run.");
5343 delete_exited_processes = 0;
5344 #endif
5345
5346 defsubr (&Srecent_doskeys);
5347 defsubr (&Smsdos_long_file_names);
5348 defsubr (&Smsdos_downcase_filename);
5349 defsubr (&Smsdos_remember_default_colors);
5350 defsubr (&Smsdos_set_mouse_buttons);
5351 }
5352
5353 #endif /* MSDOS */
5354