1 /* -*-C-*-
2 
3 Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994,
4     1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5     2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Massachusetts
6     Institute of Technology
7 
8 This file is part of MIT/GNU Scheme.
9 
10 MIT/GNU Scheme is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or (at
13 your option) any later version.
14 
15 MIT/GNU Scheme is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with MIT/GNU Scheme; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
23 USA.
24 
25 */
26 
27 #define USE_PMCON
28 /* #define USE_VIO */
29 /* #define USE_PMIO */
30 
31 #include "os2.h"
32 
33 #ifdef USE_PMCON
34 
35 extern void OS2_initialize_pm_console (void);
36 extern int  OS2_pm_console_getch (void);
37 extern void OS2_pm_console_write (const char *, size_t);
38 
39 #else
40 #ifdef USE_PMIO
41 
42 #include <pmio.h>
43 
44 #endif
45 #endif
46 
47 #ifdef USE_PMCON
48 #define getch OS2_pm_console_getch
49 #else
50 #ifndef USE_PMIO
51 static int getch (void);
52 #endif
53 #endif
54 
55 static void console_thread (void *);
56 static void grab_console_lock (void);
57 static void release_console_lock (void);
58 
59 static void process_input_char (char);
60 static void do_rubout (void);
61 static void add_to_line (char);
62 static void do_newline (void);
63 static void do_self_insert (char);
64 static void add_char_to_line_buffer (char);
65 static void finish_line (void);
66 static void send_char (char);
67 static void send_readahead (msg_t *);
68 static void handle_console_interrupt (msg_t *);
69 
70 static void console_operator
71   (Tchannel, chop_t, choparg_t, choparg_t, choparg_t);;
72 static void flush_input (void);
73 static void console_input_buffered (Tchannel, int, int *);
74 static void console_output_cooked (Tchannel, int, int *);
75 
76 static void write_char (char, int);
77 static void write_output (const char *, size_t, int);
78 static void write_output_1 (const char *, const char *);
79 static unsigned int char_output_length (char);
80 
81 static HMTX console_lock;
82 static int input_buffered_p;
83 static int output_cooked_p;
84 static qid_t console_writer_qid;
85 static channel_context_t * console_context;
86 static void * line_buffer;
87 
88 TID OS2_console_tid;
89 
90 void
OS2_initialize_console(void)91 OS2_initialize_console (void)
92 {
93 #ifdef USE_PMCON
94   OS2_initialize_pm_console ();
95 #else
96 #ifdef USE_PMIO
97   pmio_fontspec = "6.System VIO";
98   set_width (80);
99   set_height (40);
100   start_pmio ();
101 #endif
102 #endif
103   console_lock = (OS2_create_mutex_semaphore (0, 0));
104   input_buffered_p = 1;
105   output_cooked_p = 1;
106   console_context = (OS2_make_channel_context ());
107   OS2_open_qid ((CHANNEL_CONTEXT_READER_QID (console_context)),
108 		OS2_scheme_tqueue);
109   console_writer_qid = (CHANNEL_CONTEXT_WRITER_QID (console_context));
110   OS2_open_qid (console_writer_qid, (OS2_make_std_tqueue ()));
111   (CHANNEL_CONTEXT_FIRST_READ_P (console_context)) = 0;
112   OS2_console_tid = (OS2_beginthread (console_thread, 0, 0x4000));
113   (CHANNEL_CONTEXT_TID (console_context)) = OS2_console_tid;
114 }
115 
116 static void
console_thread(void * arg)117 console_thread (void * arg)
118 {
119   EXCEPTIONREGISTRATIONRECORD registration;
120   grab_console_lock ();
121   line_buffer = (OS2_make_readahead_buffer ());
122   release_console_lock ();
123   (void) OS2_thread_initialize ((&registration), console_writer_qid);
124   while (1)
125     {
126       int c = (getch ());
127       if (c == EOF)
128 	{
129 	  msg_t * message = (OS2_make_readahead ());
130 	  (SM_READAHEAD_SIZE (message)) = 0;
131 	  send_readahead (message);
132 	  break;
133 	}
134       {
135 	int code = (OS2_keyboard_interrupt_handler (c));
136 	if (code == '\0')
137 	  process_input_char (c);
138 	else
139 	  {
140 	    msg_t * message = (OS2_create_message (mt_console_interrupt));
141 	    (SM_CONSOLE_INTERRUPT_CODE (message)) = code;
142 	    OS2_send_message (OS2_interrupt_qid, message);
143 	    /* Flush buffers only for certain chars? */
144 	    flush_input ();
145 	    if (c == '\a')
146 	      write_char ('\a', 0);
147 	  }
148       }
149     }
150   {
151     tqueue_t * tqueue = (OS2_qid_tqueue (console_writer_qid));
152     OS2_close_qid (console_writer_qid);
153     OS2_close_std_tqueue (tqueue);
154   }
155   OS2_endthread ();
156 }
157 
158 #if ((!defined(USE_PMCON)) && (!defined(USE_PMIO)))
159 static int
getch(void)160 getch (void)
161 {
162   while (1)
163     {
164 #ifdef USE_VIO
165       KBDKEYINFO info;
166       XTD_API_CALL
167 	(kbd_char_in, ((&info), IO_WAIT, 0),
168 	 {
169 	   if (rc == ERROR_KBD_INVALID_HANDLE)
170 	     return (EOF);
171 	 });
172       if ((info . fbStatus) == 0x40)
173 	return (info . chChar);
174 #else
175       int c = (_getch ());
176       if (c == EOF)
177 	return (EOF);
178       else if ((c == 0) || (c == 0xe0))
179 	{
180 	  /* Discard extended keycodes. */
181 	  if ((_getch ()) == EOF)
182 	    return (EOF);
183 	}
184       else
185 	return (c);
186 #endif
187     }
188 }
189 #endif /* not USE_PMIO */
190 
191 static void
grab_console_lock(void)192 grab_console_lock (void)
193 {
194   OS2_request_mutex_semaphore (console_lock);
195 }
196 
197 static void
release_console_lock(void)198 release_console_lock (void)
199 {
200   OS2_release_mutex_semaphore (console_lock);
201 }
202 
203 static void
process_input_char(char c)204 process_input_char (char c)
205 {
206   if (!input_buffered_p)
207     send_char (c);
208   else switch (c)
209     {
210     case '\b':
211     case '\177':
212       do_rubout ();
213       break;
214     case '\r':
215       do_self_insert ('\r');
216       do_self_insert ('\n');
217       finish_line ();
218       break;
219     default:
220       do_self_insert (c);
221       break;
222     }
223 }
224 
225 static void
do_self_insert(char c)226 do_self_insert (char c)
227 {
228   add_char_to_line_buffer (c);
229   write_char (c, 1);
230 }
231 
232 static void
add_char_to_line_buffer(char c)233 add_char_to_line_buffer (char c)
234 {
235   grab_console_lock ();
236   OS2_readahead_buffer_insert (line_buffer, c);
237   release_console_lock ();
238 }
239 
240 static void
do_rubout(void)241 do_rubout (void)
242 {
243   grab_console_lock ();
244   if (OS2_readahead_buffer_emptyp (line_buffer))
245     {
246       release_console_lock ();
247       write_char ('\a', 0);
248       return;
249     }
250   {
251     unsigned int n
252       = (char_output_length (OS2_readahead_buffer_rubout (line_buffer)));
253     unsigned int i;
254     release_console_lock ();
255     for (i = 0; (i < n); i += 1)
256       write_char ('\b', 0);
257     for (i = 0; (i < n); i += 1)
258       write_char (' ', 0);
259     for (i = 0; (i < n); i += 1)
260       write_char ('\b', 0);
261   }
262 }
263 
264 static void
finish_line(void)265 finish_line (void)
266 {
267   msg_t ** messages;
268   msg_t ** scan;
269   grab_console_lock ();
270   messages = (OS2_readahead_buffer_read_all (line_buffer));
271   release_console_lock ();
272   scan = messages;
273   while (1)
274     {
275       msg_t * msg = (*scan++);
276       if (msg == 0)
277 	break;
278       send_readahead (msg);
279     }
280   OS_free (messages);
281 }
282 
283 static void
send_char(char c)284 send_char (char c)
285 {
286   msg_t * message = (OS2_make_readahead ());
287   (SM_READAHEAD_SIZE (message)) = 1;
288   ((SM_READAHEAD_DATA (message)) [0]) = c;
289   send_readahead (message);
290 }
291 
292 static void
send_readahead(msg_t * message)293 send_readahead (msg_t * message)
294 {
295   OS2_send_message (console_writer_qid, message);
296   (void) OS2_wait_for_readahead_ack (console_writer_qid);
297 }
298 
299 void
OS2_initialize_console_channel(Tchannel channel)300 OS2_initialize_console_channel (Tchannel channel)
301 {
302   (CHANNEL_OPERATOR_CONTEXT (channel)) = console_context;
303   (CHANNEL_OPERATOR (channel)) = console_operator;
304 }
305 
306 static void
console_operator(Tchannel channel,chop_t operation,choparg_t arg1,choparg_t arg2,choparg_t arg3)307 console_operator (Tchannel channel, chop_t operation,
308 		  choparg_t arg1, choparg_t arg2, choparg_t arg3)
309 {
310   switch (operation)
311     {
312     case chop_read:
313       (* ((long *) arg3))
314 	= (OS2_channel_thread_read
315 	   (channel, ((char *) arg1), ((size_t) arg2)));
316       break;
317     case chop_write:
318       write_output (((const char *) arg1), ((size_t) arg2), output_cooked_p);
319       (* ((long *) arg3)) = ((size_t) arg2);
320       break;
321     case chop_close:
322     case chop_output_flush:
323     case chop_output_drain:
324       break;
325     case chop_input_flush:
326       flush_input ();
327       break;
328     case chop_input_buffered:
329       console_input_buffered (channel, ((int) arg1), ((int *) arg2));
330       break;
331     case chop_output_cooked:
332       console_output_cooked (channel, ((int) arg1), ((int *) arg2));
333       break;
334     default:
335       OS2_logic_error ("Unknown operation for console.");
336       break;
337     }
338 }
339 
340 static void
flush_input(void)341 flush_input (void)
342 {
343   msg_t ** messages;
344   msg_t ** scan;
345   grab_console_lock ();
346   messages = (OS2_readahead_buffer_read_all (line_buffer));
347   release_console_lock ();
348   scan = messages;
349   while (1)
350     {
351       msg_t * msg = (*scan++);
352       if (msg == 0)
353 	break;
354       OS2_destroy_message (msg);
355     }
356   OS_free (messages);
357 }
358 
359 static void
console_input_buffered(Tchannel channel,int new,int * pold)360 console_input_buffered (Tchannel channel, int new, int * pold)
361 {
362   if (new < 0)
363     (* pold) = input_buffered_p;
364   else
365     {
366       int old = input_buffered_p;
367       input_buffered_p = new;
368       if (old && (!new))
369 	flush_input ();
370     }
371 }
372 
373 static void
console_output_cooked(Tchannel channel,int new,int * pold)374 console_output_cooked (Tchannel channel, int new, int * pold)
375 {
376   if (new < 0)
377     (* pold) = output_cooked_p;
378   else
379     output_cooked_p = (new ? 1 : 0);
380 }
381 
382 static void
write_char(char c,int cooked_p)383 write_char (char c, int cooked_p)
384 {
385   write_output ((&c), 1, cooked_p);
386 }
387 
388 void
OS2_console_write(const char * data,size_t size)389 OS2_console_write (const char * data, size_t size)
390 {
391   write_output (data, size, 2);
392 }
393 
394 static void
write_output(const char * data,size_t size,int cooked_p)395 write_output (const char * data, size_t size, int cooked_p)
396 {
397   const char * scan = data;
398   const char * end = (scan + size);
399   char output_translation [256];
400   char * out = output_translation;
401   char * out_limit = (out + ((sizeof (output_translation)) - 4));
402   char c;
403   if (cooked_p == 0)
404     write_output_1 (scan, end);
405   else
406     while (1)
407       {
408 	if ((scan == end) || (out >= out_limit))
409 	  {
410 	    write_output_1 (output_translation, out);
411 	    if (scan == end)
412 	      break;
413 	    out = output_translation;
414 	  }
415 	c = (*scan++);
416 	if ((cooked_p == 2) && (c == '\n'))
417 	  {
418 	    (*out++) = '\r';
419 	    (*out++) = '\n';
420 	  }
421 	else if ((isprint (c))
422 		 || (c == '\f')
423 		 || (c == '\a')
424 		 || (c == '\r')
425 		 || (c == '\n'))
426 	  (*out++) = c;
427 	else if (c < 0x20)
428 	  {
429 	    (*out++) = '^';
430 	    (*out++) = ('@' + c);
431 	  }
432 	else
433 	  {
434 	    (*out++) = '\\';
435 	    (*out++) = ('0' + ((c >> 6) & 3));
436 	    (*out++) = ('0' + ((c >> 3) & 7));
437 	    (*out++) = ('0' + (c & 7));
438 	  }
439       }
440 }
441 
442 static void
write_output_1(const char * scan,const char * end)443 write_output_1 (const char * scan, const char * end)
444 {
445 #ifdef USE_PMCON
446 
447   OS2_pm_console_write (scan, (end - scan));
448 
449 #else /* not USE_PMCON */
450 #ifdef USE_PMIO
451 
452   put_raw ((end - scan), scan);
453 
454 #else /* not USE_PMIO */
455 #ifdef USE_VIO
456 
457   STD_API_CALL (vio_wrt_tty, (((PCH) scan), (end - scan), 0));
458 
459 #else /* not USE_VIO */
460 
461   while (1)
462     {
463       ULONG n;
464       APIRET rc = (dos_write (1, ((void *) scan), (end - scan), (& n)));
465       if (rc != NO_ERROR)
466 	break;
467       scan += n;
468       if (scan == end)
469 	break;
470     }
471 
472 #endif /* not USE_VIO */
473 #endif /* not USE_PMIO */
474 #endif /* not USE_PMCON */
475 }
476 
477 static unsigned int
char_output_length(char c)478 char_output_length (char c)
479 {
480   return ((isprint (c)) ? 1 : (c < 0x20) ? 2 : 4);
481 }
482