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