3 * console-io.c: ConsoleDriver internal calls
6 * Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 * Copyright (C) 2005 Novell, Inc. (http://www.novell.com)
17 #ifdef HAVE_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>
30 /* On solaris, curses.h must come before both termios.h and term.h */
40 /* Needed for FIONREAD under solaris */
41 #ifdef HAVE_SYS_FILIO_H
42 #include <sys/filio.h>
44 #ifndef PLATFORM_WIN32
46 #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;
69 ves_icall_System_ConsoleDriver_Isatty (HANDLE handle)
73 return (GetFileType (handle) == FILE_TYPE_CHAR);
77 ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo)
83 ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break)
89 ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout)
95 ves_icall_System_ConsoleDriver_TtySetup (MonoString *keypad, MonoString *teardown, char *verase, char *vsusp, char *intr)
101 ves_icall_System_ConsoleDriver_GetTtySize (HANDLE handle, gint32 *width, gint32 *height)
107 static struct termios initial_attr;
110 ves_icall_System_ConsoleDriver_Isatty (HANDLE handle)
114 return isatty (GPOINTER_TO_INT (handle));
118 set_property (gint property, gboolean value)
121 gboolean callset = FALSE;
126 if (tcgetattr (STDIN_FILENO, &attr) == -1)
129 check = (attr.c_lflag & property) != 0;
130 if ((value || check) && !(value && check)) {
133 attr.c_lflag |= property;
135 attr.c_lflag &= ~property;
141 if (tcsetattr (STDIN_FILENO, TCSANOW, &attr) == -1)
149 ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo)
152 return set_property (ECHO, want_echo);
156 ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break)
158 return set_property (IGNBRK, !want_break);
162 ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout)
166 struct timeval *tvptr;
174 FD_SET (STDIN_FILENO, &rfds);
176 divvy = div (timeout, 1000);
177 tv.tv_sec = divvy.quot;
178 tv.tv_usec = divvy.rem;
183 ret = select (STDIN_FILENO + 1, &rfds, NULL, NULL, tvptr);
184 } while (ret == -1 && errno == EINTR);
188 ret = ioctl (STDIN_FILENO, FIONREAD, &nbytes);
193 return (ret > 0) ? ret : 0;
204 if (teardown_str != NULL) {
205 write (STDOUT_FILENO, teardown_str, strlen (teardown_str));
206 g_free (teardown_str);
210 tcflush (STDIN_FILENO, TCIFLUSH);
211 tcsetattr (STDIN_FILENO, TCSANOW, &initial_attr);
212 set_property (ECHO, TRUE);
213 setup_finished = FALSE;
217 do_console_cancel_event (void)
219 static MonoClassField *cancel_handler_field;
220 MonoDomain *domain = mono_domain_get ();
222 MonoDelegate *load_value;
224 MonoMethodMessage *msg;
230 klass = mono_class_from_name (mono_defaults.corlib, "System", "Console");
234 if (cancel_handler_field == NULL) {
235 cancel_handler_field = mono_class_get_field_from_name (klass, "cancel_handler");
236 g_assert (cancel_handler_field);
239 mono_field_static_get_value (mono_class_vtable (domain, klass), cancel_handler_field, &load_value);
240 if (load_value == NULL)
243 klass = load_value->object.vtable->klass;
244 method = mono_class_get_method_from_name (klass, "BeginInvoke", -1);
245 g_assert (method != NULL);
246 im = mono_get_delegate_invoke (method->klass);
247 msg = mono_method_call_message_new (method, NULL, im, NULL, NULL);
248 mono_thread_pool_add ((MonoObject *) load_value, msg, NULL, NULL);
251 static gboolean in_sigint;
253 sigint_handler (int signo)
261 do_console_cancel_event ();
266 sigcont_handler (int signo)
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));
275 static struct sigaction save_sigcont, save_sigint;
278 console_set_signal_handlers ()
280 struct sigaction sigcont, sigint;
283 sigcont.sa_handler = sigcont_handler;
284 sigcont.sa_flags = 0;
285 sigemptyset (&sigcont.sa_mask);
286 sigaction (SIGCONT, &sigcont, &save_sigcont);
289 sigint.sa_handler = sigint_handler;
291 sigemptyset (&sigint.sa_mask);
292 sigaction (SIGINT, &sigint, &save_sigint);
296 // Currently unused, should we ever call the restore handler?
297 // Perhaps before calling into Process.Start?
300 console_restore_signal_handlers ()
302 sigaction (SIGCONT, &save_sigcont, NULL);
303 sigaction (SIGINT, &save_sigint, NULL);
307 ves_icall_System_ConsoleDriver_TtySetup (MonoString *keypad, MonoString *teardown, char *verase, char *vsusp, char*intr)
314 if (tcgetattr (STDIN_FILENO, &initial_attr) == -1)
317 mono_attr = initial_attr;
318 mono_attr.c_lflag &= ~ICANON;
319 mono_attr.c_cc [VMIN] = 1;
320 mono_attr.c_cc [VTIME] = 0;
321 if (tcsetattr (STDIN_FILENO, TCSANOW, &mono_attr) == -1)
324 *verase = initial_attr.c_cc [VERASE];
325 *vsusp = initial_attr.c_cc [VSUSP];
326 *intr = initial_attr.c_cc [VINTR];
327 /* If initialized from another appdomain... */
331 keypad_xmit_str = keypad != NULL ? mono_string_to_utf8 (keypad) : NULL;
333 console_set_signal_handlers ();
334 setup_finished = TRUE;
335 if (!atexit_called) {
336 if (teardown != NULL)
337 teardown_str = mono_string_to_utf8 (teardown);
339 atexit (tty_teardown);
346 ves_icall_System_ConsoleDriver_GetTtySize (HANDLE handle, gint32 *width, gint32 *height)
354 res = ioctl (GPOINTER_TO_INT (handle), TIOCGWINSZ, &ws);
368 #endif /* !PLATFORM_WIN32 */