2 * console-io.c: ConsoleDriver internal calls for Unix systems.
5 * Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 * Copyright (C) 2005-2009 Novell, Inc. (http://www.novell.com)
17 #ifdef HAVE_SYS_TIME_H
18 # include <sys/time.h>
20 #include <sys/types.h>
24 #include <mono/metadata/appdomain.h>
25 #include <mono/metadata/object-internals.h>
26 #include <mono/metadata/class-internals.h>
27 #include <mono/metadata/domain-internals.h>
28 #include <mono/metadata/metadata.h>
29 #include <mono/metadata/threadpool.h>
31 /* On solaris, curses.h must come before both termios.h and term.h */
42 /* Needed for FIONREAD under solaris */
43 #ifdef HAVE_SYS_FILIO_H
44 # include <sys/filio.h>
46 #ifdef HAVE_SYS_IOCTL_H
47 # include <sys/ioctl.h>
50 #include <mono/metadata/console-io.h>
51 #include <mono/metadata/exception.h>
53 static gboolean setup_finished
;
54 static gboolean atexit_called
;
56 /* The string used to return the terminal to its previous state */
57 static gchar
*teardown_str
;
59 /* The string used to set the terminal into keypad xmit mode after SIGCONT is received */
60 static gchar
*keypad_xmit_str
;
63 /* This is the last state used by Mono, used after a CONT signal is received */
64 static struct termios mono_attr
;
68 mono_console_init (void)
72 /* Make sure the standard file descriptors are opened */
73 fd
= open ("/dev/null", O_RDWR
);
74 while (fd
>= 0 && fd
< 3) {
75 fd
= open ("/dev/null", O_RDWR
);
80 static struct termios initial_attr
;
83 ves_icall_System_ConsoleDriver_Isatty (HANDLE handle
)
87 return isatty (GPOINTER_TO_INT (handle
));
91 set_property (gint property
, gboolean value
)
94 gboolean callset
= FALSE
;
99 if (tcgetattr (STDIN_FILENO
, &attr
) == -1)
102 check
= (attr
.c_lflag
& property
) != 0;
103 if ((value
|| check
) && !(value
&& check
)) {
106 attr
.c_lflag
|= property
;
108 attr
.c_lflag
&= ~property
;
114 if (tcsetattr (STDIN_FILENO
, TCSANOW
, &attr
) == -1)
122 ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo
)
125 return set_property (ECHO
, want_echo
);
129 ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break
)
131 return set_property (IGNBRK
, !want_break
);
135 ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout
)
139 struct timeval
*tvptr
;
147 FD_SET (STDIN_FILENO
, &rfds
);
149 divvy
= div (timeout
, 1000);
150 tv
.tv_sec
= divvy
.quot
;
151 tv
.tv_usec
= divvy
.rem
;
156 ret
= select (STDIN_FILENO
+ 1, &rfds
, NULL
, NULL
, tvptr
);
157 } while (ret
== -1 && errno
== EINTR
);
161 ret
= ioctl (STDIN_FILENO
, FIONREAD
, &nbytes
);
166 return (ret
> 0) ? ret
: 0;
169 static gint32 cols_and_lines
;
173 terminal_get_dimensions (void)
177 if (ioctl (STDIN_FILENO
, TIOCGWINSZ
, &ws
) == 0)
178 return (ws
.ws_col
<< 16) | ws
.ws_row
;
184 terminal_get_dimensions (void)
198 if (teardown_str
!= NULL
) {
199 write (STDOUT_FILENO
, teardown_str
, strlen (teardown_str
));
200 g_free (teardown_str
);
204 tcflush (STDIN_FILENO
, TCIFLUSH
);
205 tcsetattr (STDIN_FILENO
, TCSANOW
, &initial_attr
);
206 set_property (ECHO
, TRUE
);
207 setup_finished
= FALSE
;
211 do_console_cancel_event (void)
213 static MonoClassField
*cancel_handler_field
;
214 MonoDomain
*domain
= mono_domain_get ();
216 MonoDelegate
*load_value
;
218 MonoMethodMessage
*msg
;
225 klass
= mono_class_from_name (mono_defaults
.corlib
, "System", "Console");
229 if (cancel_handler_field
== NULL
) {
230 cancel_handler_field
= mono_class_get_field_from_name (klass
, "cancel_handler");
231 g_assert (cancel_handler_field
);
234 vtable
= mono_class_vtable_full (domain
, klass
, FALSE
);
237 mono_field_static_get_value (vtable
, cancel_handler_field
, &load_value
);
238 if (load_value
== NULL
)
241 klass
= load_value
->object
.vtable
->klass
;
242 method
= mono_class_get_method_from_name (klass
, "BeginInvoke", -1);
243 g_assert (method
!= NULL
);
244 im
= mono_get_delegate_invoke (method
->klass
);
245 msg
= mono_method_call_message_new (method
, NULL
, im
, NULL
, NULL
);
246 mono_thread_pool_add ((MonoObject
*) load_value
, msg
, NULL
, NULL
);
249 static gboolean in_sigint
;
251 sigint_handler (int signo
)
259 do_console_cancel_event ();
263 static struct sigaction save_sigcont
, save_sigint
, save_sigwinch
;
266 sigcont_handler (int signo
, void *the_siginfo
, void *data
)
268 // Ignore error, there is not much we can do in the sigcont handler.
269 tcsetattr (STDIN_FILENO
, TCSANOW
, &mono_attr
);
271 if (keypad_xmit_str
!= NULL
)
272 write (STDOUT_FILENO
, keypad_xmit_str
, strlen (keypad_xmit_str
));
274 // Call previous handler
275 if (save_sigcont
.sa_sigaction
!= NULL
&&
276 save_sigcont
.sa_sigaction
!= (void *)SIG_DFL
&&
277 save_sigcont
.sa_sigaction
!= (void *)SIG_IGN
)
278 (*save_sigcont
.sa_sigaction
) (signo
, the_siginfo
, data
);
282 sigwinch_handler (int signo
, void *the_siginfo
, void *data
)
284 int dims
= terminal_get_dimensions ();
286 cols_and_lines
= dims
;
288 // Call previous handler
289 if (save_sigwinch
.sa_sigaction
!= NULL
&&
290 save_sigwinch
.sa_sigaction
!= (void *)SIG_DFL
&&
291 save_sigwinch
.sa_sigaction
!= (void *)SIG_IGN
)
292 (*save_sigwinch
.sa_sigaction
) (signo
, the_siginfo
, data
);
296 console_set_signal_handlers ()
298 struct sigaction sigcont
, sigint
, sigwinch
;
300 memset (&sigcont
, 0, sizeof (struct sigaction
));
301 memset (&sigint
, 0, sizeof (struct sigaction
));
302 memset (&sigwinch
, 0, sizeof (struct sigaction
));
305 sigcont
.sa_handler
= (void *) sigcont_handler
;
306 sigcont
.sa_flags
= 0;
307 sigemptyset (&sigcont
.sa_mask
);
308 sigaction (SIGCONT
, &sigcont
, &save_sigcont
);
311 sigint
.sa_handler
= sigint_handler
;
313 sigemptyset (&sigint
.sa_mask
);
314 sigaction (SIGINT
, &sigint
, &save_sigint
);
316 // Window size changed
317 sigwinch
.sa_handler
= (void *) sigwinch_handler
;
318 sigwinch
.sa_flags
= 0;
319 sigemptyset (&sigwinch
.sa_mask
);
320 sigaction (SIGWINCH
, &sigwinch
, &save_sigwinch
);
324 // Currently unused, should we ever call the restore handler?
325 // Perhaps before calling into Process.Start?
328 console_restore_signal_handlers ()
330 sigaction (SIGCONT
, &save_sigcont
, NULL
);
331 sigaction (SIGINT
, &save_sigint
, NULL
);
332 sigaction (SIGWINCH
, &save_sigwinch
, NULL
);
336 set_control_chars (MonoArray
*control_chars
, const guchar
*cc
)
338 /* The index into the array comes from corlib/System/ControlCharacters.cs */
340 mono_array_set (control_chars
, gchar
, 0, cc
[VINTR
]);
343 mono_array_set (control_chars
, gchar
, 1, cc
[VQUIT
]);
346 mono_array_set (control_chars
, gchar
, 2, cc
[VERASE
]);
349 mono_array_set (control_chars
, gchar
, 3, cc
[VKILL
]);
352 mono_array_set (control_chars
, gchar
, 4, cc
[VEOF
]);
355 mono_array_set (control_chars
, gchar
, 5, cc
[VTIME
]);
358 mono_array_set (control_chars
, gchar
, 6, cc
[VMIN
]);
361 mono_array_set (control_chars
, gchar
, 7, cc
[VSWTC
]);
364 mono_array_set (control_chars
, gchar
, 8, cc
[VSTART
]);
367 mono_array_set (control_chars
, gchar
, 9, cc
[VSTOP
]);
370 mono_array_set (control_chars
, gchar
, 10, cc
[VSUSP
]);
373 mono_array_set (control_chars
, gchar
, 11, cc
[VEOL
]);
376 mono_array_set (control_chars
, gchar
, 12, cc
[VREPRINT
]);
379 mono_array_set (control_chars
, gchar
, 13, cc
[VDISCARD
]);
382 mono_array_set (control_chars
, gchar
, 14, cc
[VWERASE
]);
385 mono_array_set (control_chars
, gchar
, 15, cc
[VLNEXT
]);
388 mono_array_set (control_chars
, gchar
, 16, cc
[VEOL2
]);
393 ves_icall_System_ConsoleDriver_TtySetup (MonoString
*keypad
, MonoString
*teardown
, MonoArray
**control_chars
, int **size
)
399 dims
= terminal_get_dimensions ();
401 int cols
= 0, rows
= 0;
403 char *str
= getenv ("COLUMNS");
406 str
= getenv ("LINES");
410 if (cols
!= 0 && rows
!= 0)
411 cols_and_lines
= (cols
<< 16) | rows
;
415 cols_and_lines
= dims
;
418 *size
= &cols_and_lines
;
420 /* 17 is the number of entries set in set_control_chars() above.
421 * NCCS is the total size, but, by now, we only care about those 17 values*/
422 mono_gc_wbarrier_generic_store (control_chars
, (MonoObject
*) mono_array_new (mono_domain_get (), mono_defaults
.byte_class
, 17));
423 if (tcgetattr (STDIN_FILENO
, &initial_attr
) == -1)
426 mono_attr
= initial_attr
;
427 mono_attr
.c_lflag
&= ~(ICANON
);
428 mono_attr
.c_iflag
&= ~(IXON
|IXOFF
);
429 mono_attr
.c_cc
[VMIN
] = 1;
430 mono_attr
.c_cc
[VTIME
] = 0;
432 /* Disable C-y being used as a suspend character on OSX */
433 mono_attr
.c_cc
[VDSUSP
] = 255;
435 if (tcsetattr (STDIN_FILENO
, TCSANOW
, &mono_attr
) == -1)
438 set_control_chars (*control_chars
, mono_attr
.c_cc
);
439 /* If initialized from another appdomain... */
443 keypad_xmit_str
= keypad
!= NULL
? mono_string_to_utf8 (keypad
) : NULL
;
445 console_set_signal_handlers ();
446 setup_finished
= TRUE
;
447 if (!atexit_called
) {
448 if (teardown
!= NULL
)
449 teardown_str
= mono_string_to_utf8 (teardown
);
451 atexit (tty_teardown
);