(internal_flush): Removed
[bpt/emacs.git] / src / msdos.c
1 /* MS-DOS specific C utilities.
2 Copyright (C) 1993, 1994 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20 /* Contributed by Morten Welinder */
21
22 /* Note: some of the stuff here was taken from end of sysdep.c in demacs. */
23
24 #include <config.h>
25
26 #ifdef MSDOS
27 #include "lisp.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <sys/param.h>
31 #include <sys/time.h>
32 #include <dos.h>
33 #include "dosfns.h"
34 #include "msdos.h"
35 #include "systime.h"
36 #include "termhooks.h"
37 #include "dispextern.h"
38 #include "termopts.h"
39 #include "frame.h"
40 #include "window.h"
41 #include <go32.h>
42 #include <pc.h>
43 #include <ctype.h>
44 /* #include <process.h> */
45 /* Damn that local process.h! Instead we can define P_WAIT ourselves. */
46 #define P_WAIT 1
47
48 static int break_stat; /* BREAK check mode status. */
49 static int stdin_stat; /* stdin IOCTL status. */
50 static int extended_kbd; /* 101 (102) keyboard present. */
51
52 int have_mouse; /* Mouse present? */
53 static int mouse_last_x;
54 static int mouse_last_y;
55
56 #define DO_TERMSCRIPT /* define if you want open-termscript to work on msdos */
57
58 /* Standard putchar may call _flsbuf which doesn't go through
59 fflush's overlayed internal_flush routine. */
60 #undef putchar
61 #define putchar(x) \
62 (--(stdout)->_cnt>=0? \
63 ((int)((unsigned char)((*(stdout)->_ptr++=(unsigned)(x))))): \
64 (internal_flush (stdout), --(stdout)->_cnt, *(stdout)->_ptr++=(unsigned)(x)))
65
66 static void
67 mouse_get_xy (int *x, int *y);
68
69 /* Turn off Dos' Ctrl-C checking and inhibit interpretation of control chars
70 by Dos. Determine the keyboard type. */
71 int
72 dos_ttraw ()
73 {
74 union REGS inregs, outregs;
75 static int only_once = 1;
76
77 if (only_once) {
78 inregs.h.ah = 0xc0;
79 int86 (0x15, &inregs, &outregs);
80 extended_kbd = (!outregs.x.cflag) && (outregs.h.ah == 0);
81 }
82
83 break_stat = getcbrk ();
84 setcbrk (0);
85 install_ctrl_break_check ();
86
87 if (only_once)
88 have_mouse = mouse_init1 ();
89
90 inregs.x.ax = 0x4400; /* Get IOCTL status. */
91 inregs.x.bx = 0x00; /* 0 = stdin. */
92 intdos (&inregs, &outregs);
93 stdin_stat = outregs.h.dl;
94
95 only_once = 0;
96
97 inregs.x.dx = stdin_stat | 0x0020; /* raw mode */
98 inregs.x.ax = 0x4401; /* Set IOCTL status */
99 intdos (&inregs, &outregs);
100 return !outregs.x.cflag;
101 }
102
103 /* Restore status of standard input and Ctrl-C checking. */
104 int
105 dos_ttcooked ()
106 {
107 union REGS inregs, outregs;
108
109 setcbrk (break_stat);
110 mouse_off ();
111
112 inregs.x.ax = 0x4401; /* Set IOCTL status. */
113 inregs.x.bx = 0x00; /* 0 = stdin. */
114 inregs.x.dx = stdin_stat;
115 intdos (&inregs, &outregs);
116 return !outregs.x.cflag;
117 }
118
119 /* generate a reliable event timestamp, KFS 1995-07-06 */
120
121 static unsigned long
122 event_timestamp ()
123 {
124 struct time t;
125 unsigned long s;
126
127 gettime (&t);
128 s = t.ti_min;
129 s *= 60;
130 s += t.ti_sec;
131 s *= 1000;
132 s += t.ti_hund * 10;
133
134 return s;
135 }
136
137 static unsigned short
138 ibmpc_translate_map[] =
139 {
140 /* --------------- 00 to 0f --------------- */
141 0, /* Ctrl Break */
142 0xff1b, /* Escape */
143 0xffb1, /* Keypad 1 */
144 0xffb2, /* Keypad 2 */
145 0xffb3, /* Keypad 3 */
146 0xffb4, /* Keypad 4 */
147 0xffb5, /* Keypad 5 */
148 0xffb6, /* Keypad 6 */
149 0xffb7, /* Keypad 7 */
150 0xffb8, /* Keypad 8 */
151 0xffb9, /* Keypad 9 */
152 0xffb0, /* Keypad 0 */
153 '-', '=',
154 0xff08, /* Backspace */
155 0xff74, /* (Shift) Tab [Tab doesn't use this table] */
156
157 /* --------------- 10 to 1f --------------- */
158 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']',
159 0xff8d, /* Keypad Enter */
160 0, /* Ctrl */
161 'a', 's',
162
163 /* --------------- 20 to 2f --------------- */
164 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
165 0, /* Left shift */
166 '\\', 'z', 'x', 'c', 'v',
167
168 /* --------------- 30 to 3f --------------- */
169 'b', 'n', 'm', ',', '.',
170 0xffaf, /* Grey / */
171 0, /* Right shift */
172 0xffaa, /* Grey * */
173 0, /* Alt */
174 ' ',
175 0, /* Caps Lock */
176 0xffbe, /* F1 */
177 0xffbf, /* F2 */
178 0xffc0, /* F3 */
179 0xffc1, /* F4 */
180 0xffc2, /* F5 */
181
182 /* --------------- 40 to 4f --------------- */
183 0xffc3, /* F6 */
184 0xffc4, /* F7 */
185 0xffc5, /* F8 */
186 0xffc6, /* F9 */
187 0xffc7, /* F10 */
188 0, /* Num Lock */
189 0, /* Scroll Lock */
190 0xff50, /* Home */
191 0xff52, /* Up */
192 0xff55, /* Page Up */
193 0xffad, /* Grey - */
194 0xff51, /* Left */
195 0xffb5, /* Keypad 5 */
196 0xff53, /* Right */
197 0xffab, /* Grey + */
198 0xff57, /* End */
199
200 /* --------------- 50 to 5f --------------- */
201 0xff54, /* Down */
202 0xff56, /* Page Down */
203 0xff63, /* Insert */
204 0xffff, /* Delete */
205 0xffbe, /* (Shift) F1 */
206 0xffbf, /* (Shift) F2 */
207 0xffc0, /* (Shift) F3 */
208 0xffc1, /* (Shift) F4 */
209 0xffc2, /* (Shift) F5 */
210 0xffc3, /* (Shift) F6 */
211 0xffc4, /* (Shift) F7 */
212 0xffc5, /* (Shift) F8 */
213 0xffc6, /* (Shift) F9 */
214 0xffc7, /* (Shift) F10 */
215 0xffbe, /* (Ctrl) F1 */
216 0xffbf, /* (Ctrl) F2 */
217
218 /* --------------- 60 to 6f --------------- */
219 0xffc0, /* (Ctrl) F3 */
220 0xffc1, /* (Ctrl) F4 */
221 0xffc2, /* (Ctrl) F5 */
222 0xffc3, /* (Ctrl) F6 */
223 0xffc4, /* (Ctrl) F7 */
224 0xffc5, /* (Ctrl) F8 */
225 0xffc6, /* (Ctrl) F9 */
226 0xffc7, /* (Ctrl) F10 */
227 0xffbe, /* (Alt) F1 */
228 0xffbf, /* (Alt) F2 */
229 0xffc0, /* (Alt) F3 */
230 0xffc1, /* (Alt) F4 */
231 0xffc2, /* (Alt) F5 */
232 0xffc3, /* (Alt) F6 */
233 0xffc4, /* (Alt) F7 */
234 0xffc5, /* (Alt) F8 */
235
236 /* --------------- 70 to 7f --------------- */
237 0xffc6, /* (Alt) F9 */
238 0xffc7, /* (Alt) F10 */
239 0xff6d, /* (Ctrl) Sys Rq */
240 0xff51, /* (Ctrl) Left */
241 0xff53, /* (Ctrl) Right */
242 0xff57, /* (Ctrl) End */
243 0xff56, /* (Ctrl) Page Down */
244 0xff50, /* (Ctrl) Home */
245 '1', '2', '3', '4', '5', '6', '7', '8', /* (Alt) */
246
247 /* --------------- 80 to 8f --------------- */
248 '9', '0', '-', '=', /* (Alt) */
249 0xff55, /* (Ctrl) Page Up */
250 0xffc8, /* F11 */
251 0xffc9, /* F12 */
252 0xffc8, /* (Shift) F11 */
253 0xffc9, /* (Shift) F12 */
254 0xffc8, /* (Ctrl) F11 */
255 0xffc9, /* (Ctrl) F12 */
256 0xffc8, /* (Alt) F11 */
257 0xffc9, /* (Alt) F12 */
258 0xff52, /* (Ctrl) Up */
259 0xffae, /* (Ctrl) Grey - */
260 0xffb5, /* (Ctrl) Keypad 5 */
261
262 /* --------------- 90 to 9f --------------- */
263 0xffab, /* (Ctrl) Grey + */
264 0xff54, /* (Ctrl) Down */
265 0xff63, /* (Ctrl) Insert */
266 0xffff, /* (Ctrl) Delete */
267 0xff09, /* (Ctrl) Tab */
268 0xffaf, /* (Ctrl) Grey / */
269 0xffaa, /* (Ctrl) Grey * */
270 0xff50, /* (Alt) Home */
271 0xff52, /* (Alt) Up */
272 0xff55, /* (Alt) Page Up */
273 0, /* NO KEY */
274 0xff51, /* (Alt) Left */
275 0, /* NO KEY */
276 0xff53, /* (Alt) Right */
277 0, /* NO KEY */
278 0xff57, /* (Alt) End */
279
280 /* --------------- a0 to af --------------- */
281 0xff54, /* (Alt) Down */
282 0xff56, /* (Alt) Page Down */
283 0xff63, /* (Alt) Insert */
284 0xffff, /* (Alt) Delete */
285 0xffaf, /* (Alt) Grey / */
286 0xff09, /* (Alt) Tab */
287 0xff0d /* (Alt) Enter */
288 };
289
290 /* Get a char from keyboard. Function keys are put into the event queue. */
291 static int
292 dos_rawgetc ()
293 {
294 struct input_event event;
295 union REGS regs;
296 int ctrl_p, alt_p, shift_p;
297
298 /* Calculate modifier bits */
299 regs.h.ah = extended_kbd ? 0x12 : 0x02;
300 int86 (0x16, &regs, &regs);
301 ctrl_p = ((regs.h.al & 4) != 0);
302 shift_p = ((regs.h.al & 3) != 0);
303 /* Please be very careful here not to break international keyboard support.
304 When Keyb.Com is loaded, the key marked `Alt Gr' is used for accessing
305 characters like { and } if their positions are overlaid. */
306 alt_p = ((extended_kbd ? (regs.h.ah & 2) : (regs.h.al & 8)) != 0);
307
308 /* The following condition is equivalent to `kbhit ()', except that
309 it uses the bios to do its job. This pleases DESQview/X. */
310 while ((regs.h.ah = extended_kbd ? 0x11 : 0x01),
311 int86 (0x16, &regs, &regs),
312 (regs.x.flags & 0x40) == 0)
313 {
314 union REGS regs;
315 register unsigned char c;
316 int sc, code;
317
318 regs.h.ah = extended_kbd ? 0x10 : 0x00;
319 int86 (0x16, &regs, &regs);
320 c = regs.h.al;
321 sc = regs.h.ah;
322
323 /* Determine from the scan code if a keypad key was pressed. */
324 if (c >= '0' && c <= '9' && sc > 0xb)
325 sc = (c == '0') ? 0xb : (c - '0' + 1), c = 0;
326 else if (sc == 0x53 && c != 0xe0)
327 {
328 code = 0xffae; /* Keypad decimal point/comma. */
329 goto nonascii;
330 }
331 else if (sc == 0xe0)
332 {
333 switch (c)
334 {
335 case 10: /* Ctrl Enter */
336 case 13:
337 sc = 0x1c;
338 break;
339 case '/':
340 sc = 0x35;
341 break;
342 default:
343 sc = 0;
344 };
345 c = 0;
346 }
347
348 if (c == 0
349 || c == ' '
350 || alt_p
351 || (ctrl_p && shift_p)
352 || (c == 0xe0 && sc != 0) /* Pseudo-key */
353 || sc == 0x37 /* Grey * */
354 || sc == 0x4a /* Grey - */
355 || sc == 0x4e /* Grey + */
356 || sc == 0x0e) /* Back space *key*, not Ctrl-h */
357 {
358 if (sc >= (sizeof (ibmpc_translate_map) / sizeof (short)))
359 code = 0;
360 else
361 code = ibmpc_translate_map[sc];
362 if (code != 0)
363 {
364 if (code >= 0x100)
365 {
366 nonascii:
367 event.kind = non_ascii_keystroke;
368 event.code = (code & 0xff) + 0xff00;
369 }
370 else
371 {
372 /* Don't return S- if we don't have to. `shifted' is
373 supposed to be the shifted versions of the characters
374 in `unshifted'. Unfortunately, this is only true for
375 US keyboard layout. If anyone knows how to do this
376 right, please tell us. */
377 static char *unshifted
378 = "abcdefghijklmnopqrstuvwxyz,./=;[\\]'-`0123456789";
379 static char *shifted
380 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ<>?+:{|}\"_~)!@#$%^&*(";
381 char *pos;
382
383 if (shift_p && (pos = strchr (unshifted, code)))
384 {
385 c = shifted[pos - unshifted];
386 shift_p = 0;
387 }
388 else
389 if (c == 0) c = code;
390 event.kind = ascii_keystroke;
391 event.code = c;
392 }
393 event.modifiers
394 = (shift_p ? shift_modifier : 0)
395 + (ctrl_p ? ctrl_modifier : 0)
396 + (alt_p ? meta_modifier : 0);
397 /* EMACS == Enter Meta Alt Control Shift */
398 XSETFRAME (event.frame_or_window, selected_frame);
399 event.timestamp = event_timestamp ();
400 kbd_buffer_store_event (&event);
401 }
402 } else
403 return c;
404 }
405
406 if (have_mouse > 0)
407 {
408 int but, press, x, y, ok;
409
410 /* Check for mouse movement *before* buttons. */
411 mouse_check_moved ();
412
413 for (but = 0; but < NUM_MOUSE_BUTTONS; but++)
414 for (press = 0; press < 2; press++)
415 {
416 if (press)
417 ok = mouse_pressed (but, &x, &y);
418 else
419 ok = mouse_released (but, &x, &y);
420 if (ok)
421 {
422 event.kind = mouse_click;
423 event.code = but;
424 event.modifiers
425 = (shift_p ? shift_modifier : 0)
426 + (ctrl_p ? ctrl_modifier : 0)
427 + (alt_p ? meta_modifier : 0)
428 + (press ? down_modifier : up_modifier);
429 event.x = x;
430 event.y = y;
431 XSETFRAME (event.frame_or_window, selected_frame);
432 event.timestamp = event_timestamp ();
433 kbd_buffer_store_event (&event);
434 }
435 }
436 }
437
438 return -1;
439 }
440
441 static int prev_get_char = -1;
442
443 /* Return 1 if a key is ready to be read without suspending execution. */
444 dos_keysns ()
445 {
446 if (prev_get_char != -1)
447 return 1;
448 else
449 return ((prev_get_char = dos_rawgetc ()) != -1);
450 }
451
452 /* Read a key. Return -1 if no key is ready. */
453 dos_keyread ()
454 {
455 if (prev_get_char != -1)
456 {
457 int c = prev_get_char;
458 prev_get_char = -1;
459 return c;
460 }
461 else
462 return dos_rawgetc ();
463 }
464
465 /* Hostnames for a pc are not really funny, but they are used in change log
466 so we emulate the best we can. */
467 gethostname (p, size)
468 char *p;
469 int size;
470 {
471 char *q = egetenv ("HOSTNAME");
472
473 if (!q) q = "pc";
474 strcpy (p, q);
475 return 0;
476 }
477
478 /* Destructively turn backslashes into slashes. */
479 void
480 dostounix_filename (p)
481 register char *p;
482 {
483 while (*p)
484 {
485 if (*p == '\\')
486 *p = '/';
487 p++;
488 }
489 }
490
491 /* Destructively turn slashes into backslashes. */
492 void
493 unixtodos_filename (p)
494 register char *p;
495 {
496 while (*p)
497 {
498 if (*p == '/')
499 *p = '\\';
500 p++;
501 }
502 }
503
504 /* Get the default directory for a given drive. 0=def, 1=A, 2=B, ... */
505 int
506 getdefdir (drive, dst)
507 int drive;
508 char *dst;
509 {
510 union REGS regs;
511
512 *dst++ = '/';
513 regs.h.dl = drive;
514 regs.x.si = (int) dst;
515 regs.h.ah = 0x47;
516 intdos (&regs, &regs);
517 return !regs.x.cflag;
518 }
519
520 /* Remove all CR's that are followed by a LF. */
521 int
522 crlf_to_lf (n, buf)
523 register int n;
524 register unsigned char *buf;
525 {
526 unsigned char *np = buf;
527 unsigned char *startp = buf;
528 unsigned char *endp = buf + n;
529 unsigned char c;
530
531 if (n == 0)
532 return n;
533 while (buf < endp - 1)
534 {
535 if (*buf == 0x0d)
536 {
537 if (*(++buf) != 0x0a)
538 *np++ = 0x0d;
539 }
540 else
541 *np++ = *buf++;
542 }
543 if (buf < endp)
544 *np++ = *buf++;
545 return np - startp;
546 }
547
548
549 /* Run command as specified by ARGV in directory DIR.
550 The command is run with input from TEMPIN and output to file TEMPOUT. */
551 int
552 run_msdos_command (argv, dir, tempin, tempout)
553 unsigned char **argv;
554 Lisp_Object dir;
555 int tempin, tempout;
556 {
557 char *saveargv1, *saveargv2, **envv;
558 char oldwd[MAXPATHLEN + 1]; /* Fixed size is safe on MSDOS. */
559 int msshell, result = -1;
560 int in, out, inbak, outbak, errbak;
561 int x, y;
562 Lisp_Object cmd;
563
564 /* Get current directory as MSDOS cwd is not per-process. */
565 getwd (oldwd);
566
567 cmd = Ffile_name_nondirectory (build_string (argv[0]));
568 msshell = !NILP (Fmember (cmd, Fsymbol_value (intern ("msdos-shells"))))
569 && !strcmp ("-c", argv[1]);
570 if (msshell)
571 {
572 saveargv1 = argv[1];
573 saveargv2 = argv[2];
574 argv[1] = "/c";
575 if (argv[2])
576 {
577 char *p = alloca (strlen (argv[2]) + 1);
578
579 strcpy (argv[2] = p, saveargv2);
580 while (*p && isspace (*p))
581 p++;
582 while (*p && !isspace (*p))
583 if (*p == '/')
584 *p++ = '\\';
585 else
586 p++;
587 }
588 }
589
590 /* Build the environment array. */
591 {
592 extern Lisp_Object Vprocess_environment;
593 Lisp_Object tmp, lst;
594 int i, len;
595
596 lst = Vprocess_environment;
597 len = XFASTINT (Flength (lst));
598
599 envv = alloca ((len + 1) * sizeof (char *));
600 for (i = 0; i < len; i++)
601 {
602 tmp = Fcar (lst);
603 lst = Fcdr (lst);
604 CHECK_STRING (tmp, 0);
605 envv[i] = alloca (XSTRING (tmp)->size + 1);
606 strcpy (envv[i], XSTRING (tmp)->data);
607 }
608 envv[len] = (char *) 0;
609 }
610
611 if (STRINGP (dir))
612 chdir (XSTRING (dir)->data);
613 inbak = dup (0);
614 outbak = dup (1);
615 errbak = dup (2);
616 if (inbak < 0 || outbak < 0 || errbak < 0)
617 goto done; /* Allocation might fail due to lack of descriptors. */
618
619 if (have_mouse > 0)
620 {
621 mouse_get_xy (&x, &y);
622 mouse_off ();
623 }
624 dos_ttcooked(); /* do it here while 0 = stdin */
625
626 dup2 (tempin, 0);
627 dup2 (tempout, 1);
628 dup2 (tempout, 2);
629
630 result = spawnve (P_WAIT, argv[0], argv, envv);
631
632 dup2 (inbak, 0);
633 dup2 (outbak, 1);
634 dup2 (errbak, 2);
635 close (inbak);
636 close (outbak);
637 close (errbak);
638
639 dos_ttraw();
640 if (have_mouse > 0) {
641 mouse_init ();
642 mouse_moveto (x, y);
643 }
644
645 done:
646 chdir (oldwd);
647 if (msshell)
648 {
649 argv[1] = saveargv1;
650 argv[2] = saveargv2;
651 }
652 return result;
653 }
654
655
656 croak (badfunc)
657 char *badfunc;
658 {
659 fprintf (stderr, "%s not yet implemented\r\n", badfunc);
660 reset_sys_modes ();
661 exit (1);
662 }
663
664 /* A list of unimplemented functions that we silently ignore. */
665 unsigned alarm (s) unsigned s; {}
666 fork () { return 0; }
667 int kill (x, y) int x, y; { return -1; }
668 nice (p) int p; {}
669 void volatile pause () {}
670 request_sigio () {}
671 setpgrp () {return 0; }
672 setpriority (x,y,z) int x,y,z; { return 0; }
673 sigsetmask (x) int x; { return 0; }
674 unrequest_sigio () {}
675
676 #ifdef chdir
677 #undef chdir
678 #endif
679
680 int
681 sys_chdir (path)
682 const char* path;
683 {
684 int len = strlen (path);
685 char *tmp = (char *)path;
686 /* Gotta do this extern here due to the corresponding #define: */
687 extern int chdir ();
688
689 if (*tmp && tmp[1] == ':')
690 {
691 if (getdisk () != tolower (tmp[0]) - 'a')
692 setdisk (tolower (tmp[0]) - 'a');
693 tmp += 2; /* strip drive: KFS 1995-07-06 */
694 len -= 2;
695 }
696
697 if (len > 1 && (tmp[len - 1] == '/'))
698 {
699 char *tmp1 = (char *) alloca (len + 1);
700 strcpy (tmp1, tmp);
701 tmp1[len - 1] = 0;
702 tmp = tmp1;
703 }
704 return chdir (tmp);
705 }
706
707 #ifndef HAVE_SELECT
708 #include "sysselect.h"
709
710 /* Only event queue is checked. */
711 int
712 sys_select (nfds, rfds, wfds, efds, timeout)
713 int nfds;
714 SELECT_TYPE *rfds, *wfds, *efds;
715 EMACS_TIME *timeout;
716 {
717 SELECT_TYPE orfds;
718 long timeoutval, clnow, cllast;
719 struct time t;
720
721 FD_ZERO (&orfds);
722 if (rfds)
723 {
724 orfds = *rfds;
725 FD_ZERO (rfds);
726 }
727 if (wfds)
728 FD_ZERO (wfds);
729 if (efds)
730 FD_ZERO (efds);
731
732 if (nfds != 1 || !FD_ISSET (0, &orfds))
733 abort ();
734
735 /* If we are looking only for the terminal, with no timeout,
736 just read it and wait -- that's more efficient. */
737 if (!timeout)
738 {
739 while (! detect_input_pending ());
740 }
741 else
742 {
743 timeoutval = EMACS_SECS (*timeout) * 100 + EMACS_USECS (*timeout) / 10000;
744 gettime (&t);
745 cllast = t.ti_sec * 100 + t.ti_hund;
746
747 while (!detect_input_pending ())
748 {
749 gettime (&t);
750 clnow = t.ti_sec * 100 + t.ti_hund;
751 if (clnow < cllast) /* time wrap */
752 timeoutval -= clnow + 6000 - cllast;
753 else
754 timeoutval -= clnow - cllast;
755 if (timeoutval <= 0) /* Stop on timer being cleared */
756 return 0;
757 cllast = clnow;
758 }
759 }
760
761 FD_SET (0, rfds);
762 return 1;
763 }
764 #endif
765
766 /* The Emacs root directory as determined by init_environment. */
767 static char emacsroot[MAXPATHLEN];
768
769 char *
770 rootrelativepath (rel)
771 char *rel;
772 {
773 static char result[MAXPATHLEN + 10];
774
775 strcpy (result, emacsroot);
776 strcat (result, "/");
777 strcat (result, rel);
778 return result;
779 }
780
781 /* Define a lot of environment variables if not already defined. Don't
782 remove anything unless you know what you're doing -- lots of code will
783 break if one or more of these are missing. */
784 void
785 init_environment (argc, argv, skip_args)
786 int argc;
787 char **argv;
788 int skip_args;
789 {
790 char *s, *t, *root;
791 int len;
792
793 /* Find our root from argv[0]. Assuming argv[0] is, say,
794 "c:/emacs/bin/emacs.exe" our root will be "c:/emacs". */
795 root = alloca (MAXPATHLEN + 20);
796 _fixpath (argv[0], root);
797 strlwr (root);
798 len = strlen (root);
799 while (len > 0 && root[len] != '/' && root[len] != ':')
800 len--;
801 root[len] = '\0';
802 if (len > 4 && strcmp (root + len - 4, "/bin") == 0)
803 root[len - 4] = '\0';
804 else
805 strcpy (root, "c:/emacs"); /* Only under debuggers, I think. */
806 len = strlen (root);
807 strcpy (emacsroot, root);
808
809 /* We default HOME to our root. */
810 setenv ("HOME", root, 0);
811
812 /* We default EMACSPATH to root + "/bin". */
813 strcpy (root + len, "/bin");
814 setenv ("EMACSPATH", root, 0);
815
816 /* I don't expect anybody to ever use other terminals so the internal
817 terminal is the default. */
818 setenv ("TERM", "internal", 0);
819
820 #ifdef HAVE_X_WINDOWS
821 /* Emacs expects DISPLAY to be set. */
822 setenv ("DISPLAY", "unix:0.0", 0);
823 #endif
824
825 /* SHELL is a bit tricky -- COMSPEC is the closest we come, but we must
826 downcase it and mirror the backslashes. */
827 s = getenv ("COMSPEC");
828 if (!s) s = "c:/command.com";
829 t = alloca (strlen (s) + 1);
830 strcpy (t, s);
831 strlwr (t);
832 dostounix_filename (t);
833 setenv ("SHELL", t, 0);
834
835 /* PATH is also downcased and backslashes mirrored. */
836 s = getenv ("PATH");
837 if (!s) s = "";
838 t = alloca (strlen (s) + 3);
839 /* Current directory is always considered part of MsDos's path but it is
840 not normally mentioned. Now it is. */
841 strcat (strcpy (t, ".;"), s);
842 strlwr (t);
843 dostounix_filename (t); /* Not a single file name, but this should work. */
844 setenv ("PATH", t, 1);
845
846 /* In some sense all dos users have root privileges, so... */
847 setenv ("USER", "root", 0);
848 setenv ("NAME", getenv ("USER"), 0);
849
850 /* Time zone determined from country code. To make this possible, the
851 country code may not span more than one time zone. In other words,
852 in the USA, you lose. */
853 switch (dos_country_code)
854 {
855 case 31: /* Belgium */
856 case 32: /* The Netherlands */
857 case 33: /* France */
858 case 34: /* Spain */
859 case 36: /* Hungary */
860 case 38: /* Yugoslavia (or what's left of it?) */
861 case 39: /* Italy */
862 case 41: /* Switzerland */
863 case 42: /* Tjekia */
864 case 45: /* Denmark */
865 case 46: /* Sweden */
866 case 47: /* Norway */
867 case 48: /* Poland */
868 case 49: /* Germany */
869 /* Daylight saving from last Sunday in March to last Sunday in
870 September, both at 2AM. */
871 setenv ("TZ", "MET-01METDST-02,M3.5.0/02:00,M9.5.0/02:00", 0);
872 break;
873 case 44: /* United Kingdom */
874 case 351: /* Portugal */
875 case 354: /* Iceland */
876 setenv ("TZ", "GMT+00", 0);
877 break;
878 case 81: /* Japan */
879 case 82: /* Korea */
880 setenv ("TZ", "???-09", 0);
881 break;
882 case 90: /* Turkey */
883 case 358: /* Finland */
884 case 972: /* Israel */
885 setenv ("TZ", "EET-02", 0);
886 break;
887 }
888 }
889
890 /* Flash the screen as a substitute for BEEPs. */
891
892 static void
893 do_visible_bell (xorattr)
894 unsigned char xorattr;
895 {
896 asm volatile
897 (" movb $1,%%dl
898 visible_bell_0:
899 movl _ScreenPrimary,%%eax
900 call dosmemsetup
901 movl %%eax,%%ebx
902 movl %1,%%ecx
903 movb %0,%%al
904 incl %%ebx
905 visible_bell_1:
906 xorb %%al,%%gs:(%%ebx)
907 addl $2,%%ebx
908 decl %%ecx
909 jne visible_bell_1
910 decb %%dl
911 jne visible_bell_3
912 visible_bell_2:
913 movzwl %%ax,%%eax
914 movzwl %%ax,%%eax
915 movzwl %%ax,%%eax
916 movzwl %%ax,%%eax
917 decw %%cx
918 jne visible_bell_2
919 jmp visible_bell_0
920 visible_bell_3:"
921 : /* no output */
922 : "m" (xorattr), "g" (ScreenCols () * ScreenRows ())
923 : "%eax", "%ebx", /* "%gs",*/ "%ecx", "%edx");
924 }
925
926 /* At screen position (X,Y), output C characters from string S with
927 attribute A. Do it fast! */
928
929 static void
930 output_string (x, y, s, c, a)
931 int x, y, c;
932 unsigned char *s;
933 unsigned char a;
934 {
935 char *t = (char *)ScreenPrimary + 2 * (x + ScreenCols () * y);
936 #ifdef DO_TERMSCRIPT
937 if (termscript)
938 {
939 fprintf (termscript, "<%d@%dx%d>", c, x, y);
940 fwrite (s, sizeof (unsigned char), c, termscript);
941 }
942 #endif
943 asm volatile
944 (" movl %1,%%eax
945 call dosmemsetup
946 movl %%eax,%%edi
947 movb %0,%%ah
948 movl %2,%%ecx
949 movl %3,%%esi
950 output_string1:
951 movb (%%esi),%%al
952 movw %%ax,%%gs:(%%edi)
953 addl $2,%%edi
954 incl %%esi
955 decl %%ecx
956 jne output_string1"
957 : /* no output */
958 : "m" (a), "g" (t), "g" (c), "g" (s)
959 : "%eax", "%ecx", /* "%gs",*/ "%esi", "%edi");
960 }
961
962 static int internal_terminal = 0;
963 static int highlight;
964
965 #undef fflush
966
967 static int /* number of characters used by escape; -1 if incomplete */
968 flush_escape (resume, cp, count, xp, yp)
969 int resume;
970 unsigned char *cp;
971 int count;
972 int *xp;
973 int *yp;
974 {
975 static char spaces[] = " ";
976 static unsigned char esc_cmd[8];
977 static int esc_count = 0;
978 int esc_needed;
979 int i, j, used = 0;
980
981 if (!resume)
982 {
983 esc_cmd[0] = '\e';
984 esc_count = 1;
985 used++;
986 }
987
988 while (esc_count < 2)
989 {
990 if (used == count)
991 return -1;
992 esc_cmd[esc_count++] = *cp++;
993 used++;
994 }
995
996 switch (esc_cmd[1])
997 {
998 case '@':
999 esc_needed = 4;
1000 break;
1001 case 'A':
1002 case 'B':
1003 case 'X':
1004 esc_needed = 3;
1005 break;
1006 default:
1007 esc_needed = 2;
1008 break;
1009 }
1010
1011 while (esc_count < esc_needed)
1012 {
1013 if (used == count)
1014 return -1;
1015 esc_cmd[esc_count++] = *cp++;
1016 used++;
1017 }
1018
1019 switch (esc_cmd[1])
1020 {
1021 case '@':
1022 *yp = esc_cmd[2];
1023 *xp = esc_cmd[3];
1024 break;
1025 case 'A':
1026 ScreenAttrib = esc_cmd[2];
1027 break;
1028 case 'B':
1029 do_visible_bell (esc_cmd[2]);
1030 break;
1031 case 'C':
1032 ScreenClear ();
1033 *xp = *yp = 0;
1034 break;
1035 case 'E':
1036 i = ScreenCols () - *xp;
1037 j = *xp;
1038 while (i >= sizeof spaces)
1039 {
1040 output_string (j, *yp, spaces, sizeof spaces, ScreenAttrib);
1041 j += sizeof spaces;
1042 i -= sizeof spaces;
1043 }
1044 if (i > 0)
1045 output_string (j, *yp, spaces, i, ScreenAttrib);
1046 break;
1047 case 'R':
1048 ++*xp;
1049 break;
1050 case 'U':
1051 --*yp;
1052 break;
1053 case 'X':
1054 ScreenAttrib ^= esc_cmd[2];
1055 break;
1056 case '\e':
1057 output_string (*xp, *yp, &esc_cmd[1], 1, ScreenAttrib);
1058 ++*xp;
1059 break;
1060 }
1061
1062 esc_count = 0;
1063 return used;
1064 }
1065
1066 int
1067 internal_flush (f)
1068 FILE *f;
1069 {
1070 static int x;
1071 static int y;
1072 static int resume_esc = 0;
1073 unsigned char *cp, *cp0;
1074 int count, i;
1075
1076 if (!internal_terminal || f != stdout)
1077 {
1078 /* This is a call to the original fflush. */
1079 fflush (f);
1080 return;
1081 }
1082
1083 mouse_off ();
1084 cp = stdout->_base;
1085 count = stdout->_ptr - stdout->_base;
1086
1087 #ifdef DO_TERMSCRIPT
1088 if (termscript)
1089 fprintf (termscript, "\n<FLUSH%s %d>\n", resume_esc ? " RESUME" : "", count);
1090 #endif
1091
1092 if (resume_esc)
1093 {
1094 i = flush_escape (1, cp, count, &x, &y);
1095 if (i < 0)
1096 count = 0;
1097 else
1098 {
1099 resume_esc = 0;
1100 count -= i;
1101 cp += i;
1102 }
1103 }
1104
1105 while (count > 0)
1106 {
1107 switch (*cp++)
1108 {
1109 case 27:
1110 i = flush_escape (0, cp, count, &x, &y);
1111 if (i < 0)
1112 {
1113 resume_esc = 1;
1114 count = 0;
1115 }
1116 else
1117 {
1118 count -= i;
1119 cp += i - 1;
1120 }
1121 break;
1122 case 7:
1123 write (1, "\007", 1);
1124 count--;
1125 break;
1126 case 8:
1127 x--;
1128 count--;
1129 break;
1130 case 13:
1131 x = 0;
1132 count--;
1133 break;
1134 case 10:
1135 y++;
1136 count--;
1137 break;
1138 default:
1139 cp0 = cp - 1;
1140 count--;
1141 while (count > 0 && *cp >= ' ')
1142 cp++, count--;
1143 output_string (x, y, cp0, cp - cp0, ScreenAttrib);
1144 x += (cp - cp0);
1145 }
1146 }
1147 fpurge (stdout);
1148 ScreenSetCursor (y, x);
1149 mouse_on ();
1150 }
1151
1152 #ifndef HAVE_X_WINDOWS
1153 static void
1154 rien_du_tout ()
1155 {
1156 /* Rien du tout, cela va sans dire! */
1157 }
1158
1159 static
1160 IT_ring_bell ()
1161 {
1162 if (visible_bell)
1163 {
1164 /* This creates an xor-mask that will swap the default fore- and
1165 background colors. */
1166 mouse_off ();
1167 do_visible_bell (((the_only_x_display.foreground_pixel
1168 ^ the_only_x_display.background_pixel)
1169 * 0x11) & 0x7f);
1170 mouse_on ();
1171 }
1172 else
1173 /* Write it directly to ms-dos -- don't let it go through our terminal
1174 emulator. This way the mouse cursor won't blink. */
1175 write (1, "\007", 1);
1176 }
1177
1178 static void
1179 IT_set_face (int face)
1180 {
1181 struct face *fp;
1182 extern struct face *intern_face (/* FRAME_PTR, struct face * */);
1183
1184 if (face == 1 || (face == 0 && highlight))
1185 fp = FRAME_MODE_LINE_FACE (foo);
1186 else if (face <= 0 || face >= FRAME_N_COMPUTED_FACES (foo))
1187 fp = FRAME_DEFAULT_FACE (foo);
1188 else
1189 fp = intern_face (selected_frame, FRAME_COMPUTED_FACES (foo)[face]);
1190 #ifdef DO_TERMSCRIPT
1191 if (termscript)
1192 fprintf (termscript, "<FACE:%d:%d>", FACE_FOREGROUND (fp), FACE_BACKGROUND (fp));
1193 #endif
1194 putchar ('\e');
1195 putchar ('A');
1196 putchar ((FACE_BACKGROUND (fp) << 4) | FACE_FOREGROUND (fp));
1197 }
1198
1199 static
1200 IT_write_glyphs (GLYPH *str, int len)
1201 {
1202 int face = -1;
1203 int newface;
1204 int ch;
1205
1206 while (len > 0)
1207 {
1208 newface = FAST_GLYPH_FACE (*str);
1209 if (newface != face)
1210 IT_set_face ((face = newface));
1211 ch = FAST_GLYPH_CHAR (*str);
1212 #ifdef DO_TERMSCRIPT
1213 if (termscript)
1214 fputc (ch, termscript);
1215 #endif
1216 if (ch == '\e') putchar (ch); /* allow esc to be printed */
1217 putchar (ch);
1218 str++, len--;
1219 }
1220 }
1221
1222 static
1223 IT_clear_end_of_line (first_unused)
1224 {
1225 IT_set_face (0);
1226 #ifdef DO_TERMSCRIPT
1227 if (termscript)
1228 fprintf (termscript, "<CLR:EOL>");
1229 #endif
1230 putchar ('\e');
1231 putchar ('E');
1232 }
1233
1234 static
1235 IT_cursor_to (int y, int x)
1236 {
1237 #ifdef DO_TERMSCRIPT
1238 if (termscript)
1239 fprintf (termscript, "\n<XY=%dx%d>", x, y);
1240 #endif
1241 putchar ('\e');
1242 putchar ('@');
1243 putchar (y);
1244 putchar (x);
1245 }
1246
1247 IT_reassert_line_highlight (new, vpos)
1248 int new, vpos;
1249 {
1250 highlight = new;
1251 IT_set_face (0); /* To possibly clear the highlighting. */
1252 }
1253
1254 static
1255 IT_change_line_highlight (new_highlight, vpos, first_unused_hpos)
1256 {
1257 highlight = new_highlight;
1258 IT_set_face (0); /* To possibly clear the highlighting. */
1259 IT_cursor_to (vpos, 0);
1260 IT_clear_end_of_line (first_unused_hpos);
1261 }
1262
1263 static
1264 IT_update_begin ()
1265 {
1266 highlight = 0;
1267 IT_set_face (0); /* To possibly clear the highlighting. */
1268 }
1269
1270 /* This was more or less copied from xterm.c */
1271 static void
1272 IT_set_menu_bar_lines (window, n)
1273 Lisp_Object window;
1274 int n;
1275 {
1276 struct window *w = XWINDOW (window);
1277
1278 XSETFASTINT (w->top, XFASTINT (w->top) + n);
1279 XSETFASTINT (w->height, XFASTINT (w->height) - n);
1280
1281 /* Handle just the top child in a vertical split. */
1282 if (!NILP (w->vchild))
1283 IT_set_menu_bar_lines (w->vchild, n);
1284
1285 /* Adjust all children in a horizontal split. */
1286 for (window = w->hchild; !NILP (window); window = w->next)
1287 {
1288 w = XWINDOW (window);
1289 IT_set_menu_bar_lines (window, n);
1290 }
1291 }
1292
1293 void
1294 IT_set_frame_parameters (frame, alist)
1295 FRAME_PTR frame;
1296 Lisp_Object alist;
1297 {
1298 Lisp_Object tail;
1299 int redraw;
1300 extern unsigned long load_color ();
1301 FRAME_PTR f = (FRAME_PTR) &the_only_frame;
1302
1303 redraw = 0;
1304 for (tail = alist; CONSP (tail); tail = Fcdr (tail))
1305 {
1306 Lisp_Object elt, prop, val;
1307
1308 elt = Fcar (tail);
1309 prop = Fcar (elt);
1310 val = Fcdr (elt);
1311 CHECK_SYMBOL (prop, 1);
1312
1313 if (EQ (prop, intern ("foreground-color")))
1314 {
1315 unsigned long new_color = load_color (f, val);
1316 if (new_color != ~0)
1317 {
1318 FRAME_FOREGROUND_PIXEL (f) = new_color;
1319 redraw = 1;
1320 }
1321 }
1322 else if (EQ (prop, intern ("background-color")))
1323 {
1324 unsigned long new_color = load_color (f, val);
1325 if (new_color != ~0)
1326 {
1327 FRAME_BACKGROUND_PIXEL (f) = new_color & ~8;
1328 redraw = 1;
1329 }
1330 }
1331 else if (EQ (prop, intern ("menu-bar-lines")))
1332 {
1333 int new;
1334 int old = FRAME_MENU_BAR_LINES (the_only_frame);
1335
1336 if (INTEGERP (val))
1337 new = XINT (val);
1338 else
1339 new = 0;
1340 FRAME_MENU_BAR_LINES (f) = new;
1341 IT_set_menu_bar_lines (the_only_frame.root_window, new - old);
1342 }
1343 }
1344
1345 if (redraw)
1346 {
1347 recompute_basic_faces (f);
1348 Fredraw_frame (Fselected_frame ());
1349 }
1350 }
1351
1352 /* Similar to the_only_frame. */
1353 struct x_display the_only_x_display;
1354
1355 /* This is never dereferenced. */
1356 Display *x_current_display;
1357
1358 #endif /* !HAVE_X_WINDOWS */
1359
1360 /* Do we need the internal terminal? */
1361 void
1362 internal_terminal_init ()
1363 {
1364 char *term = getenv ("TERM");
1365 char *colors;
1366
1367 #ifdef HAVE_X_WINDOWS
1368 if (!inhibit_window_system)
1369 return;
1370 #endif
1371
1372 internal_terminal
1373 = (!noninteractive) && term && !strcmp (term, "internal");
1374
1375 #ifndef HAVE_X_WINDOWS
1376 if (internal_terminal && !inhibit_window_system)
1377 {
1378 Vwindow_system = intern ("pc");
1379 Vwindow_system_version = make_number (1);
1380
1381 bzero (&the_only_x_display, sizeof the_only_x_display);
1382 the_only_x_display.background_pixel = 7; /* White */
1383 the_only_x_display.foreground_pixel = 0; /* Black */
1384 colors = getenv ("EMACSCOLORS");
1385 if (colors && strlen (colors) >=2)
1386 {
1387 the_only_x_display.foreground_pixel = colors[0] & 0x07;
1388 the_only_x_display.background_pixel = colors[1] & 0x07;
1389 }
1390 the_only_x_display.line_height = 1;
1391 the_only_frame.display.x = &the_only_x_display;
1392 the_only_frame.output_method = output_msdos_raw;
1393
1394 init_frame_faces ((FRAME_PTR) &the_only_frame);
1395
1396 ring_bell_hook = IT_ring_bell;
1397 write_glyphs_hook = IT_write_glyphs;
1398 cursor_to_hook = raw_cursor_to_hook = IT_cursor_to;
1399 clear_end_of_line_hook = IT_clear_end_of_line;
1400 change_line_highlight_hook = IT_change_line_highlight;
1401 update_begin_hook = IT_update_begin;
1402 reassert_line_highlight_hook = IT_reassert_line_highlight;
1403
1404 /* These hooks are called by term.c without being checked. */
1405 set_terminal_modes_hook
1406 = reset_terminal_modes_hook
1407 = update_end_hook
1408 = set_terminal_window_hook
1409 = (void *)rien_du_tout;
1410 }
1411 else
1412 the_only_frame.output_method = output_termcap;
1413 #endif
1414 }
1415 \f
1416 /* When time zones are set from Ms-Dos too may C-libraries are playing
1417 tricks with time values. We solve this by defining our own version
1418 of `gettimeofday' bypassing GO32. Our version needs to be initialized
1419 once and after each call to `tzset' with TZ changed. */
1420
1421 static int daylight, gmtoffset;
1422
1423 int
1424 gettimeofday (struct timeval *tp, struct timezone *tzp)
1425 {
1426 if (tp)
1427 {
1428 struct time t;
1429 struct date d;
1430 struct tm tmrec;
1431
1432 gettime (&t);
1433 /* If midnight occurs here, the results can be incorrect. */
1434 getdate (&d);
1435 tmrec.tm_year = d.da_year - 1900;
1436 tmrec.tm_mon = d.da_mon - 1;
1437 tmrec.tm_mday = d.da_day;
1438 tmrec.tm_hour = t.ti_hour;
1439 tmrec.tm_min = t.ti_min;
1440 tmrec.tm_sec = t.ti_sec;
1441 tmrec.tm_gmtoff = gmtoffset;
1442 tmrec.tm_isdst = daylight;
1443 tp->tv_sec = mktime (&tmrec);
1444 tp->tv_usec = t.ti_hund * (1000000 / 100);
1445 }
1446 /* Ignore tzp; it's obsolescent. */
1447 return 0;
1448 }
1449
1450 void
1451 init_gettimeofday ()
1452 {
1453 time_t ltm, gtm;
1454 struct tm *lstm;
1455 #undef tzset
1456 extern void tzset (void);
1457
1458 tzset ();
1459 daylight = 0;
1460 gmtoffset = 0;
1461 ltm = gtm = time (NULL);
1462 ltm = mktime (lstm = localtime (&ltm));
1463 gtm = mktime (gmtime (&gtm));
1464 daylight = lstm->tm_isdst;
1465 gmtoffset = (int)(gtm - ltm) / 60;
1466 }
1467 \f
1468 /* These must be global. */
1469 static _go32_dpmi_seginfo ctrl_break_vector;
1470 static _go32_dpmi_registers ctrl_break_regs;
1471 static int ctrlbreakinstalled = 0;
1472
1473 /* Interrupt level detection of Ctrl-Break. Don't do anything fancy here! */
1474 void
1475 ctrl_break_func (regs)
1476 _go32_dpmi_registers *regs;
1477 {
1478 Vquit_flag = Qt;
1479 }
1480
1481 void
1482 install_ctrl_break_check ()
1483 {
1484 if (!ctrlbreakinstalled)
1485 {
1486 /* Don't press Ctrl-Break if you don't have either DPMI or Emacs
1487 was compiler with Djgpp 1.11 maintenance level 5 or later! */
1488 ctrlbreakinstalled = 1;
1489 ctrl_break_vector.pm_offset = (int) ctrl_break_func;
1490 _go32_dpmi_allocate_real_mode_callback_iret (&ctrl_break_vector,
1491 &ctrl_break_regs);
1492 _go32_dpmi_set_real_mode_interrupt_vector (0x1b, &ctrl_break_vector);
1493 }
1494 }
1495 \f
1496 /* Mouse routines follow. Coordinates are in screen positions and zero
1497 based. Mouse buttons are numbered from left to right and also zero
1498 based. */
1499
1500 static int mouse_button_translate[NUM_MOUSE_BUTTONS];
1501 static int mouse_button_count;
1502
1503 void
1504 mouse_init ()
1505 {
1506 union REGS regs;
1507
1508 regs.x.ax = 0x0007;
1509 regs.x.cx = 0;
1510 regs.x.dx = 8 * (ScreenCols () - 1);
1511 int86 (0x33, &regs, &regs);
1512
1513 regs.x.ax = 0x0008;
1514 regs.x.cx = 0;
1515 regs.x.dx = 8 * (ScreenRows () - 1);
1516 int86 (0x33, &regs, &regs);
1517
1518 mouse_moveto (ScreenCols () - 1, ScreenRows () - 1);
1519 mouse_on ();
1520 }
1521
1522 void
1523 mouse_on ()
1524 {
1525 union REGS regs;
1526
1527 if (have_mouse > 0)
1528 {
1529 regs.x.ax = 0x0001;
1530 int86 (0x33, &regs, &regs);
1531 }
1532 }
1533
1534 void
1535 mouse_off ()
1536 {
1537 union REGS regs;
1538
1539 if (have_mouse > 0)
1540 {
1541 regs.x.ax = 0x0002;
1542 int86 (0x33, &regs, &regs);
1543 }
1544 }
1545
1546 void
1547 mouse_moveto (x, y)
1548 int x, y;
1549 {
1550 union REGS regs;
1551
1552 regs.x.ax = 0x0004;
1553 mouse_last_x = regs.x.cx = x * 8;
1554 mouse_last_y = regs.x.dx = y * 8;
1555 int86 (0x33, &regs, &regs);
1556 }
1557
1558 int
1559 mouse_pressed (b, xp, yp)
1560 int b, *xp, *yp;
1561 {
1562 union REGS regs;
1563
1564 if (b >= mouse_button_count)
1565 return 0;
1566 regs.x.ax = 0x0005;
1567 regs.x.bx = mouse_button_translate[b];
1568 int86 (0x33, &regs, &regs);
1569 if (regs.x.bx)
1570 *xp = regs.x.cx / 8, *yp = regs.x.dx / 8;
1571 return (regs.x.bx != 0);
1572 }
1573
1574 int
1575 mouse_released (b, xp, yp)
1576 int b, *xp, *yp;
1577 {
1578 union REGS regs;
1579
1580 if (b >= mouse_button_count)
1581 return 0;
1582 regs.x.ax = 0x0006;
1583 regs.x.bx = mouse_button_translate[b];
1584 int86 (0x33, &regs, &regs);
1585 #if 0
1586 if (regs.x.ax & (1 << mouse_button_translate[b]))
1587 regs.x.bx = 0; /* if mouse is still pressed, ignore release */
1588 #endif
1589 if (regs.x.bx)
1590 *xp = regs.x.cx / 8, *yp = regs.x.dx / 8;
1591 return (regs.x.bx != 0);
1592 }
1593
1594 static void
1595 mouse_get_xy (int *x, int *y)
1596 {
1597 union REGS regs;
1598
1599 regs.x.ax = 0x0003;
1600 int86 (0x33, &regs, &regs);
1601 *x = regs.x.cx / 8;
1602 *y = regs.x.dx / 8;
1603 }
1604
1605 void
1606 mouse_get_pos (f, insist, bar_window, part, x, y, time)
1607 FRAME_PTR *f;
1608 int insist;
1609 Lisp_Object *bar_window, *x, *y;
1610 enum scroll_bar_part *part;
1611 unsigned long *time;
1612 {
1613 int ix, iy;
1614 union REGS regs;
1615
1616 regs.x.ax = 0x0003;
1617 int86 (0x33, &regs, &regs);
1618 *f = selected_frame;
1619 *bar_window = Qnil;
1620 mouse_get_xy (&ix, &iy);
1621 selected_frame->mouse_moved = 0;
1622 *x = make_number (ix);
1623 *y = make_number (iy);
1624 *time = event_timestamp ();
1625 }
1626
1627 void
1628 mouse_check_moved ()
1629 {
1630 int x, y;
1631
1632 mouse_get_xy (&x, &y);
1633 selected_frame->mouse_moved |= (x != mouse_last_x || y != mouse_last_y);
1634 mouse_last_x = x;
1635 mouse_last_y = y;
1636 }
1637
1638 int
1639 mouse_init1 ()
1640 {
1641 union REGS regs;
1642 int present;
1643
1644 #ifdef HAVE_X_WINDOWS
1645 if (!inhibit_window_system)
1646 return 0;
1647 #endif
1648 if (!internal_terminal)
1649 return 0;
1650
1651 regs.x.ax = 0x0021;
1652 int86 (0x33, &regs, &regs);
1653 present = (regs.x.ax & 0xffff) == 0xffff;
1654 if (!present)
1655 {
1656 /* Reportedly, the above doesn't work for some mouse drivers. There
1657 is an additional detection method that should work, but might be
1658 a little slower. Use that as an alternative. */
1659 regs.x.ax = 0x0000;
1660 int86 (0x33, &regs, &regs);
1661 present = (regs.x.ax & 0xffff) == 0xffff;
1662 }
1663
1664 if (present)
1665 {
1666 if (regs.x.bx == 3)
1667 {
1668 mouse_button_count = 3;
1669 mouse_button_translate[0] = 0; /* Left */
1670 mouse_button_translate[1] = 2; /* Middle */
1671 mouse_button_translate[2] = 1; /* Right */
1672 }
1673 else
1674 {
1675 mouse_button_count = 2;
1676 mouse_button_translate[0] = 0;
1677 mouse_button_translate[1] = 1;
1678 }
1679 mouse_position_hook = &mouse_get_pos;
1680 mouse_init ();
1681 }
1682 return present;
1683 }
1684
1685 #ifndef HAVE_X_WINDOWS
1686 /* See xterm.c for more info. */
1687 void
1688 pixel_to_glyph_coords (f, pix_x, pix_y, x, y, bounds, noclip)
1689 FRAME_PTR f;
1690 register int pix_x, pix_y;
1691 register int *x, *y;
1692 void /* XRectangle */ *bounds;
1693 int noclip;
1694 {
1695 if (bounds) abort ();
1696
1697 /* Ignore clipping. */
1698
1699 *x = pix_x;
1700 *y = pix_y;
1701 }
1702
1703 void
1704 glyph_to_pixel_coords (f, x, y, pix_x, pix_y)
1705 FRAME_PTR f;
1706 register int x, y;
1707 register int *pix_x, *pix_y;
1708 {
1709 *pix_x = x;
1710 *pix_y = y;
1711 }
1712 \f
1713 /* Simulation of X's menus. Nothing too fancy here -- just make it work
1714 for now.
1715
1716 Actually, I don't know the meaning of all the parameters of the functions
1717 here -- I only know how they are called by xmenu.c. I could of course
1718 grab the nearest Xlib manual (down the hall, second-to-last door on the
1719 left), but I don't think it's worth the effort. */
1720
1721 static XMenu *
1722 IT_menu_create ()
1723 {
1724 XMenu *menu;
1725
1726 menu = (XMenu *) xmalloc (sizeof (XMenu));
1727 menu->allocated = menu->count = menu->panecount = menu->width = 0;
1728 return menu;
1729 }
1730
1731 /* Allocate some (more) memory for MENU ensuring that there is room for one
1732 for item. */
1733
1734 static void
1735 IT_menu_make_room (XMenu *menu)
1736 {
1737 if (menu->allocated == 0)
1738 {
1739 int count = menu->allocated = 10;
1740 menu->text = (char **) xmalloc (count * sizeof (char *));
1741 menu->submenu = (XMenu **) xmalloc (count * sizeof (XMenu *));
1742 menu->panenumber = (int *) xmalloc (count * sizeof (int));
1743 }
1744 else if (menu->allocated == menu->count)
1745 {
1746 int count = menu->allocated = menu->allocated + 10;
1747 menu->text
1748 = (char **) xrealloc (menu->text, count * sizeof (char *));
1749 menu->submenu
1750 = (XMenu **) xrealloc (menu->submenu, count * sizeof (XMenu *));
1751 menu->panenumber
1752 = (int *) xrealloc (menu->panenumber, count * sizeof (int));
1753 }
1754 }
1755
1756 /* Search the given menu structure for a given pane number. */
1757
1758 static XMenu *
1759 IT_menu_search_pane (XMenu *menu, int pane)
1760 {
1761 int i;
1762 XMenu *try;
1763
1764 for (i = 0; i < menu->count; i++)
1765 if (menu->submenu[i])
1766 {
1767 if (pane == menu->panenumber[i])
1768 return menu->submenu[i];
1769 else if ((try = IT_menu_search_pane (menu->submenu[i], pane)))
1770 return try;
1771 }
1772 return (XMenu *) 0;
1773 }
1774
1775 /* Determine how much screen space a given menu needs. */
1776
1777 static void
1778 IT_menu_calc_size (XMenu *menu, int *width, int *height)
1779 {
1780 int i, h2, w2, maxsubwidth, maxheight;
1781
1782 maxsubwidth = 0;
1783 maxheight = menu->count;
1784 for (i = 0; i < menu->count; i++)
1785 {
1786 if (menu->submenu[i])
1787 {
1788 IT_menu_calc_size (menu->submenu[i], &w2, &h2);
1789 if (w2 > maxsubwidth) maxsubwidth = w2;
1790 if (i + h2 > maxheight) maxheight = i + h2;
1791 }
1792 }
1793 *width = menu->width + maxsubwidth;
1794 *height = maxheight;
1795 }
1796
1797 /* Display MENU at (X,Y) using FACES. */
1798
1799 static void
1800 IT_menu_display (XMenu *menu, int y, int x, int *faces)
1801 {
1802 int i, j, face, width;
1803 GLYPH *text, *p;
1804 char *q;
1805 int mx, my;
1806 int enabled, mousehere;
1807 int row, col;
1808
1809 width = menu->width;
1810 text = (GLYPH *) xmalloc ((width + 2) * sizeof (GLYPH));
1811 ScreenGetCursor (&row, &col);
1812 mouse_get_xy (&mx, &my);
1813 mouse_off ();
1814 (*update_begin_hook) ();
1815 for (i = 0; i < menu->count; i++)
1816 {
1817 (*cursor_to_hook) (y + i, x);
1818 enabled
1819 = (!menu->submenu[i] && menu->panenumber[i]) || (menu->submenu[i]);
1820 mousehere = (y + i == my && x <= mx && mx < x + width + 2);
1821 face = faces[enabled + mousehere * 2];
1822 p = text;
1823 *p++ = FAST_MAKE_GLYPH (' ', face);
1824 for (j = 0, q = menu->text[i]; *q; j++)
1825 *p++ = FAST_MAKE_GLYPH (*q++, face);
1826 for (; j < width; j++)
1827 *p++ = FAST_MAKE_GLYPH (' ', face);
1828 *p++ = FAST_MAKE_GLYPH (menu->submenu[i] ? 16 : ' ', face);
1829 (*write_glyphs_hook) (text, width + 2);
1830 }
1831 internal_flush (stdout);
1832 (*update_end_hook) ();
1833 mouse_on ();
1834 ScreenSetCursor (row, col);
1835 xfree (text);
1836 }
1837
1838 /* Create a brand new menu structure. */
1839
1840 XMenu *
1841 XMenuCreate (Display *foo1, Window foo2, char *foo3)
1842 {
1843 return IT_menu_create ();
1844 }
1845
1846 /* Create a new pane and place it on the outer-most level. It is not
1847 clear that it should be placed out there, but I don't know what else
1848 to do. */
1849
1850 int
1851 XMenuAddPane (Display *foo, XMenu *menu, char *txt, int enable)
1852 {
1853 int len;
1854
1855 if (!enable)
1856 abort ();
1857
1858 IT_menu_make_room (menu);
1859 menu->submenu[menu->count] = IT_menu_create ();
1860 menu->text[menu->count] = txt;
1861 menu->panenumber[menu->count] = ++menu->panecount;
1862 menu->count++;
1863 if ((len = strlen (txt)) > menu->width)
1864 menu->width = len;
1865 return menu->panecount;
1866 }
1867
1868 /* Create a new item in a menu pane. */
1869
1870 int
1871 XMenuAddSelection (Display *bar, XMenu *menu, int pane,
1872 int foo, char *txt, int enable)
1873 {
1874 int len;
1875
1876 if (pane)
1877 if (!(menu = IT_menu_search_pane (menu, pane)))
1878 return XM_FAILURE;
1879 IT_menu_make_room (menu);
1880 menu->submenu[menu->count] = (XMenu *) 0;
1881 menu->text[menu->count] = txt;
1882 menu->panenumber[menu->count] = enable;
1883 menu->count++;
1884 if ((len = strlen (txt)) > menu->width)
1885 menu->width = len;
1886 return XM_SUCCESS;
1887 }
1888
1889 /* Decide where the menu would be placed if requested at (X,Y). */
1890
1891 void
1892 XMenuLocate (Display *foo0, XMenu *menu, int foo1, int foo2, int x, int y,
1893 int *ulx, int *uly, int *width, int *height)
1894 {
1895 if (menu->count == 1 && menu->submenu[0])
1896 /* Special case: the menu consists of only one pane. */
1897 IT_menu_calc_size (menu->submenu[0], width, height);
1898 else
1899 IT_menu_calc_size (menu, width, height);
1900 *ulx = x + 1;
1901 *uly = y;
1902 *width += 2;
1903 }
1904
1905 struct IT_menu_state
1906 {
1907 void *screen_behind;
1908 XMenu *menu;
1909 int pane;
1910 int x, y;
1911 };
1912
1913
1914 /* Display menu, wait for user's response, and return that response. */
1915
1916 int
1917 XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx,
1918 int x0, int y0, unsigned ButtonMask, char **txt)
1919 {
1920 struct IT_menu_state *state;
1921 int statecount;
1922 int x, y, i, b;
1923 int screensize;
1924 int faces[4], selectface;
1925 int leave, result, onepane;
1926
1927 /* Just in case we got here without a mouse present... */
1928 if (have_mouse <= 0)
1929 return XM_IA_SELECT;
1930
1931 state = alloca (menu->panecount * sizeof (struct IT_menu_state));
1932 screensize = ScreenRows () * ScreenCols () * 2;
1933 faces[0]
1934 = compute_glyph_face (&the_only_frame,
1935 face_name_id_number
1936 (&the_only_frame,
1937 intern ("msdos-menu-passive-face")),
1938 0);
1939 faces[1]
1940 = compute_glyph_face (&the_only_frame,
1941 face_name_id_number
1942 (&the_only_frame,
1943 intern ("msdos-menu-active-face")),
1944 0);
1945 selectface
1946 = face_name_id_number (&the_only_frame, intern ("msdos-menu-select-face"));
1947 faces[2] = compute_glyph_face (&the_only_frame, selectface, faces[0]);
1948 faces[3] = compute_glyph_face (&the_only_frame, selectface, faces[1]);
1949
1950 statecount = 1;
1951 state[0].menu = menu;
1952 mouse_off ();
1953 ScreenRetrieve (state[0].screen_behind = xmalloc (screensize));
1954 mouse_on ();
1955 if ((onepane = menu->count == 1 && menu->submenu[0]))
1956 {
1957 menu->width = menu->submenu[0]->width;
1958 state[0].menu = menu->submenu[0];
1959 }
1960 else
1961 {
1962 state[0].menu = menu;
1963 }
1964 state[0].x = x0 - 1;
1965 state[0].y = y0;
1966 state[0].pane = onepane;
1967
1968 mouse_last_x = -1; /* A hack that forces display. */
1969 leave = 0;
1970 while (!leave)
1971 {
1972 mouse_check_moved ();
1973 if (selected_frame->mouse_moved)
1974 {
1975 selected_frame->mouse_moved = 0;
1976 result = XM_IA_SELECT;
1977 mouse_get_xy (&x, &y);
1978 for (i = 0; i < statecount; i++)
1979 if (state[i].x <= x && x < state[i].x + state[i].menu->width + 2)
1980 {
1981 int dy = y - state[i].y;
1982 if (0 <= dy && dy < state[i].menu->count)
1983 {
1984 if (!state[i].menu->submenu[dy])
1985 if (state[i].menu->panenumber[dy])
1986 result = XM_SUCCESS;
1987 else
1988 result = XM_IA_SELECT;
1989 *pane = state[i].pane - 1;
1990 *selidx = dy;
1991 /* We hit some part of a menu, so drop extra menues that
1992 have been opened. That does not include an open and
1993 active submenu. */
1994 if (i != statecount - 2
1995 || state[i].menu->submenu[dy] != state[i+1].menu)
1996 while (i != statecount - 1)
1997 {
1998 statecount--;
1999 mouse_off ();
2000 ScreenUpdate (state[statecount].screen_behind);
2001 mouse_on ();
2002 xfree (state[statecount].screen_behind);
2003 }
2004 if (i == statecount - 1 && state[i].menu->submenu[dy])
2005 {
2006 IT_menu_display (state[i].menu,
2007 state[i].y,
2008 state[i].x,
2009 faces);
2010 state[statecount].menu = state[i].menu->submenu[dy];
2011 state[statecount].pane = state[i].menu->panenumber[dy];
2012 mouse_off ();
2013 ScreenRetrieve (state[statecount].screen_behind
2014 = xmalloc (screensize));
2015 mouse_on ();
2016 state[statecount].x
2017 = state[i].x + state[i].menu->width + 2;
2018 state[statecount].y = y;
2019 statecount++;
2020 }
2021 }
2022 }
2023 IT_menu_display (state[statecount - 1].menu,
2024 state[statecount - 1].y,
2025 state[statecount - 1].x,
2026 faces);
2027 }
2028 for (b = 0; b < mouse_button_count; b++)
2029 {
2030 (void) mouse_pressed (b, &x, &y);
2031 if (mouse_released (b, &x, &y))
2032 leave = 1;
2033 }
2034 }
2035
2036 mouse_off ();
2037 ScreenUpdate (state[0].screen_behind);
2038 mouse_on ();
2039 while (statecount--)
2040 xfree (state[statecount].screen_behind);
2041 return result;
2042 }
2043
2044 /* Dispose of a menu. */
2045
2046 void
2047 XMenuDestroy (Display *foo, XMenu *menu)
2048 {
2049 int i;
2050 if (menu->allocated)
2051 {
2052 for (i = 0; i < menu->count; i++)
2053 if (menu->submenu[i])
2054 XMenuDestroy (foo, menu->submenu[i]);
2055 xfree (menu->text);
2056 xfree (menu->submenu);
2057 xfree (menu->panenumber);
2058 }
2059 xfree (menu);
2060 }
2061
2062 int
2063 x_pixel_width (struct frame *f)
2064 {
2065 return FRAME_WIDTH (f);
2066 }
2067
2068 int
2069 x_pixel_height (struct frame *f)
2070 {
2071 return FRAME_HEIGHT (f);
2072 }
2073 #endif /* !HAVE_X_WINDOWS */
2074
2075 #endif /* MSDOS */