3 * ConsoleDriver internal calls for Unix systems.
6 * Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 * Copyright (C) 2005-2009 Novell, Inc. (http://www.novell.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
18 #ifdef HAVE_SYS_SELECT_H
19 # include <sys/select.h>
21 #ifdef HAVE_SYS_TIME_H
22 # include <sys/time.h>
24 #include <sys/types.h>
28 #include <mono/metadata/appdomain.h>
29 #include <mono/metadata/object-internals.h>
30 #include <mono/metadata/class-internals.h>
31 #include <mono/metadata/domain-internals.h>
32 #include <mono/metadata/gc-internals.h>
33 #include <mono/metadata/metadata.h>
34 #include <mono/metadata/threadpool.h>
35 #include <mono/utils/mono-signal-handler.h>
36 #include <mono/utils/mono-proclib.h>
37 #include <mono/utils/w32api.h>
38 #include <mono/utils/mono-errno.h>
39 #include <mono/metadata/console-io.h>
40 #include <mono/metadata/exception.h>
41 #include "icall-decl.h"
43 #ifndef ENABLE_NETCORE
45 /* On solaris, curses.h must come before both termios.h and term.h */
56 /* Needed for FIONREAD under solaris */
57 #ifdef HAVE_SYS_FILIO_H
58 # include <sys/filio.h>
60 #ifdef HAVE_SYS_IOCTL_H
61 # include <sys/ioctl.h>
64 static gboolean setup_finished
;
65 static gboolean atexit_called
;
67 /* The string used to return the terminal to its previous state */
68 static gchar
*teardown_str
;
70 /* The string used to set the terminal into keypad xmit mode after SIGCONT is received */
71 static gchar
*keypad_xmit_str
;
74 /* This is the last state used by Mono, used after a CONT signal is received */
75 static struct termios mono_attr
;
78 /* static void console_restore_signal_handlers (void); */
79 static void console_set_signal_handlers (void);
81 static GENERATE_TRY_GET_CLASS_WITH_CACHE (console
, "System", "Console");
84 mono_console_init (void)
88 /* Make sure the standard file descriptors are opened */
89 fd
= open ("/dev/null", O_RDWR
);
90 while (fd
>= 0 && fd
< 3) {
91 fd
= open ("/dev/null", O_RDWR
);
96 static struct termios initial_attr
;
99 ves_icall_System_ConsoleDriver_Isatty (HANDLE handle
, MonoError
* error
)
101 return isatty (GPOINTER_TO_INT (handle
));
105 set_property (gint property
, gboolean value
)
108 gboolean callset
= FALSE
;
111 if (tcgetattr (STDIN_FILENO
, &attr
) == -1)
114 check
= (attr
.c_lflag
& property
) != 0;
115 if ((value
|| check
) && !(value
&& check
)) {
118 attr
.c_lflag
|= property
;
120 attr
.c_lflag
&= ~property
;
126 if (tcsetattr (STDIN_FILENO
, TCSANOW
, &attr
) == -1)
134 ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo
, MonoError
* error
)
136 return set_property (ECHO
, want_echo
);
140 ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break
, MonoError
* error
)
142 return set_property (IGNBRK
, !want_break
);
146 ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout
, MonoError
* error
)
150 struct timeval
*tvptr
;
156 FD_SET (STDIN_FILENO
, &rfds
);
158 divvy
= div (timeout
, 1000);
159 tv
.tv_sec
= divvy
.quot
;
160 tv
.tv_usec
= divvy
.rem
;
165 ret
= select (STDIN_FILENO
+ 1, &rfds
, NULL
, NULL
, tvptr
);
166 } while (ret
== -1 && errno
== EINTR
);
170 ret
= ioctl (STDIN_FILENO
, FIONREAD
, &nbytes
);
175 return (ret
> 0) ? ret
: 0;
178 static gint32 cols_and_lines
;
182 terminal_get_dimensions (void)
186 int save_errno
= errno
;
188 if (ioctl (STDIN_FILENO
, TIOCGWINSZ
, &ws
) == 0){
189 ret
= (ws
.ws_col
<< 16) | ws
.ws_row
;
190 mono_set_errno (save_errno
);
197 terminal_get_dimensions (void)
206 int unused G_GNUC_UNUSED
;
211 if (teardown_str
!= NULL
) {
212 unused
= write (STDOUT_FILENO
, teardown_str
, strlen (teardown_str
));
213 g_free (teardown_str
);
217 tcflush (STDIN_FILENO
, TCIFLUSH
);
218 tcsetattr (STDIN_FILENO
, TCSANOW
, &initial_attr
);
219 set_property (ECHO
, TRUE
);
220 setup_finished
= FALSE
;
224 do_console_cancel_event (void)
226 static MonoMethod
*System_Console_DoConsoleCancelEventBackground_method
= (MonoMethod
*)(intptr_t)-1;
229 if (mono_class_try_get_console_class () == NULL
)
232 if (System_Console_DoConsoleCancelEventBackground_method
== (gpointer
)(intptr_t)-1) {
233 System_Console_DoConsoleCancelEventBackground_method
= mono_class_get_method_from_name_checked (mono_class_try_get_console_class (), "DoConsoleCancelEventInBackground", 0, 0, error
);
234 mono_error_assert_ok (error
);
236 if (System_Console_DoConsoleCancelEventBackground_method
== NULL
)
239 mono_runtime_invoke_checked (System_Console_DoConsoleCancelEventBackground_method
, NULL
, NULL
, error
);
240 mono_error_assert_ok (error
);
243 static int need_cancel
= FALSE
;
244 /* this is executed from the finalizer thread */
246 mono_console_handle_async_ops (void)
250 do_console_cancel_event ();
254 static gboolean in_sigint
;
256 MONO_SIG_HANDLER_FUNC (static, sigint_handler
)
266 mono_gc_finalize_notify ();
267 mono_set_errno (save_errno
);
271 static struct sigaction save_sigcont
, save_sigwinch
;
274 static struct sigaction save_sigint
;
277 MONO_SIG_HANDLER_FUNC (static, sigcont_handler
)
279 int unused G_GNUC_UNUSED
;
280 // Ignore error, there is not much we can do in the sigcont handler.
281 tcsetattr (STDIN_FILENO
, TCSANOW
, &mono_attr
);
283 if (keypad_xmit_str
!= NULL
)
284 unused
= write (STDOUT_FILENO
, keypad_xmit_str
, strlen (keypad_xmit_str
));
286 // Call previous handler
287 if (save_sigcont
.sa_sigaction
!= NULL
&&
288 save_sigcont
.sa_sigaction
!= (void *)SIG_DFL
&&
289 save_sigcont
.sa_sigaction
!= (void *)SIG_IGN
)
290 (*save_sigcont
.sa_sigaction
) (MONO_SIG_HANDLER_PARAMS
);
293 MONO_SIG_HANDLER_FUNC (static, sigwinch_handler
)
295 int dims
= terminal_get_dimensions ();
297 cols_and_lines
= dims
;
299 // Call previous handler
300 if (save_sigwinch
.sa_sigaction
!= NULL
&&
301 save_sigwinch
.sa_sigaction
!= (void *)SIG_DFL
&&
302 save_sigwinch
.sa_sigaction
!= (void *)SIG_IGN
)
303 (*save_sigwinch
.sa_sigaction
) (MONO_SIG_HANDLER_PARAMS
);
307 * console_set_signal_handlers:
309 * Installs various signals handlers for the use of the console, as
312 * SIGCONT: this is received after the application has resumed execution
313 * if it was suspended with Control-Z before. This signal handler needs
314 * to resend the terminal sequence to send keyboard in keypad mode (this
315 * is the difference between getting a cuu1 code or a kcuu1 code for up-arrow
318 * SIGINT: invokes the System.Console.DoConsoleCancelEvent method using
319 * a thread from the thread pool which notifies all registered cancel_event
322 * SIGWINCH: is used to track changes to the console window when a GUI
323 * terminal is resized. It sets an internal variable that is checked
324 * by System.Console when the Terminfo driver has been activated.
327 console_set_signal_handlers ()
329 #if defined(HAVE_SIGACTION)
330 struct sigaction sigcont
, sigint
, sigwinch
;
332 memset (&sigcont
, 0, sizeof (struct sigaction
));
333 memset (&sigint
, 0, sizeof (struct sigaction
));
334 memset (&sigwinch
, 0, sizeof (struct sigaction
));
337 sigcont
.sa_handler
= (void (*)(int)) sigcont_handler
;
338 sigcont
.sa_flags
= SA_RESTART
;
339 sigemptyset (&sigcont
.sa_mask
);
340 sigaction (SIGCONT
, &sigcont
, &save_sigcont
);
343 sigint
.sa_handler
= (void (*)(int)) sigint_handler
;
344 sigint
.sa_flags
= SA_RESTART
;
345 sigemptyset (&sigint
.sa_mask
);
346 sigaction (SIGINT
, &sigint
, &save_sigint
);
348 // Window size changed
349 sigwinch
.sa_handler
= (void (*)(int)) sigwinch_handler
;
350 sigwinch
.sa_flags
= SA_RESTART
;
351 sigemptyset (&sigwinch
.sa_mask
);
352 sigaction (SIGWINCH
, &sigwinch
, &save_sigwinch
);
356 #if currently_unuused
358 // Currently unused, should we ever call the restore handler?
359 // Perhaps before calling into Process.Start?
362 console_restore_signal_handlers ()
364 sigaction (SIGCONT
, &save_sigcont
, NULL
);
365 sigaction (SIGINT
, &save_sigint
, NULL
);
366 sigaction (SIGWINCH
, &save_sigwinch
, NULL
);
371 set_control_chars (gchar
*control_chars
, const guchar
*cc
)
373 /* The index into the array comes from corlib/System/ControlCharacters.cs */
375 control_chars
[0] = cc
[VINTR
];
378 control_chars
[1] = cc
[VQUIT
];
381 control_chars
[2] = cc
[VERASE
];
384 control_chars
[3] = cc
[VKILL
];
387 control_chars
[4] = cc
[VEOF
];
390 control_chars
[5] = cc
[VTIME
];
393 control_chars
[6] = cc
[VMIN
];
396 control_chars
[7] = cc
[VSWTC
];
399 control_chars
[8] = cc
[VSTART
];
402 control_chars
[9] = cc
[VSTOP
];
405 control_chars
[10] = cc
[VSUSP
];
408 control_chars
[11] = cc
[VEOL
];
411 control_chars
[12] = cc
[VREPRINT
];
414 control_chars
[13] = cc
[VDISCARD
];
417 control_chars
[14] = cc
[VWERASE
];
420 control_chars
[15] = cc
[VLNEXT
];
423 control_chars
[16] = cc
[VEOL2
];
428 ves_icall_System_ConsoleDriver_TtySetup (MonoStringHandle keypad
, MonoStringHandle teardown
, MonoArrayHandleOut control_chars
, int **size
, MonoError
* error
)
430 // FIXME Lock around the globals?
434 dims
= terminal_get_dimensions ();
436 int cols
= 0, rows
= 0;
438 char *str
= g_getenv ("COLUMNS");
443 str
= g_getenv ("LINES");
449 if (cols
!= 0 && rows
!= 0)
450 cols_and_lines
= (cols
<< 16) | rows
;
454 cols_and_lines
= dims
;
457 *size
= &cols_and_lines
;
459 /* 17 is the number of entries set in set_control_chars() above.
460 * NCCS is the total size, but, by now, we only care about those 17 values*/
461 MonoArrayHandle control_chars_arr
= mono_array_new_handle (mono_domain_get (), mono_defaults
.byte_class
, 17, error
);
462 return_val_if_nok (error
, FALSE
);
464 MONO_HANDLE_ASSIGN (control_chars
, control_chars_arr
);
465 if (tcgetattr (STDIN_FILENO
, &initial_attr
) == -1)
468 mono_attr
= initial_attr
;
469 mono_attr
.c_lflag
&= ~(ICANON
);
470 mono_attr
.c_iflag
&= ~(IXON
|IXOFF
);
471 mono_attr
.c_cc
[VMIN
] = 1;
472 mono_attr
.c_cc
[VTIME
] = 0;
474 /* Disable C-y being used as a suspend character on OSX */
475 mono_attr
.c_cc
[VDSUSP
] = 255;
480 ret
= tcsetattr (STDIN_FILENO
, TCSANOW
, &mono_attr
);
482 } while (ret
== -1 && errno
== EINTR
);
488 set_control_chars (MONO_ARRAY_HANDLE_PIN (control_chars_arr
, gchar
, 0, &h
), mono_attr
.c_cc
);
489 mono_gchandle_free_internal (h
);
490 /* If initialized from another appdomain... */
494 keypad_xmit_str
= NULL
;
495 if (!MONO_HANDLE_IS_NULL (keypad
)) {
496 keypad_xmit_str
= mono_string_handle_to_utf8 (keypad
, error
);
497 return_val_if_nok (error
, FALSE
);
500 console_set_signal_handlers ();
501 setup_finished
= TRUE
;
502 if (!atexit_called
) {
503 if (!MONO_HANDLE_IS_NULL (teardown
)) {
504 teardown_str
= mono_string_handle_to_utf8 (teardown
, error
);
505 return_val_if_nok (error
, FALSE
);
508 mono_atexit (tty_teardown
);
514 #else /* ENABLE_NETCORE */
517 mono_console_init (void)
522 mono_console_handle_async_ops (void)