[csproj] Fix diff rendering when XML comments appear in the diff (#8642)
[mono-project.git] / mono / metadata / console-unix.c
blob61d2b152f8dcaecc3aad1cc774f2fa327ccbbc2e
1 /**
2 * \file
3 * ConsoleDriver internal calls for Unix systems.
5 * Author:
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.
11 #include <config.h>
12 #include <glib.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <fcntl.h>
16 #include <errno.h>
17 #include <signal.h>
18 #ifdef HAVE_SYS_SELECT_H
19 # include <sys/select.h>
20 #endif
21 #ifdef HAVE_SYS_TIME_H
22 # include <sys/time.h>
23 #endif
24 #include <sys/types.h>
25 #ifdef HAVE_UNISTD_H
26 # include <unistd.h>
27 #endif
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 */
40 #ifdef HAVE_CURSES_H
41 # include <curses.h>
42 #endif
43 #ifdef HAVE_TERMIOS_H
44 # include <termios.h>
45 #endif
46 #ifdef HAVE_TERM_H
47 # include <term.h>
48 #endif
50 /* Needed for FIONREAD under solaris */
51 #ifdef HAVE_SYS_FILIO_H
52 # include <sys/filio.h>
53 #endif
54 #ifdef HAVE_SYS_IOCTL_H
55 # include <sys/ioctl.h>
56 #endif
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;
70 #ifdef HAVE_TERMIOS_H
71 /* This is the last state used by Mono, used after a CONT signal is received */
72 static struct termios mono_attr;
73 #endif
75 /* static void console_restore_signal_handlers (void); */
76 static void console_set_signal_handlers (void);
78 void
79 mono_console_init (void)
81 int fd;
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);
88 close (fd);
91 static struct termios initial_attr;
93 MonoBoolean
94 ves_icall_System_ConsoleDriver_Isatty (HANDLE handle, MonoError* error)
96 return isatty (GPOINTER_TO_INT (handle));
99 static MonoBoolean
100 set_property (gint property, gboolean value)
102 struct termios attr;
103 gboolean callset = FALSE;
104 gboolean check;
106 if (tcgetattr (STDIN_FILENO, &attr) == -1)
107 return FALSE;
109 check = (attr.c_lflag & property) != 0;
110 if ((value || check) && !(value && check)) {
111 callset = TRUE;
112 if (value)
113 attr.c_lflag |= property;
114 else
115 attr.c_lflag &= ~property;
118 if (!callset)
119 return TRUE;
121 if (tcsetattr (STDIN_FILENO, TCSANOW, &attr) == -1)
122 return FALSE;
124 mono_attr = attr;
125 return TRUE;
128 MonoBoolean
129 ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo, MonoError* error)
131 return set_property (ECHO, want_echo);
134 MonoBoolean
135 ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break, MonoError* error)
137 return set_property (IGNBRK, !want_break);
140 gint32
141 ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout, MonoError* error)
143 fd_set rfds;
144 struct timeval tv;
145 struct timeval *tvptr;
146 div_t divvy;
147 int ret, nbytes;
149 do {
150 FD_ZERO (&rfds);
151 FD_SET (STDIN_FILENO, &rfds);
152 if (timeout >= 0) {
153 divvy = div (timeout, 1000);
154 tv.tv_sec = divvy.quot;
155 tv.tv_usec = divvy.rem;
156 tvptr = &tv;
157 } else {
158 tvptr = NULL;
160 ret = select (STDIN_FILENO + 1, &rfds, NULL, NULL, tvptr);
161 } while (ret == -1 && errno == EINTR);
163 if (ret > 0) {
164 nbytes = 0;
165 ret = ioctl (STDIN_FILENO, FIONREAD, &nbytes);
166 if (ret >= 0)
167 ret = nbytes;
170 return (ret > 0) ? ret : 0;
173 static gint32 cols_and_lines;
175 #ifdef TIOCGWINSZ
176 static int
177 terminal_get_dimensions (void)
179 struct winsize ws;
180 int ret;
181 int save_errno = errno;
183 if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) == 0){
184 ret = (ws.ws_col << 16) | ws.ws_row;
185 errno = save_errno;
186 return ret;
188 return -1;
190 #else
191 static int
192 terminal_get_dimensions (void)
194 return -1;
196 #endif
198 static void
199 tty_teardown (void)
201 int unused G_GNUC_UNUSED;
203 if (!setup_finished)
204 return;
206 if (teardown_str != NULL) {
207 unused = write (STDOUT_FILENO, teardown_str, strlen (teardown_str));
208 g_free (teardown_str);
209 teardown_str = NULL;
212 tcflush (STDIN_FILENO, TCIFLUSH);
213 tcsetattr (STDIN_FILENO, TCSANOW, &initial_attr);
214 set_property (ECHO, TRUE);
215 setup_finished = FALSE;
218 static void
219 do_console_cancel_event (void)
221 static MonoMethod *System_Console_DoConsoleCancelEventBackground_method = ((gpointer)-1);
222 ERROR_DECL (error);
224 if (mono_defaults.console_class == NULL)
225 return;
227 if (System_Console_DoConsoleCancelEventBackground_method == ((gpointer)-1))
228 System_Console_DoConsoleCancelEventBackground_method = mono_class_get_method_from_name (mono_defaults.console_class, "DoConsoleCancelEventInBackground", 0);
229 if (System_Console_DoConsoleCancelEventBackground_method == NULL)
230 return;
232 mono_runtime_invoke_checked (System_Console_DoConsoleCancelEventBackground_method, NULL, NULL, error);
233 mono_error_assert_ok (error);
236 static int need_cancel = FALSE;
237 /* this is executed from the finalizer thread */
238 void
239 mono_console_handle_async_ops (void)
241 if (need_cancel) {
242 need_cancel = FALSE;
243 do_console_cancel_event ();
247 static gboolean in_sigint;
249 MONO_SIG_HANDLER_FUNC (static, sigint_handler)
251 int save_errno;
253 if (in_sigint)
254 return;
256 in_sigint = TRUE;
257 save_errno = errno;
258 need_cancel = TRUE;
259 mono_gc_finalize_notify ();
260 errno = save_errno;
261 in_sigint = FALSE;
264 static struct sigaction save_sigcont, save_sigint, save_sigwinch;
266 MONO_SIG_HANDLER_FUNC (static, sigcont_handler)
268 int unused G_GNUC_UNUSED;
269 // Ignore error, there is not much we can do in the sigcont handler.
270 tcsetattr (STDIN_FILENO, TCSANOW, &mono_attr);
272 if (keypad_xmit_str != NULL)
273 unused = write (STDOUT_FILENO, keypad_xmit_str, strlen (keypad_xmit_str));
275 // Call previous handler
276 if (save_sigcont.sa_sigaction != NULL &&
277 save_sigcont.sa_sigaction != (void *)SIG_DFL &&
278 save_sigcont.sa_sigaction != (void *)SIG_IGN)
279 (*save_sigcont.sa_sigaction) (MONO_SIG_HANDLER_PARAMS);
282 MONO_SIG_HANDLER_FUNC (static, sigwinch_handler)
284 int dims = terminal_get_dimensions ();
285 if (dims != -1)
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) (MONO_SIG_HANDLER_PARAMS);
296 * console_set_signal_handlers:
298 * Installs various signals handlers for the use of the console, as
299 * follows:
301 * SIGCONT: this is received after the application has resumed execution
302 * if it was suspended with Control-Z before. This signal handler needs
303 * to resend the terminal sequence to send keyboard in keypad mode (this
304 * is the difference between getting a cuu1 code or a kcuu1 code for up-arrow
305 * for example
307 * SIGINT: invokes the System.Console.DoConsoleCancelEvent method using
308 * a thread from the thread pool which notifies all registered cancel_event
309 * listeners.
311 * SIGWINCH: is used to track changes to the console window when a GUI
312 * terminal is resized. It sets an internal variable that is checked
313 * by System.Console when the Terminfo driver has been activated.
315 static void
316 console_set_signal_handlers ()
318 #if defined(HAVE_SIGACTION)
319 struct sigaction sigcont, sigint, sigwinch;
321 memset (&sigcont, 0, sizeof (struct sigaction));
322 memset (&sigint, 0, sizeof (struct sigaction));
323 memset (&sigwinch, 0, sizeof (struct sigaction));
325 // Continuing
326 sigcont.sa_handler = (void (*)(int)) sigcont_handler;
327 sigcont.sa_flags = SA_RESTART;
328 sigemptyset (&sigcont.sa_mask);
329 sigaction (SIGCONT, &sigcont, &save_sigcont);
331 // Interrupt handler
332 sigint.sa_handler = (void (*)(int)) sigint_handler;
333 sigint.sa_flags = SA_RESTART;
334 sigemptyset (&sigint.sa_mask);
335 sigaction (SIGINT, &sigint, &save_sigint);
337 // Window size changed
338 sigwinch.sa_handler = (void (*)(int)) sigwinch_handler;
339 sigwinch.sa_flags = SA_RESTART;
340 sigemptyset (&sigwinch.sa_mask);
341 sigaction (SIGWINCH, &sigwinch, &save_sigwinch);
342 #endif
345 #if currently_unuused
347 // Currently unused, should we ever call the restore handler?
348 // Perhaps before calling into Process.Start?
350 void
351 console_restore_signal_handlers ()
353 sigaction (SIGCONT, &save_sigcont, NULL);
354 sigaction (SIGINT, &save_sigint, NULL);
355 sigaction (SIGWINCH, &save_sigwinch, NULL);
357 #endif
359 static void
360 set_control_chars (gchar *control_chars, const guchar *cc)
362 /* The index into the array comes from corlib/System/ControlCharacters.cs */
363 #ifdef VINTR
364 control_chars [0] = cc [VINTR];
365 #endif
366 #ifdef VQUIT
367 control_chars [1] = cc [VQUIT];
368 #endif
369 #ifdef VERASE
370 control_chars [2] = cc [VERASE];
371 #endif
372 #ifdef VKILL
373 control_chars [3] = cc [VKILL];
374 #endif
375 #ifdef VEOF
376 control_chars [4] = cc [VEOF];
377 #endif
378 #ifdef VTIME
379 control_chars [5] = cc [VTIME];
380 #endif
381 #ifdef VMIN
382 control_chars [6] = cc [VMIN];
383 #endif
384 #ifdef VSWTC
385 control_chars [7] = cc [VSWTC];
386 #endif
387 #ifdef VSTART
388 control_chars [8] = cc [VSTART];
389 #endif
390 #ifdef VSTOP
391 control_chars [9] = cc [VSTOP];
392 #endif
393 #ifdef VSUSP
394 control_chars [10] = cc [VSUSP];
395 #endif
396 #ifdef VEOL
397 control_chars [11] = cc [VEOL];
398 #endif
399 #ifdef VREPRINT
400 control_chars [12] = cc [VREPRINT];
401 #endif
402 #ifdef VDISCARD
403 control_chars [13] = cc [VDISCARD];
404 #endif
405 #ifdef VWERASE
406 control_chars [14] = cc [VWERASE];
407 #endif
408 #ifdef VLNEXT
409 control_chars [15] = cc [VLNEXT];
410 #endif
411 #ifdef VEOL2
412 control_chars [16] = cc [VEOL2];
413 #endif
416 MonoBoolean
417 ves_icall_System_ConsoleDriver_TtySetup (MonoStringHandle keypad, MonoStringHandle teardown, MonoArrayHandleOut control_chars, int **size, MonoError* error)
419 // FIXME Lock around the globals?
421 int dims;
423 dims = terminal_get_dimensions ();
424 if (dims == -1){
425 int cols = 0, rows = 0;
427 char *str = g_getenv ("COLUMNS");
428 if (str != NULL) {
429 cols = atoi (str);
430 g_free (str);
432 str = g_getenv ("LINES");
433 if (str != NULL) {
434 rows = atoi (str);
435 g_free (str);
438 if (cols != 0 && rows != 0)
439 cols_and_lines = (cols << 16) | rows;
440 else
441 cols_and_lines = -1;
442 } else {
443 cols_and_lines = dims;
446 *size = &cols_and_lines;
448 /* 17 is the number of entries set in set_control_chars() above.
449 * NCCS is the total size, but, by now, we only care about those 17 values*/
450 MonoArrayHandle control_chars_arr = mono_array_new_handle (mono_domain_get (), mono_defaults.byte_class, 17, error);
451 return_val_if_nok (error, FALSE);
453 MONO_HANDLE_ASSIGN (control_chars, control_chars_arr);
454 if (tcgetattr (STDIN_FILENO, &initial_attr) == -1)
455 return FALSE;
457 mono_attr = initial_attr;
458 mono_attr.c_lflag &= ~(ICANON);
459 mono_attr.c_iflag &= ~(IXON|IXOFF);
460 mono_attr.c_cc [VMIN] = 1;
461 mono_attr.c_cc [VTIME] = 0;
462 #ifdef VDSUSP
463 /* Disable C-y being used as a suspend character on OSX */
464 mono_attr.c_cc [VDSUSP] = 255;
465 #endif
466 gint ret;
467 do {
468 MONO_ENTER_GC_SAFE;
469 ret = tcsetattr (STDIN_FILENO, TCSANOW, &mono_attr);
470 MONO_EXIT_GC_SAFE;
471 } while (ret == -1 && errno == EINTR);
473 if (ret == -1)
474 return FALSE;
476 uint32_t h;
477 set_control_chars (MONO_ARRAY_HANDLE_PIN (control_chars_arr, gchar, 0, &h), mono_attr.c_cc);
478 mono_gchandle_free (h);
479 /* If initialized from another appdomain... */
480 if (setup_finished)
481 return TRUE;
483 keypad_xmit_str = NULL;
484 if (!MONO_HANDLE_IS_NULL (keypad)) {
485 keypad_xmit_str = mono_string_handle_to_utf8 (keypad, error);
486 return_val_if_nok (error, FALSE);
489 console_set_signal_handlers ();
490 setup_finished = TRUE;
491 if (!atexit_called) {
492 if (!MONO_HANDLE_IS_NULL (teardown)) {
493 teardown_str = mono_string_handle_to_utf8 (teardown, error);
494 return_val_if_nok (error, FALSE);
497 mono_atexit (tty_teardown);
500 return TRUE;