2 * console-io.c: ConsoleDriver internal calls
5 * Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 * Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com)
16 #ifdef HAVE_SYS_TIME_H
19 #include <sys/types.h>
23 #include <mono/metadata/appdomain.h>
24 #include <mono/metadata/object-internals.h>
25 #include <mono/metadata/class-internals.h>
26 #include <mono/metadata/domain-internals.h>
27 #include <mono/metadata/metadata.h>
28 #include <mono/metadata/threadpool.h>
29 /* On solaris, curses.h must come before both termios.h and term.h */
39 /* Needed for FIONREAD under solaris */
40 #ifdef HAVE_SYS_FILIO_H
41 #include <sys/filio.h>
44 #ifdef HAVE_SYS_IOCTL_H
45 #include <sys/ioctl.h>
49 #include <mono/metadata/console-io.h>
50 #include <mono/metadata/exception.h>
52 static gboolean setup_finished
;
53 static gboolean atexit_called
;
55 /* The string used to return the terminal to its previous state */
56 static gchar
*teardown_str
;
58 /* The string used to set the terminal into keypad xmit mode after SIGCONT is received */
59 static gchar
*keypad_xmit_str
;
62 /* This is the last state used by Mono, used after a CONT signal is received */
63 static struct termios mono_attr
;
66 #if defined (PLATFORM_WIN32) || defined (MONO_NULL_TTYDRIVER)
68 ves_icall_System_ConsoleDriver_Isatty (HANDLE handle
)
72 return (GetFileType (handle
) == FILE_TYPE_CHAR
);
76 ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo
)
82 ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break
)
88 ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout
)
94 ves_icall_System_ConsoleDriver_TtySetup (MonoString
*keypad
, MonoString
*teardown
, char *verase
, char *vsusp
, char*intr
, int **size
)
100 static struct termios initial_attr
;
103 ves_icall_System_ConsoleDriver_Isatty (HANDLE handle
)
107 return isatty (GPOINTER_TO_INT (handle
));
111 set_property (gint property
, gboolean value
)
114 gboolean callset
= FALSE
;
119 if (tcgetattr (STDIN_FILENO
, &attr
) == -1)
122 check
= (attr
.c_lflag
& property
) != 0;
123 if ((value
|| check
) && !(value
&& check
)) {
126 attr
.c_lflag
|= property
;
128 attr
.c_lflag
&= ~property
;
134 if (tcsetattr (STDIN_FILENO
, TCSANOW
, &attr
) == -1)
142 ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo
)
145 return set_property (ECHO
, want_echo
);
149 ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break
)
151 return set_property (IGNBRK
, !want_break
);
155 ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout
)
159 struct timeval
*tvptr
;
167 FD_SET (STDIN_FILENO
, &rfds
);
169 divvy
= div (timeout
, 1000);
170 tv
.tv_sec
= divvy
.quot
;
171 tv
.tv_usec
= divvy
.rem
;
176 ret
= select (STDIN_FILENO
+ 1, &rfds
, NULL
, NULL
, tvptr
);
177 } while (ret
== -1 && errno
== EINTR
);
181 ret
= ioctl (STDIN_FILENO
, FIONREAD
, &nbytes
);
186 return (ret
> 0) ? ret
: 0;
189 static gint32 cols_and_lines
;
193 terminal_get_dimensions (void)
197 if (ioctl (STDIN_FILENO
, TIOCGWINSZ
, &ws
) == 0)
198 return (ws
.ws_col
<< 16) | ws
.ws_row
;
204 terminal_get_dimensions (void)
218 if (teardown_str
!= NULL
) {
219 write (STDOUT_FILENO
, teardown_str
, strlen (teardown_str
));
220 g_free (teardown_str
);
224 tcflush (STDIN_FILENO
, TCIFLUSH
);
225 tcsetattr (STDIN_FILENO
, TCSANOW
, &initial_attr
);
226 set_property (ECHO
, TRUE
);
227 setup_finished
= FALSE
;
231 do_console_cancel_event (void)
233 static MonoClassField
*cancel_handler_field
;
234 MonoDomain
*domain
= mono_domain_get ();
236 MonoDelegate
*load_value
;
238 MonoMethodMessage
*msg
;
244 klass
= mono_class_from_name (mono_defaults
.corlib
, "System", "Console");
248 if (cancel_handler_field
== NULL
) {
249 cancel_handler_field
= mono_class_get_field_from_name (klass
, "cancel_handler");
250 g_assert (cancel_handler_field
);
253 mono_field_static_get_value (mono_class_vtable (domain
, klass
), cancel_handler_field
, &load_value
);
254 if (load_value
== NULL
)
257 klass
= load_value
->object
.vtable
->klass
;
258 method
= mono_class_get_method_from_name (klass
, "BeginInvoke", -1);
259 g_assert (method
!= NULL
);
260 im
= mono_get_delegate_invoke (method
->klass
);
261 msg
= mono_method_call_message_new (method
, NULL
, im
, NULL
, NULL
);
262 mono_thread_pool_add ((MonoObject
*) load_value
, msg
, NULL
, NULL
);
265 static gboolean in_sigint
;
267 sigint_handler (int signo
)
275 do_console_cancel_event ();
279 static struct sigaction save_sigcont
, save_sigint
, save_sigwinch
;
282 sigcont_handler (int signo
, void *the_siginfo
, void *data
)
284 // Ignore error, there is not much we can do in the sigcont handler.
285 tcsetattr (STDIN_FILENO
, TCSANOW
, &mono_attr
);
287 if (keypad_xmit_str
!= NULL
)
288 write (STDOUT_FILENO
, keypad_xmit_str
, strlen (keypad_xmit_str
));
290 // Call previous handler
291 if (save_sigcont
.sa_sigaction
!= NULL
&&
292 save_sigcont
.sa_sigaction
!= (void *)SIG_DFL
&&
293 save_sigcont
.sa_sigaction
!= (void *)SIG_IGN
)
294 (*save_sigcont
.sa_sigaction
) (signo
, the_siginfo
, data
);
298 sigwinch_handler (int signo
, void *the_siginfo
, void *data
)
300 int dims
= terminal_get_dimensions ();
302 cols_and_lines
= dims
;
304 // Call previous handler
305 if (save_sigwinch
.sa_sigaction
!= NULL
&&
306 save_sigwinch
.sa_sigaction
!= (void *)SIG_DFL
&&
307 save_sigwinch
.sa_sigaction
!= (void *)SIG_IGN
)
308 (*save_sigwinch
.sa_sigaction
) (signo
, the_siginfo
, data
);
312 console_set_signal_handlers ()
314 struct sigaction sigcont
, sigint
, sigwinch
;
316 memset (&sigcont
, 0, sizeof (struct sigaction
));
317 memset (&sigint
, 0, sizeof (struct sigaction
));
318 memset (&sigwinch
, 0, sizeof (struct sigaction
));
321 sigcont
.sa_handler
= (void *) sigcont_handler
;
322 sigcont
.sa_flags
= 0;
323 sigemptyset (&sigcont
.sa_mask
);
324 sigaction (SIGCONT
, &sigcont
, &save_sigcont
);
327 sigint
.sa_handler
= sigint_handler
;
329 sigemptyset (&sigint
.sa_mask
);
330 sigaction (SIGINT
, &sigint
, &save_sigint
);
332 // Window size changed
333 sigwinch
.sa_handler
= (void *) sigwinch_handler
;
334 sigwinch
.sa_flags
= 0;
335 sigemptyset (&sigwinch
.sa_mask
);
336 sigaction (SIGWINCH
, &sigwinch
, &save_sigwinch
);
340 // Currently unused, should we ever call the restore handler?
341 // Perhaps before calling into Process.Start?
344 console_restore_signal_handlers ()
346 sigaction (SIGCONT
, &save_sigcont
, NULL
);
347 sigaction (SIGINT
, &save_sigint
, NULL
);
348 sigaction (SIGWINCH
, &save_sigwinch
, NULL
);
352 ves_icall_System_ConsoleDriver_TtySetup (MonoString
*keypad
, MonoString
*teardown
, char *verase
, char *vsusp
, char*intr
, int **size
)
358 dims
= terminal_get_dimensions ();
360 int cols
= 0, rows
= 0;
362 char *str
= getenv ("COLUMNS");
365 str
= getenv ("LINES");
369 if (cols
!= 0 && rows
!= 0)
370 cols_and_lines
= (cols
<< 16) | rows
;
374 cols_and_lines
= dims
;
377 *size
= &cols_and_lines
;
382 if (tcgetattr (STDIN_FILENO
, &initial_attr
) == -1)
385 mono_attr
= initial_attr
;
386 mono_attr
.c_lflag
&= ~(ICANON
);
387 mono_attr
.c_iflag
&= ~(IXON
|IXOFF
);
388 mono_attr
.c_cc
[VMIN
] = 1;
389 mono_attr
.c_cc
[VTIME
] = 0;
390 if (tcsetattr (STDIN_FILENO
, TCSANOW
, &mono_attr
) == -1)
393 *verase
= initial_attr
.c_cc
[VERASE
];
394 *vsusp
= initial_attr
.c_cc
[VSUSP
];
395 *intr
= initial_attr
.c_cc
[VINTR
];
396 /* If initialized from another appdomain... */
400 keypad_xmit_str
= keypad
!= NULL
? mono_string_to_utf8 (keypad
) : NULL
;
402 console_set_signal_handlers ();
403 setup_finished
= TRUE
;
404 if (!atexit_called
) {
405 if (teardown
!= NULL
)
406 teardown_str
= mono_string_to_utf8 (teardown
);
408 atexit (tty_teardown
);
414 #endif /* !PLATFORM_WIN32 */