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