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 ((®istration), 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