(Fset_time_zone_rule): New function.
[bpt/emacs.git] / src / msdos.c
CommitLineData
9da6e765
RS
1/* MS-DOS specific C utilities.
2 Copyright (C) 1993, 1994 Free Software Foundation, Inc.
1b94449f
RS
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
edfc0d45 8the Free Software Foundation; either version 2, or (at your option)
1b94449f
RS
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
9da6e765
RS
20/* Contributed by Morten Welinder */
21
1b94449f
RS
22/* Note: some of the stuff here was taken from end of sysdep.c in demacs. */
23
48984716 24#include <config.h>
1b94449f
RS
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"
87485d6f
MW
37#include "dispextern.h"
38#include "termopts.h"
1b94449f 39#include "frame.h"
87485d6f 40#include "window.h"
1b94449f
RS
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
48static int break_stat; /* BREAK check mode status. */
49static int stdin_stat; /* stdin IOCTL status. */
50static int extended_kbd; /* 101 (102) keyboard present. */
51
52int have_mouse; /* Mouse present? */
53static int mouse_last_x;
54static int mouse_last_y;
55
aee81730
RS
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
66static void
67mouse_get_xy (int *x, int *y);
68
1b94449f
RS
69/* Turn off Dos' Ctrl-C checking and inhibit interpretation of control chars
70 by Dos. Determine the keyboard type. */
71int
72dos_ttraw ()
73{
74 union REGS inregs, outregs;
aee81730
RS
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
1b94449f
RS
83 break_stat = getcbrk ();
84 setcbrk (0);
85 install_ctrl_break_check ();
1b94449f 86
aee81730
RS
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. */
1b94449f
RS
92 intdos (&inregs, &outregs);
93 stdin_stat = outregs.h.dl;
94
aee81730
RS
95 only_once = 0;
96
97 inregs.x.dx = stdin_stat | 0x0020; /* raw mode */
98 inregs.x.ax = 0x4401; /* Set IOCTL status */
1b94449f
RS
99 intdos (&inregs, &outregs);
100 return !outregs.x.cflag;
101}
102
103/* Restore status of standard input and Ctrl-C checking. */
104int
105dos_ttcooked ()
106{
107 union REGS inregs, outregs;
108
109 setcbrk (break_stat);
aee81730 110 mouse_off ();
1b94449f
RS
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
aee81730
RS
119/* generate a reliable event timestamp, KFS 1995-07-06 */
120
121static unsigned long
122event_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
1b94449f
RS
137static unsigned short
138ibmpc_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. */
291static int
292dos_rawgetc ()
293{
294 struct input_event event;
1b94449f
RS
295 union REGS regs;
296 int ctrl_p, alt_p, shift_p;
aee81730 297
1b94449f
RS
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);
9da6e765
RS
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. */
1b94449f
RS
306 alt_p = ((extended_kbd ? (regs.h.ah & 2) : (regs.h.al & 8)) != 0);
307
3ec5651b
RS
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)
1b94449f
RS
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;
9da6e765
RS
326 else if (sc == 0x53 && c != 0xe0)
327 {
aee81730 328 code = 0xffae; /* Keypad decimal point/comma. */
9da6e765
RS
329 goto nonascii;
330 }
1b94449f
RS
331 else if (sc == 0xe0)
332 {
333 switch (c)
334 {
aee81730 335 case 10: /* Ctrl Enter */
1b94449f
RS
336 case 13:
337 sc = 0x1c;
338 break;
1b94449f
RS
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)
aee81730
RS
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;
1b94449f
RS
404 }
405
aee81730 406 if (have_mouse > 0)
1b94449f
RS
407 {
408 int but, press, x, y, ok;
409
410 /* Check for mouse movement *before* buttons. */
49a09c76 411 mouse_check_moved ();
1b94449f
RS
412
413 for (but = 0; but < NUM_MOUSE_BUTTONS; but++)
414 for (press = 0; press < 2; press++)
415 {
416 if (press)
49a09c76 417 ok = mouse_pressed (but, &x, &y);
1b94449f 418 else
49a09c76 419 ok = mouse_released (but, &x, &y);
1b94449f
RS
420 if (ok)
421 {
422 event.kind = mouse_click;
423 event.code = but;
424 event.modifiers
9da6e765
RS
425 = (shift_p ? shift_modifier : 0)
426 + (ctrl_p ? ctrl_modifier : 0)
427 + (alt_p ? meta_modifier : 0)
428 + (press ? down_modifier : up_modifier);
1b94449f
RS
429 event.x = x;
430 event.y = y;
87485d6f 431 XSETFRAME (event.frame_or_window, selected_frame);
aee81730 432 event.timestamp = event_timestamp ();
1b94449f
RS
433 kbd_buffer_store_event (&event);
434 }
435 }
436 }
437
438 return -1;
439}
440
441static int prev_get_char = -1;
442
443/* Return 1 if a key is ready to be read without suspending execution. */
444dos_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. */
453dos_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. */
467gethostname (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. */
479void
480dostounix_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. */
492void
493unixtodos_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, ... */
505int
506getdefdir (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. */
521int
522crlf_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;
3d4ad6e0 533 while (buf < endp - 1)
1b94449f
RS
534 {
535 if (*buf == 0x0d)
536 {
537 if (*(++buf) != 0x0a)
538 *np++ = 0x0d;
539 }
540 else
541 *np++ = *buf++;
542 }
3d4ad6e0
RS
543 if (buf < endp)
544 *np++ = *buf++;
1b94449f
RS
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. */
551int
552run_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;
aee81730 561 int x, y;
1b94449f
RS
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];
647c32eb 573 saveargv2 = argv[2];
1b94449f
RS
574 argv[1] = "/c";
575 if (argv[2])
576 {
647c32eb
KH
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++;
1b94449f
RS
587 }
588 }
589
590 /* Build the environment array. */
591 {
592 extern Lisp_Object Vprocess_environment;
091d0bdf
KH
593 Lisp_Object tmp, lst;
594 int i, len;
595
596 lst = Vprocess_environment;
597 len = XFASTINT (Flength (lst));
1b94449f
RS
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
30d0f552 611 if (STRINGP (dir))
1b94449f
RS
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. */
aee81730
RS
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
1b94449f
RS
626 dup2 (tempin, 0);
627 dup2 (tempout, 1);
628 dup2 (tempout, 2);
aee81730 629
1b94449f 630 result = spawnve (P_WAIT, argv[0], argv, envv);
aee81730 631
1b94449f
RS
632 dup2 (inbak, 0);
633 dup2 (outbak, 1);
634 dup2 (errbak, 2);
aee81730
RS
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
1b94449f
RS
645 done:
646 chdir (oldwd);
647 if (msshell)
648 {
649 argv[1] = saveargv1;
647c32eb 650 argv[2] = saveargv2;
1b94449f
RS
651 }
652 return result;
653}
654
655
656croak (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. */
665unsigned alarm (s) unsigned s; {}
666fork () { return 0; }
667int kill (x, y) int x, y; { return -1; }
668nice (p) int p; {}
669void volatile pause () {}
670request_sigio () {}
671setpgrp () {return 0; }
672setpriority (x,y,z) int x,y,z; { return 0; }
673sigsetmask (x) int x; { return 0; }
674unrequest_sigio () {}
675
676#ifdef chdir
677#undef chdir
678#endif
679
680int
681sys_chdir (path)
682 const char* path;
683{
684 int len = strlen (path);
aee81730 685 char *tmp = (char *)path;
1b94449f
RS
686 /* Gotta do this extern here due to the corresponding #define: */
687 extern int chdir ();
688
aee81730
RS
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 }
1b94449f
RS
704 return chdir (tmp);
705}
706
aee81730
RS
707#ifndef HAVE_SELECT
708#include "sysselect.h"
709
710/* Only event queue is checked. */
711int
712sys_select (nfds, rfds, wfds, efds, timeout)
713 int nfds;
714 SELECT_TYPE *rfds, *wfds, *efds;
715 EMACS_TIME *timeout;
1b94449f 716{
aee81730
RS
717 SELECT_TYPE orfds;
718 long timeoutval, clnow, cllast;
719 struct time t;
1b94449f 720
aee81730
RS
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);
1b94449f 731
aee81730
RS
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)
1b94449f 738 {
aee81730 739 while (! detect_input_pending ());
1b94449f 740 }
aee81730
RS
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;
1b94449f 763}
aee81730 764#endif
1b94449f 765
88b28483
RS
766/* The Emacs root directory as determined by init_environment. */
767static char emacsroot[MAXPATHLEN];
768
769char *
770rootrelativepath (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
1b94449f
RS
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. */
784void
785init_environment (argc, argv, skip_args)
786 int argc;
787 char **argv;
788 int skip_args;
789{
88b28483
RS
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". */
872e2c82
RS
795 root = alloca (MAXPATHLEN + 20);
796 _fixpath (argv[0], root);
87485d6f
MW
797 strlwr (root);
798 len = strlen (root);
88b28483
RS
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';
1b94449f 804 else
88b28483
RS
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);
1b94449f
RS
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
87485d6f
MW
820#ifdef HAVE_X_WINDOWS
821 /* Emacs expects DISPLAY to be set. */
822 setenv ("DISPLAY", "unix:0.0", 0);
823#endif
824
1b94449f
RS
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 tzset ();
b278e52a 889 init_gettimeofday ();
1b94449f
RS
890}
891
892/* Flash the screen as a substitute for BEEPs. */
893
49a09c76 894static void
fcea9cd4 895do_visible_bell (xorattr)
1b94449f
RS
896 unsigned char xorattr;
897{
49a09c76 898 asm volatile
ca986694 899 (" movb $1,%%dl
1b94449f 900visible_bell_0:
ca986694 901 movl _ScreenPrimary,%%eax
49a09c76 902 call dosmemsetup
ca986694
RS
903 movl %%eax,%%ebx
904 movl %1,%%ecx
905 movb %0,%%al
906 incl %%ebx
1b94449f 907visible_bell_1:
ca986694
RS
908 xorb %%al,%%gs:(%%ebx)
909 addl $2,%%ebx
910 decl %%ecx
49a09c76 911 jne visible_bell_1
ca986694 912 decb %%dl
49a09c76 913 jne visible_bell_3
1b94449f 914visible_bell_2:
ca986694
RS
915 movzwl %%ax,%%eax
916 movzwl %%ax,%%eax
917 movzwl %%ax,%%eax
918 movzwl %%ax,%%eax
919 decw %%cx
49a09c76
RS
920 jne visible_bell_2
921 jmp visible_bell_0
ca986694
RS
922visible_bell_3:"
923 : /* no output */
924 : "m" (xorattr), "g" (ScreenCols () * ScreenRows ())
925 : "%eax", "%ebx", /* "%gs",*/ "%ecx", "%edx");
1b94449f
RS
926}
927
09e2ac30
RS
928/* At screen position (X,Y), output C characters from string S with
929 attribute A. Do it fast! */
930
931static void
932output_string (x, y, s, c, a)
933 int x, y, c;
934 unsigned char *s;
935 unsigned char a;
936{
937 char *t = (char *)ScreenPrimary + 2 * (x + ScreenCols () * y);
aee81730
RS
938#ifdef DO_TERMSCRIPT
939 if (termscript)
940 {
941 fprintf (termscript, "<%d@%dx%d>", c, x, y);
942 fwrite (s, sizeof (unsigned char), c, termscript);
943 }
944#endif
09e2ac30
RS
945 asm volatile
946 (" movl %1,%%eax
947 call dosmemsetup
948 movl %%eax,%%edi
949 movb %0,%%ah
950 movl %2,%%ecx
951 movl %3,%%esi
952output_string1:
953 movb (%%esi),%%al
954 movw %%ax,%%gs:(%%edi)
955 addl $2,%%edi
956 incl %%esi
957 decl %%ecx
958 jne output_string1"
aee81730 959 : /* no output */
09e2ac30 960 : "m" (a), "g" (t), "g" (c), "g" (s)
aee81730 961 : "%eax", "%ecx", /* "%gs",*/ "%esi", "%edi");
09e2ac30
RS
962}
963
1b94449f 964static int internal_terminal = 0;
87485d6f
MW
965static int highlight;
966
1b94449f
RS
967#undef fflush
968
aee81730
RS
969static int /* number of characters used by escape; -1 if incomplete */
970flush_escape (resume, cp, count, xp, yp)
971 int resume;
972 unsigned char *cp;
973 int count;
974 int *xp;
975 int *yp;
976{
977 static char spaces[] = " ";
978 static unsigned char esc_cmd[8];
979 static int esc_count = 0;
980 int esc_needed;
981 int i, j, used = 0;
982
983 if (!resume)
984 {
985 esc_cmd[0] = '\e';
986 esc_count = 1;
987 used++;
988 }
989
990 while (esc_count < 2)
991 {
992 if (used == count)
993 return -1;
994 esc_cmd[esc_count++] = *cp++;
995 used++;
996 }
997
998 switch (esc_cmd[1])
999 {
1000 case '@':
1001 esc_needed = 4;
1002 break;
1003 case 'A':
1004 case 'B':
1005 case 'X':
1006 esc_needed = 3;
1007 break;
1008 default:
1009 esc_needed = 2;
1010 break;
1011 }
1012
1013 while (esc_count < esc_needed)
1014 {
1015 if (used == count)
1016 return -1;
1017 esc_cmd[esc_count++] = *cp++;
1018 used++;
1019 }
1020
1021 switch (esc_cmd[1])
1022 {
1023 case '@':
1024 *yp = esc_cmd[2];
1025 *xp = esc_cmd[3];
1026 break;
1027 case 'A':
1028 ScreenAttrib = esc_cmd[2];
1029 break;
1030 case 'B':
1031 do_visible_bell (esc_cmd[2]);
1032 break;
1033 case 'C':
1034 ScreenClear ();
1035 *xp = *yp = 0;
1036 break;
1037 case 'E':
1038 i = ScreenCols () - *xp;
1039 j = *xp;
1040 while (i >= sizeof spaces)
1041 {
1042 output_string (j, *yp, spaces, sizeof spaces, ScreenAttrib);
1043 j += sizeof spaces;
1044 i -= sizeof spaces;
1045 }
1046 if (i > 0)
1047 output_string (j, *yp, spaces, i, ScreenAttrib);
1048 break;
1049 case 'R':
1050 ++*xp;
1051 break;
1052 case 'U':
1053 --*yp;
1054 break;
1055 case 'X':
1056 ScreenAttrib ^= esc_cmd[2];
1057 break;
1058 case '\e':
1059 output_string (*xp, *yp, &esc_cmd[1], 1, ScreenAttrib);
1060 ++*xp;
1061 break;
1062 }
1063
1064 esc_count = 0;
1065 return used;
1066}
1067
1b94449f
RS
1068int
1069internal_flush (f)
1070 FILE *f;
1071{
1072 static int x;
1073 static int y;
aee81730 1074 static int resume_esc = 0;
09e2ac30 1075 unsigned char *cp, *cp0;
aee81730 1076 int count, i;
1b94449f 1077
aee81730 1078 if (!internal_terminal || f != stdout)
1b94449f 1079 {
aee81730
RS
1080 /* This is a call to the original fflush. */
1081 fflush (f);
1082 return;
1083 }
1084
1085 mouse_off ();
1086 cp = stdout->_base;
1087 count = stdout->_ptr - stdout->_base;
1088
1089#ifdef DO_TERMSCRIPT
1090 if (termscript)
1091 fprintf (termscript, "\n<FLUSH%s %d>\n", resume_esc ? " RESUME" : "", count);
1092#endif
1093
1094 if (resume_esc)
1095 {
1096 i = flush_escape (1, cp, count, &x, &y);
1097 if (i < 0)
1098 count = 0;
1099 else
1b94449f 1100 {
aee81730
RS
1101 resume_esc = 0;
1102 count -= i;
1103 cp += i;
1104 }
1105 }
1106
1107 while (count > 0)
1108 {
1109 switch (*cp++)
1110 {
1111 case 27:
1112 i = flush_escape (0, cp, count, &x, &y);
1113 if (i < 0)
1b94449f 1114 {
aee81730
RS
1115 resume_esc = 1;
1116 count = 0;
1b94449f 1117 }
aee81730
RS
1118 else
1119 {
1120 count -= i;
1121 cp += i - 1;
1122 }
1123 break;
1124 case 7:
1125 write (1, "\007", 1);
1126 count--;
1127 break;
1128 case 8:
1129 x--;
1130 count--;
1131 break;
1132 case 13:
1133 x = 0;
1134 count--;
1135 break;
1136 case 10:
1137 y++;
1138 count--;
1139 break;
1140 default:
1141 cp0 = cp - 1;
1142 count--;
1143 while (count > 0 && *cp >= ' ')
1144 cp++, count--;
1145 output_string (x, y, cp0, cp - cp0, ScreenAttrib);
1146 x += (cp - cp0);
1b94449f 1147 }
1b94449f 1148 }
aee81730
RS
1149 fpurge (stdout);
1150 ScreenSetCursor (y, x);
1151 mouse_on ();
1b94449f
RS
1152}
1153
87485d6f
MW
1154#ifndef HAVE_X_WINDOWS
1155static void
1156rien_du_tout ()
1157{
1158 /* Rien du tout, cela va sans dire! */
1159}
1160
1161static
1162IT_ring_bell ()
1163{
1164 if (visible_bell)
1165 {
1166 /* This creates an xor-mask that will swap the default fore- and
1167 background colors. */
aee81730 1168 mouse_off ();
87485d6f
MW
1169 do_visible_bell (((the_only_x_display.foreground_pixel
1170 ^ the_only_x_display.background_pixel)
1171 * 0x11) & 0x7f);
aee81730 1172 mouse_on ();
87485d6f
MW
1173 }
1174 else
1175 /* Write it directly to ms-dos -- don't let it go through our terminal
1176 emulator. This way the mouse cursor won't blink. */
1177 write (1, "\007", 1);
1178}
1179
1180static void
1181IT_set_face (int face)
1182{
1183 struct face *fp;
1184 extern struct face *intern_face (/* FRAME_PTR, struct face * */);
1185
1186 if (face == 1 || (face == 0 && highlight))
1187 fp = FRAME_MODE_LINE_FACE (foo);
1188 else if (face <= 0 || face >= FRAME_N_COMPUTED_FACES (foo))
1189 fp = FRAME_DEFAULT_FACE (foo);
1190 else
1191 fp = intern_face (selected_frame, FRAME_COMPUTED_FACES (foo)[face]);
aee81730
RS
1192#ifdef DO_TERMSCRIPT
1193 if (termscript)
1194 fprintf (termscript, "<FACE:%d:%d>", FACE_FOREGROUND (fp), FACE_BACKGROUND (fp));
1195#endif
87485d6f
MW
1196 putchar ('\e');
1197 putchar ('A');
1198 putchar ((FACE_BACKGROUND (fp) << 4) | FACE_FOREGROUND (fp));
1199}
1200
1201static
1202IT_write_glyphs (GLYPH *str, int len)
1203{
1204 int face = -1;
1205 int newface;
aee81730
RS
1206 int ch;
1207
87485d6f
MW
1208 while (len > 0)
1209 {
1210 newface = FAST_GLYPH_FACE (*str);
1211 if (newface != face)
1212 IT_set_face ((face = newface));
aee81730
RS
1213 ch = FAST_GLYPH_CHAR (*str);
1214#ifdef DO_TERMSCRIPT
1215 if (termscript)
1216 fputc (ch, termscript);
1217#endif
1218 if (ch == '\e') putchar (ch); /* allow esc to be printed */
1219 putchar (ch);
87485d6f
MW
1220 str++, len--;
1221 }
1222}
1223
1224static
1225IT_clear_end_of_line (first_unused)
1226{
aee81730
RS
1227 IT_set_face (0);
1228#ifdef DO_TERMSCRIPT
1229 if (termscript)
1230 fprintf (termscript, "<CLR:EOL>");
1231#endif
87485d6f
MW
1232 putchar ('\e');
1233 putchar ('E');
1234}
1235
1236static
1237IT_cursor_to (int y, int x)
1238{
aee81730
RS
1239#ifdef DO_TERMSCRIPT
1240 if (termscript)
1241 fprintf (termscript, "\n<XY=%dx%d>", x, y);
1242#endif
87485d6f
MW
1243 putchar ('\e');
1244 putchar ('@');
1245 putchar (y);
1246 putchar (x);
1247}
1248
1249IT_reassert_line_highlight (new, vpos)
1250 int new, vpos;
1251{
1252 highlight = new;
1253 IT_set_face (0); /* To possibly clear the highlighting. */
1254}
1255
1256static
1257IT_change_line_highlight (new_highlight, vpos, first_unused_hpos)
1258{
1259 highlight = new_highlight;
1260 IT_set_face (0); /* To possibly clear the highlighting. */
1261 IT_cursor_to (vpos, 0);
1262 IT_clear_end_of_line (first_unused_hpos);
1263}
1264
1265static
1266IT_update_begin ()
1267{
1268 highlight = 0;
1269 IT_set_face (0); /* To possibly clear the highlighting. */
1270}
1271
1272/* This was more or less copied from xterm.c */
1273static void
1274IT_set_menu_bar_lines (window, n)
1275 Lisp_Object window;
1276 int n;
1277{
1278 struct window *w = XWINDOW (window);
1279
1280 XSETFASTINT (w->top, XFASTINT (w->top) + n);
1281 XSETFASTINT (w->height, XFASTINT (w->height) - n);
1282
1283 /* Handle just the top child in a vertical split. */
1284 if (!NILP (w->vchild))
1285 IT_set_menu_bar_lines (w->vchild, n);
1286
1287 /* Adjust all children in a horizontal split. */
1288 for (window = w->hchild; !NILP (window); window = w->next)
1289 {
1290 w = XWINDOW (window);
1291 IT_set_menu_bar_lines (window, n);
1292 }
1293}
1294
1295void
1296IT_set_frame_parameters (frame, alist)
1297 FRAME_PTR frame;
1298 Lisp_Object alist;
1299{
1300 Lisp_Object tail;
1301 int redraw;
1302 extern unsigned long load_color ();
1303 FRAME_PTR f = (FRAME_PTR) &the_only_frame;
1304
1305 redraw = 0;
1306 for (tail = alist; CONSP (tail); tail = Fcdr (tail))
1307 {
1308 Lisp_Object elt, prop, val;
1309
1310 elt = Fcar (tail);
1311 prop = Fcar (elt);
1312 val = Fcdr (elt);
1313 CHECK_SYMBOL (prop, 1);
1314
1315 if (EQ (prop, intern ("foreground-color")))
1316 {
1317 unsigned long new_color = load_color (f, val);
1318 if (new_color != ~0)
1319 {
1320 FRAME_FOREGROUND_PIXEL (f) = new_color;
1321 redraw = 1;
1322 }
1323 }
1324 else if (EQ (prop, intern ("background-color")))
1325 {
1326 unsigned long new_color = load_color (f, val);
1327 if (new_color != ~0)
1328 {
1329 FRAME_BACKGROUND_PIXEL (f) = new_color & ~8;
1330 redraw = 1;
1331 }
1332 }
1333 else if (EQ (prop, intern ("menu-bar-lines")))
1334 {
1335 int new;
1336 int old = FRAME_MENU_BAR_LINES (the_only_frame);
1337
1338 if (INTEGERP (val))
1339 new = XINT (val);
1340 else
1341 new = 0;
1342 FRAME_MENU_BAR_LINES (f) = new;
1343 IT_set_menu_bar_lines (the_only_frame.root_window, new - old);
1344 }
1345 }
1346
1347 if (redraw)
1348 {
1349 recompute_basic_faces (f);
1350 Fredraw_frame (Fselected_frame ());
1351 }
1352}
1353
1354/* Similar to the_only_frame. */
1355struct x_display the_only_x_display;
1356
1357/* This is never dereferenced. */
1358Display *x_current_display;
1359
1360#endif /* !HAVE_X_WINDOWS */
1361
1b94449f
RS
1362/* Do we need the internal terminal? */
1363void
1364internal_terminal_init ()
1365{
1366 char *term = getenv ("TERM");
aee81730
RS
1367 char *colors;
1368
87485d6f
MW
1369#ifdef HAVE_X_WINDOWS
1370 if (!inhibit_window_system)
1371 return;
1372#endif
1373
1b94449f
RS
1374 internal_terminal
1375 = (!noninteractive) && term && !strcmp (term, "internal");
87485d6f
MW
1376
1377#ifndef HAVE_X_WINDOWS
1378 if (internal_terminal && !inhibit_window_system)
1379 {
1380 Vwindow_system = intern ("pc");
1381 Vwindow_system_version = make_number (1);
1382
1383 bzero (&the_only_x_display, sizeof the_only_x_display);
1384 the_only_x_display.background_pixel = 7; /* White */
1385 the_only_x_display.foreground_pixel = 0; /* Black */
aee81730
RS
1386 colors = getenv ("EMACSCOLORS");
1387 if (colors && strlen (colors) >=2)
1388 {
1389 the_only_x_display.foreground_pixel = colors[0] & 0x07;
1390 the_only_x_display.background_pixel = colors[1] & 0x07;
1391 }
87485d6f
MW
1392 the_only_x_display.line_height = 1;
1393 the_only_frame.display.x = &the_only_x_display;
1394 the_only_frame.output_method = output_msdos_raw;
1395
1396 init_frame_faces ((FRAME_PTR) &the_only_frame);
1397
1398 ring_bell_hook = IT_ring_bell;
1399 write_glyphs_hook = IT_write_glyphs;
1400 cursor_to_hook = raw_cursor_to_hook = IT_cursor_to;
1401 clear_end_of_line_hook = IT_clear_end_of_line;
1402 change_line_highlight_hook = IT_change_line_highlight;
1403 update_begin_hook = IT_update_begin;
1404 reassert_line_highlight_hook = IT_reassert_line_highlight;
1405
1406 /* These hooks are called by term.c without being checked. */
1407 set_terminal_modes_hook
1408 = reset_terminal_modes_hook
1409 = update_end_hook
1410 = set_terminal_window_hook
1411 = (void *)rien_du_tout;
1412 }
1413 else
1414 the_only_frame.output_method = output_termcap;
1415#endif
1b94449f 1416}
b278e52a
RS
1417\f
1418/* When time zones are set from Ms-Dos too may C-libraries are playing
1419 tricks with time values. We solve this by defining our own version
1420 of `gettimeofday' bypassing GO32. Our version needs to be initialized
1421 once and after each call to `tzset' with TZ changed. */
1b94449f 1422
b278e52a
RS
1423static int daylight, gmtoffset;
1424
1425int
1426gettimeofday (struct timeval *tp, struct timezone *tzp)
1427{
1428 if (tp)
1429 {
1430 struct time t;
1431 struct date d;
1432 struct tm tmrec;
1433
1434 gettime (&t);
1435 getdate (&d);
1436 tmrec.tm_year = d.da_year - 1900;
1437 tmrec.tm_mon = d.da_mon - 1;
1438 tmrec.tm_mday = d.da_day;
1439 tmrec.tm_hour = t.ti_hour;
1440 tmrec.tm_min = t.ti_min;
1441 tmrec.tm_sec = t.ti_sec;
1442 tmrec.tm_gmtoff = gmtoffset;
1443 tmrec.tm_isdst = daylight;
1444 tp->tv_sec = mktime (&tmrec);
1445 tp->tv_usec = t.ti_hund * (1000000 / 100);
1446 }
1447 if (tzp)
1448 {
1449 tzp->tz_minuteswest = gmtoffset;
1450 tzp->tz_dsttime = daylight;
1451 }
1452 return 0;
1453}
1454
1455void
1456init_gettimeofday ()
1457{
1458 time_t ltm, gtm;
1459 struct tm *lstm;
1460
1461 daylight = 0;
1462 gmtoffset = 0;
1463 ltm = gtm = time (NULL);
1464 ltm = mktime (lstm = localtime (&ltm));
1465 gtm = mktime (gmtime (&gtm));
1466 daylight = lstm->tm_isdst;
1467 gmtoffset = (int)(gtm - ltm) / 60;
1468}
1469\f
1b94449f
RS
1470/* These must be global. */
1471static _go32_dpmi_seginfo ctrl_break_vector;
1472static _go32_dpmi_registers ctrl_break_regs;
1473static int ctrlbreakinstalled = 0;
1474
1475/* Interrupt level detection of Ctrl-Break. Don't do anything fancy here! */
1476void
1477ctrl_break_func (regs)
1478 _go32_dpmi_registers *regs;
1479{
1480 Vquit_flag = Qt;
1481}
1482
1483void
1484install_ctrl_break_check ()
1485{
1486 if (!ctrlbreakinstalled)
1487 {
1488 /* Don't press Ctrl-Break if you don't have either DPMI or Emacs
ca986694 1489 was compiler with Djgpp 1.11 maintenance level 5 or later! */
1b94449f
RS
1490 ctrlbreakinstalled = 1;
1491 ctrl_break_vector.pm_offset = (int) ctrl_break_func;
1492 _go32_dpmi_allocate_real_mode_callback_iret (&ctrl_break_vector,
1493 &ctrl_break_regs);
1494 _go32_dpmi_set_real_mode_interrupt_vector (0x1b, &ctrl_break_vector);
1495 }
1496}
1497\f
87485d6f
MW
1498/* Mouse routines follow. Coordinates are in screen positions and zero
1499 based. Mouse buttons are numbered from left to right and also zero
1500 based. */
1b94449f
RS
1501
1502static int mouse_button_translate[NUM_MOUSE_BUTTONS];
1503static int mouse_button_count;
1504
1505void
1506mouse_init ()
1507{
1508 union REGS regs;
1509
1510 regs.x.ax = 0x0007;
1511 regs.x.cx = 0;
1512 regs.x.dx = 8 * (ScreenCols () - 1);
1513 int86 (0x33, &regs, &regs);
1514
1515 regs.x.ax = 0x0008;
1516 regs.x.cx = 0;
1517 regs.x.dx = 8 * (ScreenRows () - 1);
1518 int86 (0x33, &regs, &regs);
1519
1520 mouse_moveto (ScreenCols () - 1, ScreenRows () - 1);
1521 mouse_on ();
1522}
1523
1524void
1525mouse_on ()
1526{
1527 union REGS regs;
1528
aee81730
RS
1529 if (have_mouse > 0)
1530 {
1531 regs.x.ax = 0x0001;
1532 int86 (0x33, &regs, &regs);
1533 }
1b94449f
RS
1534}
1535
1536void
1537mouse_off ()
1538{
1539 union REGS regs;
1540
aee81730
RS
1541 if (have_mouse > 0)
1542 {
1543 regs.x.ax = 0x0002;
1544 int86 (0x33, &regs, &regs);
1545 }
1b94449f
RS
1546}
1547
1548void
1549mouse_moveto (x, y)
1550 int x, y;
1551{
1552 union REGS regs;
1553
1554 regs.x.ax = 0x0004;
1555 mouse_last_x = regs.x.cx = x * 8;
1556 mouse_last_y = regs.x.dx = y * 8;
1557 int86 (0x33, &regs, &regs);
1558}
1559
1560int
1561mouse_pressed (b, xp, yp)
1562 int b, *xp, *yp;
1563{
1564 union REGS regs;
1565
1566 if (b >= mouse_button_count)
1567 return 0;
1568 regs.x.ax = 0x0005;
1569 regs.x.bx = mouse_button_translate[b];
1570 int86 (0x33, &regs, &regs);
1571 if (regs.x.bx)
ca986694 1572 *xp = regs.x.cx / 8, *yp = regs.x.dx / 8;
1b94449f
RS
1573 return (regs.x.bx != 0);
1574}
1575
1576int
1577mouse_released (b, xp, yp)
1578 int b, *xp, *yp;
1579{
1580 union REGS regs;
1581
1582 if (b >= mouse_button_count)
1583 return 0;
1584 regs.x.ax = 0x0006;
1585 regs.x.bx = mouse_button_translate[b];
1586 int86 (0x33, &regs, &regs);
aee81730
RS
1587#if 0
1588 if (regs.x.ax & (1 << mouse_button_translate[b]))
1589 regs.x.bx = 0; /* if mouse is still pressed, ignore release */
1590#endif
1b94449f
RS
1591 if (regs.x.bx)
1592 *xp = regs.x.cx / 8, *yp = regs.x.dx / 8;
1593 return (regs.x.bx != 0);
1594}
1595
87485d6f
MW
1596static void
1597mouse_get_xy (int *x, int *y)
1598{
1599 union REGS regs;
1600
1601 regs.x.ax = 0x0003;
1602 int86 (0x33, &regs, &regs);
1603 *x = regs.x.cx / 8;
1604 *y = regs.x.dx / 8;
1605}
1606
1b94449f 1607void
fadfaf55 1608mouse_get_pos (f, insist, bar_window, part, x, y, time)
1b94449f 1609 FRAME_PTR *f;
fadfaf55 1610 int insist;
1b94449f
RS
1611 Lisp_Object *bar_window, *x, *y;
1612 enum scroll_bar_part *part;
1613 unsigned long *time;
1614{
87485d6f 1615 int ix, iy;
1b94449f 1616 union REGS regs;
1b94449f
RS
1617
1618 regs.x.ax = 0x0003;
1619 int86 (0x33, &regs, &regs);
1620 *f = selected_frame;
1621 *bar_window = Qnil;
87485d6f 1622 mouse_get_xy (&ix, &iy);
f2f23562 1623 selected_frame->mouse_moved = 0;
87485d6f
MW
1624 *x = make_number (ix);
1625 *y = make_number (iy);
aee81730 1626 *time = event_timestamp ();
1b94449f
RS
1627}
1628
1629void
1630mouse_check_moved ()
1631{
87485d6f 1632 int x, y;
1b94449f 1633
87485d6f 1634 mouse_get_xy (&x, &y);
f2f23562 1635 selected_frame->mouse_moved |= (x != mouse_last_x || y != mouse_last_y);
87485d6f
MW
1636 mouse_last_x = x;
1637 mouse_last_y = y;
1b94449f
RS
1638}
1639
1640int
1641mouse_init1 ()
1642{
1643 union REGS regs;
1644 int present;
1645
87485d6f
MW
1646#ifdef HAVE_X_WINDOWS
1647 if (!inhibit_window_system)
1648 return 0;
1649#endif
e118d5ef
RS
1650 if (!internal_terminal)
1651 return 0;
1652
1b94449f
RS
1653 regs.x.ax = 0x0021;
1654 int86 (0x33, &regs, &regs);
e118d5ef
RS
1655 present = (regs.x.ax & 0xffff) == 0xffff;
1656 if (!present)
1657 {
1658 /* Reportedly, the above doesn't work for some mouse drivers. There
1659 is an additional detection method that should work, but might be
1660 a little slower. Use that as an alternative. */
1661 regs.x.ax = 0x0000;
1662 int86 (0x33, &regs, &regs);
1663 present = (regs.x.ax & 0xffff) == 0xffff;
1664 }
1665
1b94449f
RS
1666 if (present)
1667 {
1668 if (regs.x.bx == 3)
1669 {
1670 mouse_button_count = 3;
1671 mouse_button_translate[0] = 0; /* Left */
1672 mouse_button_translate[1] = 2; /* Middle */
1673 mouse_button_translate[2] = 1; /* Right */
1674 }
1675 else
1676 {
1677 mouse_button_count = 2;
1678 mouse_button_translate[0] = 0;
1679 mouse_button_translate[1] = 1;
1680 }
1681 mouse_position_hook = &mouse_get_pos;
1682 mouse_init ();
aee81730 1683 }
1b94449f
RS
1684 return present;
1685}
1686
87485d6f 1687#ifndef HAVE_X_WINDOWS
49a09c76
RS
1688/* See xterm.c for more info. */
1689void
1690pixel_to_glyph_coords (f, pix_x, pix_y, x, y, bounds, noclip)
1691 FRAME_PTR f;
1692 register int pix_x, pix_y;
1693 register int *x, *y;
1694 void /* XRectangle */ *bounds;
1695 int noclip;
1696{
1697 if (bounds) abort ();
1698
1699 /* Ignore clipping. */
1700
1701 *x = pix_x;
1702 *y = pix_y;
1703}
1704
1705void
1706glyph_to_pixel_coords (f, x, y, pix_x, pix_y)
1707 FRAME_PTR f;
1708 register int x, y;
1709 register int *pix_x, *pix_y;
1710{
1711 *pix_x = x;
1712 *pix_y = y;
1713}
87485d6f
MW
1714\f
1715/* Simulation of X's menus. Nothing too fancy here -- just make it work
1716 for now.
1717
1718 Actually, I don't know the meaning of all the parameters of the functions
1719 here -- I only know how they are called by xmenu.c. I could of course
1720 grab the nearest Xlib manual (down the hall, second-to-last door on the
1721 left), but I don't think it's worth the effort. */
1722
1723static XMenu *
1724IT_menu_create ()
1725{
1726 XMenu *menu;
1727
1728 menu = (XMenu *) xmalloc (sizeof (XMenu));
1729 menu->allocated = menu->count = menu->panecount = menu->width = 0;
1730 return menu;
1731}
1732
1733/* Allocate some (more) memory for MENU ensuring that there is room for one
1734 for item. */
aee81730 1735
87485d6f
MW
1736static void
1737IT_menu_make_room (XMenu *menu)
1738{
1739 if (menu->allocated == 0)
1740 {
1741 int count = menu->allocated = 10;
1742 menu->text = (char **) xmalloc (count * sizeof (char *));
1743 menu->submenu = (XMenu **) xmalloc (count * sizeof (XMenu *));
1744 menu->panenumber = (int *) xmalloc (count * sizeof (int));
1745 }
1746 else if (menu->allocated == menu->count)
1747 {
1748 int count = menu->allocated = menu->allocated + 10;
1749 menu->text
1750 = (char **) xrealloc (menu->text, count * sizeof (char *));
1751 menu->submenu
1752 = (XMenu **) xrealloc (menu->submenu, count * sizeof (XMenu *));
1753 menu->panenumber
1754 = (int *) xrealloc (menu->panenumber, count * sizeof (int));
1755 }
1756}
1757
1758/* Search the given menu structure for a given pane number. */
aee81730 1759
87485d6f
MW
1760static XMenu *
1761IT_menu_search_pane (XMenu *menu, int pane)
1762{
1763 int i;
1764 XMenu *try;
1765
1766 for (i = 0; i < menu->count; i++)
1767 if (menu->submenu[i])
aee81730
RS
1768 {
1769 if (pane == menu->panenumber[i])
1770 return menu->submenu[i];
1771 else if ((try = IT_menu_search_pane (menu->submenu[i], pane)))
87485d6f 1772 return try;
aee81730 1773 }
87485d6f
MW
1774 return (XMenu *) 0;
1775}
1776
1777/* Determine how much screen space a given menu needs. */
aee81730 1778
87485d6f
MW
1779static void
1780IT_menu_calc_size (XMenu *menu, int *width, int *height)
1781{
1782 int i, h2, w2, maxsubwidth, maxheight;
1783
1784 maxsubwidth = 0;
1785 maxheight = menu->count;
1786 for (i = 0; i < menu->count; i++)
1787 {
1788 if (menu->submenu[i])
1789 {
1790 IT_menu_calc_size (menu->submenu[i], &w2, &h2);
1791 if (w2 > maxsubwidth) maxsubwidth = w2;
1792 if (i + h2 > maxheight) maxheight = i + h2;
1793 }
1794 }
1795 *width = menu->width + maxsubwidth;
1796 *height = maxheight;
1797}
1798
1799/* Display MENU at (X,Y) using FACES. */
aee81730 1800
87485d6f
MW
1801static void
1802IT_menu_display (XMenu *menu, int y, int x, int *faces)
1803{
1804 int i, j, face, width;
1805 GLYPH *text, *p;
1806 char *q;
1807 int mx, my;
1808 int enabled, mousehere;
1809 int row, col;
1810
1811 width = menu->width;
1812 text = (GLYPH *) xmalloc ((width + 2) * sizeof (GLYPH));
1813 ScreenGetCursor (&row, &col);
1814 mouse_get_xy (&mx, &my);
1815 mouse_off ();
1816 (*update_begin_hook) ();
1817 for (i = 0; i < menu->count; i++)
1818 {
1819 (*cursor_to_hook) (y + i, x);
1820 enabled
1821 = (!menu->submenu[i] && menu->panenumber[i]) || (menu->submenu[i]);
1822 mousehere = (y + i == my && x <= mx && mx < x + width + 2);
1823 face = faces[enabled + mousehere * 2];
1824 p = text;
1825 *p++ = FAST_MAKE_GLYPH (' ', face);
1826 for (j = 0, q = menu->text[i]; *q; j++)
1827 *p++ = FAST_MAKE_GLYPH (*q++, face);
1828 for (; j < width; j++)
1829 *p++ = FAST_MAKE_GLYPH (' ', face);
1830 *p++ = FAST_MAKE_GLYPH (menu->submenu[i] ? 16 : ' ', face);
1831 (*write_glyphs_hook) (text, width + 2);
1832 }
1833 internal_flush (stdout);
1834 (*update_end_hook) ();
1835 mouse_on ();
1836 ScreenSetCursor (row, col);
1837 xfree (text);
1838}
1839
1840/* Create a brand new menu structure. */
aee81730 1841
87485d6f 1842XMenu *
1189e5a2 1843XMenuCreate (Display *foo1, Window foo2, char *foo3)
87485d6f
MW
1844{
1845 return IT_menu_create ();
1846}
1847
1848/* Create a new pane and place it on the outer-most level. It is not
1849 clear that it should be placed out there, but I don't know what else
1850 to do. */
aee81730 1851
87485d6f 1852int
1189e5a2 1853XMenuAddPane (Display *foo, XMenu *menu, char *txt, int enable)
87485d6f
MW
1854{
1855 int len;
1856
1857 if (!enable)
1858 abort ();
1859
1860 IT_menu_make_room (menu);
1861 menu->submenu[menu->count] = IT_menu_create ();
1862 menu->text[menu->count] = txt;
1863 menu->panenumber[menu->count] = ++menu->panecount;
1864 menu->count++;
aee81730
RS
1865 if ((len = strlen (txt)) > menu->width)
1866 menu->width = len;
87485d6f
MW
1867 return menu->panecount;
1868}
1869
1870/* Create a new item in a menu pane. */
aee81730 1871
87485d6f 1872int
1189e5a2
RS
1873XMenuAddSelection (Display *bar, XMenu *menu, int pane,
1874 int foo, char *txt, int enable)
87485d6f
MW
1875{
1876 int len;
1877
1878 if (pane)
1879 if (!(menu = IT_menu_search_pane (menu, pane)))
1880 return XM_FAILURE;
1881 IT_menu_make_room (menu);
1882 menu->submenu[menu->count] = (XMenu *) 0;
1883 menu->text[menu->count] = txt;
1884 menu->panenumber[menu->count] = enable;
1885 menu->count++;
aee81730
RS
1886 if ((len = strlen (txt)) > menu->width)
1887 menu->width = len;
87485d6f
MW
1888 return XM_SUCCESS;
1889}
1890
1891/* Decide where the menu would be placed if requested at (X,Y). */
aee81730 1892
1189e5a2
RS
1893void
1894XMenuLocate (Display *foo0, XMenu *menu, int foo1, int foo2, int x, int y,
87485d6f
MW
1895 int *ulx, int *uly, int *width, int *height)
1896{
1897 if (menu->count == 1 && menu->submenu[0])
1898 /* Special case: the menu consists of only one pane. */
1899 IT_menu_calc_size (menu->submenu[0], width, height);
1900 else
1901 IT_menu_calc_size (menu, width, height);
1902 *ulx = x + 1;
1903 *uly = y;
1904 *width += 2;
1905}
1906
aee81730 1907struct IT_menu_state
87485d6f
MW
1908{
1909 void *screen_behind;
1910 XMenu *menu;
1911 int pane;
1912 int x, y;
aee81730 1913};
87485d6f
MW
1914
1915
1916/* Display menu, wait for user's response, and return that response. */
aee81730 1917
87485d6f 1918int
1189e5a2 1919XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx,
87485d6f
MW
1920 int x0, int y0, unsigned ButtonMask, char **txt)
1921{
aee81730 1922 struct IT_menu_state *state;
87485d6f
MW
1923 int statecount;
1924 int x, y, i, b;
1925 int screensize;
1926 int faces[4], selectface;
1927 int leave, result, onepane;
1928
1929 /* Just in case we got here without a mouse present... */
aee81730 1930 if (have_mouse <= 0)
87485d6f
MW
1931 return XM_IA_SELECT;
1932
aee81730 1933 state = alloca (menu->panecount * sizeof (struct IT_menu_state));
87485d6f
MW
1934 screensize = ScreenRows () * ScreenCols () * 2;
1935 faces[0]
1936 = compute_glyph_face (&the_only_frame,
1937 face_name_id_number
1938 (&the_only_frame,
1939 intern ("msdos-menu-passive-face")),
1940 0);
1941 faces[1]
1942 = compute_glyph_face (&the_only_frame,
1943 face_name_id_number
1944 (&the_only_frame,
1945 intern ("msdos-menu-active-face")),
1946 0);
1947 selectface
1948 = face_name_id_number (&the_only_frame, intern ("msdos-menu-select-face"));
1949 faces[2] = compute_glyph_face (&the_only_frame, selectface, faces[0]);
1950 faces[3] = compute_glyph_face (&the_only_frame, selectface, faces[1]);
1951
1952 statecount = 1;
1953 state[0].menu = menu;
1954 mouse_off ();
1955 ScreenRetrieve (state[0].screen_behind = xmalloc (screensize));
1956 mouse_on ();
1957 if ((onepane = menu->count == 1 && menu->submenu[0]))
1958 {
1959 menu->width = menu->submenu[0]->width;
1960 state[0].menu = menu->submenu[0];
1961 }
1962 else
1963 {
1964 state[0].menu = menu;
1965 }
1966 state[0].x = x0 - 1;
1967 state[0].y = y0;
1968 state[0].pane = onepane;
1969
1970 mouse_last_x = -1; /* A hack that forces display. */
1971 leave = 0;
1972 while (!leave)
1973 {
1974 mouse_check_moved ();
f2f23562 1975 if (selected_frame->mouse_moved)
87485d6f 1976 {
f2f23562 1977 selected_frame->mouse_moved = 0;
87485d6f
MW
1978 result = XM_IA_SELECT;
1979 mouse_get_xy (&x, &y);
1980 for (i = 0; i < statecount; i++)
1981 if (state[i].x <= x && x < state[i].x + state[i].menu->width + 2)
1982 {
1983 int dy = y - state[i].y;
1984 if (0 <= dy && dy < state[i].menu->count)
1985 {
1986 if (!state[i].menu->submenu[dy])
1987 if (state[i].menu->panenumber[dy])
1988 result = XM_SUCCESS;
1989 else
1990 result = XM_IA_SELECT;
1991 *pane = state[i].pane - 1;
1992 *selidx = dy;
1993 /* We hit some part of a menu, so drop extra menues that
1994 have been opened. That does not include an open and
1995 active submenu. */
1996 if (i != statecount - 2
1997 || state[i].menu->submenu[dy] != state[i+1].menu)
1998 while (i != statecount - 1)
1999 {
2000 statecount--;
2001 mouse_off ();
2002 ScreenUpdate (state[statecount].screen_behind);
2003 mouse_on ();
2004 xfree (state[statecount].screen_behind);
2005 }
2006 if (i == statecount - 1 && state[i].menu->submenu[dy])
2007 {
2008 IT_menu_display (state[i].menu,
2009 state[i].y,
2010 state[i].x,
2011 faces);
2012 state[statecount].menu = state[i].menu->submenu[dy];
2013 state[statecount].pane = state[i].menu->panenumber[dy];
2014 mouse_off ();
2015 ScreenRetrieve (state[statecount].screen_behind
2016 = xmalloc (screensize));
2017 mouse_on ();
2018 state[statecount].x
2019 = state[i].x + state[i].menu->width + 2;
2020 state[statecount].y = y;
2021 statecount++;
2022 }
2023 }
2024 }
2025 IT_menu_display (state[statecount - 1].menu,
2026 state[statecount - 1].y,
2027 state[statecount - 1].x,
2028 faces);
2029 }
2030 for (b = 0; b < mouse_button_count; b++)
2031 {
2032 (void) mouse_pressed (b, &x, &y);
2033 if (mouse_released (b, &x, &y))
2034 leave = 1;
2035 }
2036 }
2037
2038 mouse_off ();
2039 ScreenUpdate (state[0].screen_behind);
2040 mouse_on ();
2041 while (statecount--)
2042 xfree (state[statecount].screen_behind);
2043 return result;
2044}
2045
2046/* Dispose of a menu. */
aee81730 2047
1189e5a2
RS
2048void
2049XMenuDestroy (Display *foo, XMenu *menu)
87485d6f
MW
2050{
2051 int i;
2052 if (menu->allocated)
2053 {
2054 for (i = 0; i < menu->count; i++)
2055 if (menu->submenu[i])
1189e5a2 2056 XMenuDestroy (foo, menu->submenu[i]);
87485d6f
MW
2057 xfree (menu->text);
2058 xfree (menu->submenu);
2059 xfree (menu->panenumber);
2060 }
2061 xfree (menu);
2062}
2063
aee81730
RS
2064int
2065x_pixel_width (struct frame *f)
87485d6f 2066{
aee81730 2067 return FRAME_WIDTH (f);
87485d6f
MW
2068}
2069
aee81730
RS
2070int
2071x_pixel_height (struct frame *f)
87485d6f 2072{
aee81730 2073 return FRAME_HEIGHT (f);
87485d6f
MW
2074}
2075#endif /* !HAVE_X_WINDOWS */
49a09c76 2076
1b94449f 2077#endif /* MSDOS */