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>
39 /* On solaris, curses.h must come before both termios.h and term.h */
50 /* Needed for FIONREAD under solaris */
51 #ifdef HAVE_SYS_FILIO_H
52 # include <sys/filio.h>
54 #ifdef HAVE_SYS_IOCTL_H
55 # include <sys/ioctl.h>
58 #include <mono/metadata/console-io.h>
59 #include <mono/metadata/exception.h>
61 static gboolean setup_finished
;
62 static gboolean atexit_called
;
64 /* The string used to return the terminal to its previous state */
65 static gchar
*teardown_str
;
67 /* The string used to set the terminal into keypad xmit mode after SIGCONT is received */
68 static gchar
*keypad_xmit_str
;
71 /* This is the last state used by Mono, used after a CONT signal is received */
72 static struct termios mono_attr
;
75 /* static void console_restore_signal_handlers (void); */
76 static void console_set_signal_handlers (void);
79 mono_console_init (void)
83 /* Make sure the standard file descriptors are opened */
84 fd
= open ("/dev/null", O_RDWR
);
85 while (fd
>= 0 && fd
< 3) {
86 fd
= open ("/dev/null", O_RDWR
);
91 static struct termios initial_attr
;
94 ves_icall_System_ConsoleDriver_Isatty (HANDLE handle
)
96 return isatty (GPOINTER_TO_INT (handle
));
100 set_property (gint property
, gboolean value
)
103 gboolean callset
= FALSE
;
106 if (tcgetattr (STDIN_FILENO
, &attr
) == -1)
109 check
= (attr
.c_lflag
& property
) != 0;
110 if ((value
|| check
) && !(value
&& check
)) {
113 attr
.c_lflag
|= property
;
115 attr
.c_lflag
&= ~property
;
121 if (tcsetattr (STDIN_FILENO
, TCSANOW
, &attr
) == -1)
129 ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo
)
132 return set_property (ECHO
, want_echo
);
136 ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break
)
138 return set_property (IGNBRK
, !want_break
);
142 ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout
)
146 struct timeval
*tvptr
;
152 FD_SET (STDIN_FILENO
, &rfds
);
154 divvy
= div (timeout
, 1000);
155 tv
.tv_sec
= divvy
.quot
;
156 tv
.tv_usec
= divvy
.rem
;
161 ret
= select (STDIN_FILENO
+ 1, &rfds
, NULL
, NULL
, tvptr
);
162 } while (ret
== -1 && errno
== EINTR
);
166 ret
= ioctl (STDIN_FILENO
, FIONREAD
, &nbytes
);
171 return (ret
> 0) ? ret
: 0;
174 static gint32 cols_and_lines
;
178 terminal_get_dimensions (void)
182 int save_errno
= errno
;
184 if (ioctl (STDIN_FILENO
, TIOCGWINSZ
, &ws
) == 0){
185 ret
= (ws
.ws_col
<< 16) | ws
.ws_row
;
193 terminal_get_dimensions (void)
202 int unused G_GNUC_UNUSED
;
207 if (teardown_str
!= NULL
) {
208 unused
= write (STDOUT_FILENO
, teardown_str
, strlen (teardown_str
));
209 g_free (teardown_str
);
213 tcflush (STDIN_FILENO
, TCIFLUSH
);
214 tcsetattr (STDIN_FILENO
, TCSANOW
, &initial_attr
);
215 set_property (ECHO
, TRUE
);
216 setup_finished
= FALSE
;
220 do_console_cancel_event (void)
222 static MonoClassField
*cancel_handler_field
;
224 MonoDomain
*domain
= mono_domain_get ();
226 MonoDelegate
*load_value
;
230 /* FIXME: this should likely iterate all the domains, instead */
234 klass
= mono_class_try_load_from_name (mono_defaults
.corlib
, "System", "Console");
238 if (cancel_handler_field
== NULL
) {
239 cancel_handler_field
= mono_class_get_field_from_name (klass
, "cancel_handler");
240 g_assert (cancel_handler_field
);
243 vtable
= mono_class_vtable_full (domain
, klass
, &error
);
244 if (vtable
== NULL
|| !is_ok (&error
)) {
245 mono_error_cleanup (&error
);
248 mono_field_static_get_value_checked (vtable
, cancel_handler_field
, &load_value
, &error
);
249 if (load_value
== NULL
|| !is_ok (&error
)) {
250 mono_error_cleanup (&error
);
254 klass
= load_value
->object
.vtable
->klass
;
255 method
= mono_class_get_method_from_name (klass
, "BeginInvoke", -1);
256 g_assert (method
!= NULL
);
258 mono_threadpool_begin_invoke (domain
, (MonoObject
*) load_value
, method
, NULL
, &error
);
259 if (!is_ok (&error
)) {
260 g_warning ("Couldn't invoke System.Console cancel handler due to %s", mono_error_get_message (&error
));
261 mono_error_cleanup (&error
);
265 static int need_cancel
= FALSE
;
266 /* this is executed from the finalizer thread */
268 mono_console_handle_async_ops (void)
272 do_console_cancel_event ();
276 static gboolean in_sigint
;
278 MONO_SIG_HANDLER_FUNC (static, sigint_handler
)
288 mono_gc_finalize_notify ();
293 static struct sigaction save_sigcont
, save_sigint
, save_sigwinch
;
295 MONO_SIG_HANDLER_FUNC (static, sigcont_handler
)
297 int unused G_GNUC_UNUSED
;
298 // Ignore error, there is not much we can do in the sigcont handler.
299 tcsetattr (STDIN_FILENO
, TCSANOW
, &mono_attr
);
301 if (keypad_xmit_str
!= NULL
)
302 unused
= write (STDOUT_FILENO
, keypad_xmit_str
, strlen (keypad_xmit_str
));
304 // Call previous handler
305 if (save_sigcont
.sa_sigaction
!= NULL
&&
306 save_sigcont
.sa_sigaction
!= (void *)SIG_DFL
&&
307 save_sigcont
.sa_sigaction
!= (void *)SIG_IGN
)
308 (*save_sigcont
.sa_sigaction
) (MONO_SIG_HANDLER_PARAMS
);
311 MONO_SIG_HANDLER_FUNC (static, sigwinch_handler
)
313 int dims
= terminal_get_dimensions ();
315 cols_and_lines
= dims
;
317 // Call previous handler
318 if (save_sigwinch
.sa_sigaction
!= NULL
&&
319 save_sigwinch
.sa_sigaction
!= (void *)SIG_DFL
&&
320 save_sigwinch
.sa_sigaction
!= (void *)SIG_IGN
)
321 (*save_sigwinch
.sa_sigaction
) (MONO_SIG_HANDLER_PARAMS
);
325 * console_set_signal_handlers:
327 * Installs various signals handlers for the use of the console, as
330 * SIGCONT: this is received after the application has resumed execution
331 * if it was suspended with Control-Z before. This signal handler needs
332 * to resend the terminal sequence to send keyboard in keypad mode (this
333 * is the difference between getting a cuu1 code or a kcuu1 code for up-arrow
336 * SIGINT: invokes the System.Console.DoConsoleCancelEvent method using
337 * a thread from the thread pool which notifies all registered cancel_event
340 * SIGWINCH: is used to track changes to the console window when a GUI
341 * terminal is resized. It sets an internal variable that is checked
342 * by System.Console when the Terminfo driver has been activated.
345 console_set_signal_handlers ()
347 #if defined(HAVE_SIGACTION)
348 struct sigaction sigcont
, sigint
, sigwinch
;
350 memset (&sigcont
, 0, sizeof (struct sigaction
));
351 memset (&sigint
, 0, sizeof (struct sigaction
));
352 memset (&sigwinch
, 0, sizeof (struct sigaction
));
355 sigcont
.sa_handler
= (void (*)(int)) sigcont_handler
;
356 sigcont
.sa_flags
= SA_RESTART
;
357 sigemptyset (&sigcont
.sa_mask
);
358 sigaction (SIGCONT
, &sigcont
, &save_sigcont
);
361 sigint
.sa_handler
= (void (*)(int)) sigint_handler
;
362 sigint
.sa_flags
= SA_RESTART
;
363 sigemptyset (&sigint
.sa_mask
);
364 sigaction (SIGINT
, &sigint
, &save_sigint
);
366 // Window size changed
367 sigwinch
.sa_handler
= (void (*)(int)) sigwinch_handler
;
368 sigwinch
.sa_flags
= SA_RESTART
;
369 sigemptyset (&sigwinch
.sa_mask
);
370 sigaction (SIGWINCH
, &sigwinch
, &save_sigwinch
);
374 #if currently_unuused
376 // Currently unused, should we ever call the restore handler?
377 // Perhaps before calling into Process.Start?
380 console_restore_signal_handlers ()
382 sigaction (SIGCONT
, &save_sigcont
, NULL
);
383 sigaction (SIGINT
, &save_sigint
, NULL
);
384 sigaction (SIGWINCH
, &save_sigwinch
, NULL
);
389 set_control_chars (MonoArray
*control_chars
, const guchar
*cc
)
391 /* The index into the array comes from corlib/System/ControlCharacters.cs */
393 mono_array_set (control_chars
, gchar
, 0, cc
[VINTR
]);
396 mono_array_set (control_chars
, gchar
, 1, cc
[VQUIT
]);
399 mono_array_set (control_chars
, gchar
, 2, cc
[VERASE
]);
402 mono_array_set (control_chars
, gchar
, 3, cc
[VKILL
]);
405 mono_array_set (control_chars
, gchar
, 4, cc
[VEOF
]);
408 mono_array_set (control_chars
, gchar
, 5, cc
[VTIME
]);
411 mono_array_set (control_chars
, gchar
, 6, cc
[VMIN
]);
414 mono_array_set (control_chars
, gchar
, 7, cc
[VSWTC
]);
417 mono_array_set (control_chars
, gchar
, 8, cc
[VSTART
]);
420 mono_array_set (control_chars
, gchar
, 9, cc
[VSTOP
]);
423 mono_array_set (control_chars
, gchar
, 10, cc
[VSUSP
]);
426 mono_array_set (control_chars
, gchar
, 11, cc
[VEOL
]);
429 mono_array_set (control_chars
, gchar
, 12, cc
[VREPRINT
]);
432 mono_array_set (control_chars
, gchar
, 13, cc
[VDISCARD
]);
435 mono_array_set (control_chars
, gchar
, 14, cc
[VWERASE
]);
438 mono_array_set (control_chars
, gchar
, 15, cc
[VLNEXT
]);
441 mono_array_set (control_chars
, gchar
, 16, cc
[VEOL2
]);
446 ves_icall_System_ConsoleDriver_TtySetup (MonoString
*keypad
, MonoString
*teardown
, MonoArray
**control_chars
, int **size
)
452 dims
= terminal_get_dimensions ();
454 int cols
= 0, rows
= 0;
456 char *str
= g_getenv ("COLUMNS");
461 str
= g_getenv ("LINES");
467 if (cols
!= 0 && rows
!= 0)
468 cols_and_lines
= (cols
<< 16) | rows
;
472 cols_and_lines
= dims
;
475 *size
= &cols_and_lines
;
477 /* 17 is the number of entries set in set_control_chars() above.
478 * NCCS is the total size, but, by now, we only care about those 17 values*/
479 MonoArray
*control_chars_arr
= mono_array_new_checked (mono_domain_get (), mono_defaults
.byte_class
, 17, &error
);
480 if (mono_error_set_pending_exception (&error
))
482 mono_gc_wbarrier_generic_store (control_chars
, (MonoObject
*) control_chars_arr
);
483 if (tcgetattr (STDIN_FILENO
, &initial_attr
) == -1)
486 mono_attr
= initial_attr
;
487 mono_attr
.c_lflag
&= ~(ICANON
);
488 mono_attr
.c_iflag
&= ~(IXON
|IXOFF
);
489 mono_attr
.c_cc
[VMIN
] = 1;
490 mono_attr
.c_cc
[VTIME
] = 0;
492 /* Disable C-y being used as a suspend character on OSX */
493 mono_attr
.c_cc
[VDSUSP
] = 255;
495 if (tcsetattr (STDIN_FILENO
, TCSANOW
, &mono_attr
) == -1)
498 set_control_chars (*control_chars
, mono_attr
.c_cc
);
499 /* If initialized from another appdomain... */
503 keypad_xmit_str
= NULL
;
504 if (keypad
!= NULL
) {
505 keypad_xmit_str
= mono_string_to_utf8_checked (keypad
, &error
);
506 if (mono_error_set_pending_exception (&error
))
510 console_set_signal_handlers ();
511 setup_finished
= TRUE
;
512 if (!atexit_called
) {
513 if (teardown
!= NULL
) {
514 teardown_str
= mono_string_to_utf8_checked (teardown
, &error
);
515 if (mono_error_set_pending_exception (&error
))
519 mono_atexit (tty_teardown
);