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