1 /*
2 Copyright (C) 2004-2017,2018 John E. Davis
3
4 This file is part of the S-Lang Library.
5
6 The S-Lang Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
11 The S-Lang Library 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 GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 USA.
20 */
21
22 #include "slinclud.h"
23
24 #ifdef __DJGPP__
25 # define _NAIVE_DOS_REGS
26 #endif
27
28 #include <dos.h>
29
30 #if defined (__EMX__)
31 # define int86 _int86
32 # define delay _sleep2
33 #endif /* __EMX__ */
34
35 #if defined (__WATCOMC__)
36 # include <conio.h>
37 # include <bios.h>
38 # define int86 int386
39 #endif
40
41 #if defined (__DJGPP__)
42 # include <sys/farptr.h>
43 # include <go32.h>
44 # include <bios.h>
45 #endif
46
47 #ifndef _NKEYBRD_READ
48 # define _NKEYBRD_READ 0x0
49 #endif
50 #ifndef _NKEYBRD_READY
51 # define _NKEYBRD_READY 0x1
52 #endif
53 #ifndef _NKEYBRD_SHIFTSTATUS
54 # define _NKEYBRD_SHIFTSTATUS 0x2
55 #endif
56
57 #define BIOSKEY slbioskey
58 #if defined(__WATCOMC__)
59 # define keyWaiting() _bios_keybrd(_NKEYBRD_READY)
60 #else
61 # define keyWaiting() BIOSKEY(_NKEYBRD_READY)
62 #endif
63
64 #ifndef __EMX__
65 # define USE_MOUSE_CODE 1
66 #else
67 # define USE_MOUSE_CODE 0
68 #endif
69
70 #include "slang.h"
71 #include "_slang.h"
72
73 #ifdef __cplusplus
74 # define _DOTS_ ...
75 #else
76 # define _DOTS_ void
77 #endif
78
79 #if !defined (__EMX__) && !defined (__GO32__) && !defined (__WATCOMC__)
80 #define HAS_INT9
81 #endif
82
83 #ifdef __GO32__
84 # include <signal.h>
85 #endif
86
87 #if defined (HAS_INT9)
88 static void interrupt (*int9_old) (_DOTS_);
89 static unsigned char far *shift = (unsigned char far *) 0x417;
90 static unsigned int Abort_Scan_Code = 34; /* 34 = scan code for ^G */
91
92 /*----------------------------------------------------------------------*\
93 * an interrupt 9 handler, not for use with most 32 bit compilers
94 \*----------------------------------------------------------------------*/
int9_handler(_DOTS_)95 static void interrupt int9_handler (_DOTS_)
96 {
97 unsigned char s, s1;
98
99 s1 = *shift & 0xF; /* ignore caps, ins, num lock, scroll lock */
100 s = inp (0x60);
101 if (s1 & 0x04) /* control key */
102 {
103 if (s == Abort_Scan_Code)
104 {
105 if (SLang_Ignore_User_Abort == 0) SLang_Error = SL_USER_BREAK;
106 SLKeyBoard_Quit = 1;
107 }
108 }
109 (*int9_old) ();
110 }
111 #endif /* HAS_INT9 */
112
int9_change(int set)113 static void int9_change (int set)
114 {
115 #if defined (HAS_INT9)
116 if (set) /* install a new handler */
117 {
118 if (int9_old != NULL) return;
119 int9_old = getvect (9);
120 setvect (9, int9_handler);
121 }
122 else if (int9_old != NULL) /* restore the old handler */
123 {
124 setvect (9, int9_old);
125 int9_old = NULL;
126 }
127 #else
128 (void) set;
129 #endif /* HAS_INT9 */
130 }
131
132 /*----------------------------------------------------------------------*\
133 * Function: static void set_ctrl_break (int state);
134 *
135 * set the control-break setting
136 \*----------------------------------------------------------------------*/
set_ctrl_break(int state)137 static void set_ctrl_break (int state)
138 {
139 #if defined (__EMX__)
140 (void) state; /* not really required */
141 #else /* __EMX__ */
142
143 static int prev = 0;
144
145 # if defined (__GO32__)
146 if (state == 0)
147 {
148 # if __DJGPP__ >= 2
149 signal (SIGINT, SIG_IGN);
150 # endif
151 prev = getcbrk ();
152 setcbrk (0);
153 }
154 else
155 {
156 # if __DJGPP__ >= 2
157 signal (SIGINT, SIG_DFL);
158 # endif
159 setcbrk (prev);
160 }
161 # else /* __GO32__ */
162 # if defined(__WATCOMC__)
163 fprintf (stderr, "Have not yet defined set_ctrl_break for __WATCOMC__\n");
164 prev = state;
165 # else
166 asm mov dl, byte ptr prev
167 asm mov ax, state
168 asm cmp ax, 0
169 asm jne L1
170 asm mov ah, 33h
171 asm mov al, 0
172 asm mov dl, byte ptr prev
173 asm int 21h
174 asm xor ax, ax
175 asm mov al, dl
176 asm mov prev, ax
177 asm mov dl, 0
178 L1:
179 asm mov al, 1
180 asm mov ah, 33h
181 asm int 21h
182 # endif /* __WATCOMC__ */
183 # endif /* __GO32__ */
184 #endif /* __EMX__ */
185 }
186
187 /*----------------------------------------------------------------------*\
188 * static unsigned int slbioskey (int op);
189 *
190 * op 0-2 (standard) and 0x10-0x12 (extended) are valid
191 *
192 * 0, 0x10 _NKEYBRD_READ - read the key
193 * 1, 0x11 _NKEYBRD_READY - check if a key is waiting
194 * if so give a peek of its value, otherwise return 0
195 * 2, 0x12 _NKEYBRD_SHIFTSTATUS - get shift flags
196 * (Ins, Cap, Num, Scroll, Alt, ^Ctrl L_shift, R_shift)
197 * flags = ICNSA^LR only the lower byte is valid!
198 \*----------------------------------------------------------------------*/
199 static int bios_key_f = 0;
slbioskey(int op)200 static unsigned int slbioskey (int op)
201 {
202 union REGS r;
203 r.h.ah = (op & 0x03) | bios_key_f;
204 int86 (0x16, &r, &r);
205 #if defined(__WATCOMC__)
206 /* return (_bios_keybrd ((op & 0x03) | bios_key_f)); */
207 # if 1 /* the correct zero flag for watcom? */
208 /* is zero flag set? (no key waiting) */
209 if ((op & _NKEYBRD_READY) && (r.x.cflag & 0x40) == 0x40) return 0;
210 # else /* the correct zero flag for watcom? */
211 /* is zero flag set? (no key waiting) */
212 if ((op & _NKEYBRD_READY) && (r.x.cflag & 0x4)) return 0;
213 # endif
214 return (r.x.eax & 0xffff);
215 #else
216 /* is zero flag set? (no key waiting) */
217 if (op & _NKEYBRD_READY)
218 {
219 if ((r.x.flags & 0x40) == 0x40)
220 return 0;
221 if (r.x.ax == 0) /* CTRL-BREAK */
222 return -1;
223 }
224 return (r.x.ax & 0xffff);
225 #endif
226 }
227
228 #if USE_MOUSE_CODE
229 /*----------------------------------------------------------------------*\
230 * Simple mouse routines for 16/32-bit DOS-targets.
231 * Gisle Vanem <giva@bgnett.no>
232 \*----------------------------------------------------------------------*/
233
234 #define HARD_MOUSE_RESET 0
235
236 static int Have_Mouse = 0;
237 static int Process_Mouse_Events = 0;
238
239 /*----------------------------------------------------------------------*\
240 * peem_far_mem()
241 *
242 \*----------------------------------------------------------------------*/
peek_dos_mem(unsigned long dos_addr,unsigned char * pentry)243 static unsigned long peek_dos_mem (unsigned long dos_addr,
244 unsigned char *pentry)
245 {
246 unsigned long vector;
247 unsigned char entry;
248
249 #if defined(__DJGPP__)
250 # define MAKE_LINEAR(seg,ofs) ((unsigned long)(((seg) << 4) + (ofs)))
251 vector = _farpeekl (_dos_ds, dos_addr);
252 entry = _farpeekb (_dos_ds, MAKE_LINEAR(vector >> 16, vector & 0xffff));
253
254 #elif defined(__EMX__)
255 vector = 0;
256 entry = 0; /* to-do!! */
257
258 #elif defined(__WATCOMC__) && defined(__FLAT__) /* wcc386 */
259 vector = *(unsigned long*) dos_addr;
260 entry = *(unsigned char*) vector;
261 #else
262 vector = *(unsigned long far*) dos_addr;
263 entry = *(unsigned char far*) vector;
264 #endif
265
266 if (pentry)
267 *pentry = entry;
268 return (vector);
269 }
270
271 /*----------------------------------------------------------------------*\
272 * mouse_pressed (int button, int *x_pos, int *y_pos)
273 *
274 * Return 1 if left (0) or right (1) mouse-button was pressed.
275 \*----------------------------------------------------------------------*/
mouse_pressed(int button,int * x_pos,int * y_pos)276 static int mouse_pressed (int button, int *x_pos, int *y_pos)
277 {
278 union REGS r;
279
280 r.x.ax = 5;
281 r.x.bx = button;
282 int86 (0x33, &r, &r);
283 *x_pos = r.x.cx;
284 *y_pos = r.x.dx;
285 return (r.x.bx);
286 }
287
288 /*----------------------------------------------------------------------*\
289 * mouse_show (int show)
290 *
291 * Show (show=1) or hide (show=0) the mouse cursor
292 \*----------------------------------------------------------------------*/
mouse_show(int show)293 static int mouse_show (int show)
294 {
295 union REGS r;
296
297 if (Have_Mouse == 0)
298 return -1;
299
300 r.x.ax = show ? 1 : 2;
301 int86 (0x33, &r, &r);
302 return 0;
303 }
304
305 /*----------------------------------------------------------------------*\
306 * mouse_exit (void)
307 *
308 * Do a soft-reset of the mouse-driver (hides cursor)
309 \*----------------------------------------------------------------------*/
mouse_exit(void)310 static void mouse_exit (void)
311 {
312 union REGS r;
313 r.x.ax = 0x21;
314 int86 (0x33, &r, &r);
315 }
316
317 /*----------------------------------------------------------------------*\
318 * mouse_init (void)
319 *
320 * Peek at mouse interrupt vector for a driver.
321 * Do a soft/hard-reset of the mouse-driver.
322 * Add a SLang atexit function
323 \*----------------------------------------------------------------------*/
mouse_init(void)324 static int mouse_init (void)
325 {
326 union REGS r;
327 unsigned char entry = 0;
328 unsigned long vector = peek_dos_mem (4*0x33, &entry);
329
330 if (!vector || entry == 0xCF) /* NULL or points to IRET */
331 return -1;
332
333 #if HARD_MOUSE_RESET
334 r.x.ax = 0; /* mouse hard-reset and reinit */
335 #else
336 r.x.ax = 0x21; /* mouse soft-reset and reinit */
337 #endif
338
339 int86 (0x33, &r, &r);
340 if (r.x.ax != 0xFFFF)
341 return -1;
342
343 (void) SLang_add_cleanup_function (mouse_exit);
344 Have_Mouse = 1;
345 return 0;
346 }
347
348 /*----------------------------------------------------------------------*\
349 * static int mouse_get_event (void);
350 *
351 * Poll mouse for changed button-state and encode x/y position and
352 * button state into an escape sequence "\e[M.."
353 \*----------------------------------------------------------------------*/
mouse_get_event(void)354 static int mouse_get_event (void)
355 {
356 char buf [6];
357 int x, y;
358
359 if (!Have_Mouse || Process_Mouse_Events == 0)
360 return (0);
361
362 if (mouse_pressed(0, &x, &y)) /* left button pressed? */
363 buf[3] = 040;
364 else if (mouse_pressed(1, &x, &y)) /* right button pressed */
365 buf[3] = 041;
366 else return (0);
367
368 #if 0 /* test */
369 fprintf (stderr, "mouse_get_event: x=%d, y=%d\n", x, y);
370 #endif
371
372 /*
373 * Taken from slw32tty.c / process_mouse_event():
374 *
375 * We have a simple press or release. Encode it as an escape sequence
376 * and buffer the result. The encoding is:
377 * 'ESC [ M b x y'
378 * where b represents the button state, and x,y represent the coordinates.
379 * The ESC is handled by the calling routine.
380 */
381 buf[0] = 27;
382 buf[1] = '[';
383 buf[2] = 'M';
384 buf[4] = 1 + ' ' + (x >> 3); /* textmode co-ordinates are 1/8th of */
385 buf[5] = 1 + ' ' + (y >> 3); /* graphics-mode co-ordinates */
386
387 if (SLang_buffer_keystring (buf, sizeof(buf)) < 0)
388 return (0);
389 return (1);
390 }
391
392 #endif /* USE_MOUSE_CODE */
393
394 /*----------------------------------------------------------------------*\
395 * Function: int SLang_init_tty (int abort_char, int no_flow_control,
396 * int opost);
397 *
398 * initialize the keyboard interface and attempt to set-up the interrupt 9
399 * handler if ABORT_CHAR is non-zero.
400 * NO_FLOW_CONTROL and OPOST are only for compatiblity and are ignored.
401 \*----------------------------------------------------------------------*/
SLang_init_tty(int abort_char,int no_flow_control,int opost)402 int SLang_init_tty (int abort_char, int no_flow_control, int opost)
403 {
404 (void) no_flow_control;
405 (void) opost;
406
407 SLKeyBoard_Quit = 0;
408
409 bios_key_f = 0x10; /* assume it's an enhanced keyboard */
410 #if defined (HAS_INT9)
411 bios_key_f &= peekb (0x40,0x96); /* verify it's true */
412 if (abort_char > 0) Abort_Scan_Code = (unsigned int) abort_char;
413 #else
414 (void) abort_char;
415 #endif
416
417 set_ctrl_break (0);
418
419 return 0;
420 }
421
422 /*----------------------------------------------------------------------*\
423 * Function: void SLang_reset_tty (void);
424 *
425 * reset the tty before exiting
426 \*----------------------------------------------------------------------*/
SLang_reset_tty(void)427 void SLang_reset_tty (void)
428 {
429 int9_change (0);
430 set_ctrl_break (1);
431 }
432
433 /*----------------------------------------------------------------------*\
434 * Function: int _pSLsys_input_pending (int tsecs);
435 *
436 * sleep for *tsecs tenths of a sec waiting for input
437 \*----------------------------------------------------------------------*/
_pSLsys_input_pending(int tsecs)438 int _pSLsys_input_pending (int tsecs)
439 {
440 if (keyWaiting()) return 1;
441
442 /* Convert tsecs to units of 20 ms */
443 tsecs = tsecs * 5;
444
445 /* If tsecs is less than 0, it represents millisecs */
446 if (tsecs < 0)
447 tsecs = -tsecs / 100;
448
449 while ((tsecs > 0) && (SLang_Input_Buffer_Len == 0))
450 {
451 delay (20); /* 20 ms or 1/50 sec */
452 #if USE_MOUSE_CODE
453 if (1 == mouse_get_event ())
454 return SLang_Input_Buffer_Len;
455 #endif
456 if (keyWaiting()) break;
457 tsecs--;
458 }
459 return (tsecs);
460 }
461
462 /*----------------------------------------------------------------------*\
463 * Function: unsigned int _pSLsys_getkey (void);
464 *
465 * Wait for and get the next available mouse-event / keystroke.
466 * Also re-maps some useful keystrokes.
467 *
468 * Backspace (^H) => Del (127)
469 * Ctrl-Space => ^@ (^@^3 - a pc NUL char)
470 * extended keys are prefixed by a null character
471 \*----------------------------------------------------------------------*/
_pSLsys_getkey(void)472 unsigned int _pSLsys_getkey (void)
473 {
474 unsigned int key, scan, ch, shift;
475
476 while ((SLang_Input_Buffer_Len == 0)
477 && (0 == _pSLsys_input_pending (300)))
478 ;
479
480 if (SLang_Input_Buffer_Len)
481 return SLang_getkey ();
482
483 key = BIOSKEY(_NKEYBRD_READ);
484 ch = key & 0xff;
485 scan = key >> 8;
486 shift = BIOSKEY(_NKEYBRD_SHIFTSTATUS) & 0xf;
487
488 if (key == 0x0e08)
489 return 127; /* Backspace key */
490
491 switch (ch)
492 {
493 case 32:
494 if (0 == (shift & 0x04))
495 break;
496 /* ^space = ^@ */
497 scan = 3; /* send back Ctrl-@ => ^@^C */
498 /* drop */
499 case 0xe0:
500 case 0: /* extended key code */
501 ch = _pSLpc_convert_scancode (scan, 0, 1);
502 }
503 return (ch);
504 }
505
506 /*----------------------------------------------------------------------*\
507 * Function: void SLang_set_abort_signal (void (*handler)(int));
508 \*----------------------------------------------------------------------*/
SLang_set_abort_signal(void (* handler)(int))509 int SLang_set_abort_signal (void (*handler)(int))
510 {
511 if (handler == NULL) int9_change (1);
512 return 0;
513 }
514
SLtt_set_mouse_mode(int mode,int force)515 int SLtt_set_mouse_mode (int mode, int force)
516 {
517 #if USE_MOUSE_CODE
518 (void) force;
519
520 if ((Have_Mouse == 0)
521 && (-1 == mouse_init ()))
522 {
523 Process_Mouse_Events = 0;
524 return -1;
525 }
526
527 Process_Mouse_Events = mode;
528 return mouse_show (mode);
529 #else
530 (void) mode;
531 (void) force;
532
533 return -1;
534 #endif
535 }
536