2007-08-24 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / metadata / console-io.c
1 /*
2  * console-io.c: ConsoleDriver internal calls
3  *
4  * Author:
5  *      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6  *
7  * Copyright (C) 2005 Novell, Inc. (http://www.novell.com)
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <signal.h>
16 #ifdef HAVE_SYS_TIME_H
17 #include <sys/time.h>
18 #endif
19 #include <sys/types.h>
20 #ifdef HAVE_UNISTD_H
21 #include <unistd.h>
22 #endif
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 */
30 #ifdef HAVE_CURSES_H
31 #include <curses.h>
32 #endif
33 #ifdef HAVE_TERMIOS_H
34 #include <termios.h>
35 #endif
36 #ifdef HAVE_TERM_H
37 #include <term.h>
38 #endif
39 /* Needed for FIONREAD under solaris */
40 #ifdef HAVE_SYS_FILIO_H
41 #include <sys/filio.h>
42 #endif
43 #ifndef PLATFORM_WIN32
44 #ifndef TIOCGWINSZ
45 #include <sys/ioctl.h>
46 #endif
47 #endif
48
49 #include <mono/metadata/console-io.h>
50 #include <mono/metadata/exception.h>
51
52 static gboolean setup_finished;
53 static gboolean atexit_called;
54 static gchar *teardown_str;
55
56 #ifdef PLATFORM_WIN32
57 MonoBoolean
58 ves_icall_System_ConsoleDriver_Isatty (HANDLE handle)
59 {
60         MONO_ARCH_SAVE_REGS;
61
62         return (GetFileType (handle) == FILE_TYPE_CHAR);
63 }
64
65 MonoBoolean
66 ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo)
67 {
68         return FALSE;
69 }
70
71 MonoBoolean
72 ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break)
73 {
74         return FALSE;
75 }
76
77 gint32
78 ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout)
79 {
80         return FALSE;
81 }
82
83 MonoBoolean
84 ves_icall_System_ConsoleDriver_TtySetup (MonoString *teardown, char *verase, char *vsusp, char *intr)
85 {
86         return FALSE;
87 }
88
89 MonoBoolean
90 ves_icall_System_ConsoleDriver_GetTtySize (HANDLE handle, gint32 *width, gint32 *height)
91 {
92         return FALSE;
93 }
94
95 #else
96 static struct termios initial_attr;
97
98 MonoBoolean
99 ves_icall_System_ConsoleDriver_Isatty (HANDLE handle)
100 {
101         MONO_ARCH_SAVE_REGS;
102
103         return isatty (GPOINTER_TO_INT (handle));
104 }
105
106 static MonoBoolean
107 set_property (gint property, gboolean value)
108 {
109         struct termios attr;
110         gboolean callset = FALSE;
111         gboolean check;
112         
113         MONO_ARCH_SAVE_REGS;
114
115         if (tcgetattr (STDIN_FILENO, &attr) == -1)
116                 return FALSE;
117
118         check = (attr.c_lflag & property) != 0;
119         if ((value || check) && !(value && check)) {
120                 callset = TRUE;
121                 if (value)
122                         attr.c_lflag |= property;
123                 else
124                         attr.c_lflag &= ~property;
125         }
126
127         if (!callset)
128                 return TRUE;
129
130         if (tcsetattr (STDIN_FILENO, TCSANOW, &attr) == -1)
131                 return FALSE;
132
133         return TRUE;
134 }
135
136 MonoBoolean
137 ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo)
138 {
139         return set_property (ECHO, want_echo);
140 }
141
142 MonoBoolean
143 ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break)
144 {
145         return set_property (IGNBRK, !want_break);
146 }
147
148 gint32
149 ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout)
150 {
151         fd_set rfds;
152         struct timeval tv;
153         struct timeval *tvptr;
154         div_t divvy;
155         int ret, nbytes;
156
157         MONO_ARCH_SAVE_REGS;
158
159         do {
160                 FD_ZERO (&rfds);
161                 FD_SET (STDIN_FILENO, &rfds);
162                 if (timeout >= 0) {
163                         divvy = div (timeout, 1000);
164                         tv.tv_sec = divvy.quot;
165                         tv.tv_usec = divvy.rem;
166                         tvptr = &tv;
167                 } else {
168                         tvptr = NULL;
169                 }
170                 ret = select (STDIN_FILENO + 1, &rfds, NULL, NULL, tvptr);
171         } while (ret == -1 && errno == EINTR);
172
173         if (ret > 0) {
174                 nbytes = 0;
175                 ret = ioctl (STDIN_FILENO, FIONREAD, &nbytes);
176                 if (ret >= 0)
177                         ret = nbytes;
178         }
179
180         return (ret > 0) ? ret : 0;
181 }
182
183 static void
184 tty_teardown (void)
185 {
186         MONO_ARCH_SAVE_REGS;
187
188         if (!setup_finished)
189                 return;
190
191         if (teardown_str != NULL) {
192                 write (STDOUT_FILENO, teardown_str, strlen (teardown_str));
193                 g_free (teardown_str);
194                 teardown_str = NULL;
195         }
196
197         tcflush (STDIN_FILENO, TCIFLUSH);
198         tcsetattr (STDIN_FILENO, TCSANOW, &initial_attr);
199         set_property (ECHO, TRUE);
200         setup_finished = FALSE;
201 }
202
203 static void
204 do_console_cancel_event (void)
205 {
206         static MonoClassField *cancel_handler_field;
207         MonoDomain *domain = mono_domain_get ();
208         MonoClass *klass;
209         MonoDelegate *load_value;
210         MonoMethod *method;
211         MonoMethodMessage *msg;
212         MonoMethod *im;
213
214         if (!domain->domain)
215                 return;
216
217         klass = mono_class_from_name (mono_defaults.corlib, "System", "Console");
218         if (klass == NULL)
219                 return;
220
221         if (cancel_handler_field == NULL) {
222                 cancel_handler_field = mono_class_get_field_from_name (klass, "cancel_handler");
223                 g_assert (cancel_handler_field);
224         }
225
226         mono_field_static_get_value (mono_class_vtable (domain, klass), cancel_handler_field, &load_value);
227         if (load_value == NULL)
228                 return;
229
230         klass = load_value->object.vtable->klass;
231         method = mono_class_get_method_from_name (klass, "BeginInvoke", -1);
232         g_assert (method != NULL);
233         im = mono_get_delegate_invoke (method->klass);
234         msg = mono_method_call_message_new (method, NULL, im, NULL, NULL);
235         mono_thread_pool_add ((MonoObject *) load_value, msg, NULL, NULL);
236 }
237
238 static gboolean in_sigint;
239 static void
240 sigint_handler (int signo)
241 {
242         MONO_ARCH_SAVE_REGS;
243
244         if (in_sigint)
245                 return;
246
247         in_sigint = TRUE;
248         do_console_cancel_event ();
249         in_sigint = FALSE;
250 }
251
252 MonoBoolean
253 ves_icall_System_ConsoleDriver_TtySetup (MonoString *teardown, char *verase, char *vsusp, char*intr)
254 {
255         struct termios attr;
256         
257         MONO_ARCH_SAVE_REGS;
258
259
260         *verase = '\0';
261         *vsusp = '\0';
262         *intr = '\0';
263         if (tcgetattr (STDIN_FILENO, &initial_attr) == -1)
264                 return FALSE;
265
266         /* TODO: handle SIGTSTP - Ctrl-Z */
267         attr = initial_attr;
268         attr.c_lflag &= ~ICANON;
269         attr.c_cc [VMIN] = 1;
270         attr.c_cc [VTIME] = 0;
271         if (tcsetattr (STDIN_FILENO, TCSANOW, &attr) == -1)
272                 return FALSE;
273
274         *verase = initial_attr.c_cc [VERASE];
275         *vsusp = initial_attr.c_cc [VSUSP];
276         *intr = initial_attr.c_cc [VINTR];
277         /* If initialized from another appdomain... */
278         if (setup_finished)
279                 return TRUE;
280
281         signal (SIGINT, sigint_handler);
282         setup_finished = TRUE;
283         if (!atexit_called) {
284                 if (teardown != NULL)
285                         teardown_str = mono_string_to_utf8 (teardown);
286
287                 atexit (tty_teardown);
288         }
289
290         return TRUE;
291 }
292
293 MonoBoolean
294 ves_icall_System_ConsoleDriver_GetTtySize (HANDLE handle, gint32 *width, gint32 *height)
295 {
296 #ifdef TIOCGWINSZ
297         struct winsize ws;
298         int res;
299
300         MONO_ARCH_SAVE_REGS;
301
302         res = ioctl (GPOINTER_TO_INT (handle), TIOCGWINSZ, &ws);
303
304         if (!res) {
305                 *width = ws.ws_col;
306                 *height = ws.ws_row;
307                 return TRUE;
308         }
309         else
310                 return FALSE;
311 #else
312         return FALSE;
313 #endif
314 }
315
316 #endif /* !PLATFORM_WIN32 */