3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4 2006, 2008, 2009, 2010 Red Hat, Inc.
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
13 #include "miscfuncs.h"
21 #include <sys/cygwin.h>
22 #include <cygwin/kd.h>
31 #include "shared_info.h"
36 /* Don't make this bigger than NT_MAX_PATH as long as the temporary buffer
37 is allocated using tmp_pathbuf!!! */
38 #define CONVERT_LIMIT NT_MAX_PATH
41 * Scroll the screen context.
44 * xn, yn - new ul corner
45 * Negative values represents current screen dimensions
48 #define srTop (dev_state->info.winTop + dev_state->scroll_region.Top)
49 #define srBottom ((dev_state->scroll_region.Bottom < 0) ? dev_state->info.winBottom : dev_state->info.winTop + dev_state->scroll_region.Bottom)
51 #define use_tty ISSTATE (myself, PID_USETTY)
53 const char *get_nonascii_key (INPUT_RECORD&, char *);
55 const unsigned fhandler_console::MAX_WRITE_CHARS = 16384;
57 static console_state NO_COPY *shared_console_info;
59 dev_console NO_COPY *fhandler_console::dev_state;
64 reg_key r (HKEY_CURRENT_USER, KEY_ALL_ACCESS, "AppEvents", "Schemes", "Apps",
65 ".Default", ".Default", ".Current", NULL);
69 UINT len = GetWindowsDirectory (buf, 0);
70 buf = (char *) alloca (len += sizeof ("\\media\\ding.wav"));
71 UINT res = GetWindowsDirectory (buf, len);
72 if (res && res <= len)
73 r.set_string ("", strcat (buf, "\\media\\ding.wav"));
78 /* Allocate and initialize the shared record for the current console.
79 Returns a pointer to shared_console_info. */
81 fhandler_console::get_tty_stuff (int flags = 0)
84 return &shared_console_info->tty_min_state;
87 (console_state *) open_shared (NULL, 0, cygheap->console_h,
88 sizeof (*shared_console_info),
90 dev_state = &shared_console_info->dev_state;
92 ProtectHandleINH (cygheap->console_h);
93 if (!shared_console_info->tty_min_state.ntty)
95 shared_console_info->tty_min_state.setntty (TTY_CONSOLE);
96 shared_console_info->tty_min_state.setsid (myself->sid);
97 myself->set_ctty (&shared_console_info->tty_min_state, flags, NULL);
99 dev_state->scroll_region.Bottom = -1;
100 dev_state->dwLastCursorPosition.X = -1;
101 dev_state->dwLastCursorPosition.Y = -1;
102 dev_state->dwLastMousePosition.X = -1;
103 dev_state->dwLastMousePosition.Y = -1;
104 dev_state->dwLastButtonState = 0; /* none pressed */
105 dev_state->last_button_code = 3; /* released */
106 dev_state->underline_color = FOREGROUND_GREEN | FOREGROUND_BLUE;
107 dev_state->dim_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
108 dev_state->meta_mask = LEFT_ALT_PRESSED;
109 /* Set the mask that determines if an input keystroke is modified by
110 META. We set this based on the keyboard layout language loaded
111 for the current thread. The left <ALT> key always generates
112 META, but the right <ALT> key only generates META if we are using
113 an English keyboard because many "international" keyboards
114 replace common shell symbols ('[', '{', etc.) with accented
115 language-specific characters (umlaut, accent grave, etc.). On
116 these keyboards right <ALT> (called AltGr) is used to produce the
117 shell symbols and should not be interpreted as META. */
118 if (PRIMARYLANGID (LOWORD (GetKeyboardLayout (0))) == LANG_ENGLISH)
119 dev_state->meta_mask |= RIGHT_ALT_PRESSED;
120 dev_state->set_default_attr ();
121 dev_state->backspace_keycode = CERASE;
122 shared_console_info->tty_min_state.sethwnd ((HWND) INVALID_HANDLE_VALUE);
125 return &shared_console_info->tty_min_state;
131 fhandler_console::get_tty_stuff ();
134 /* Return the tty structure associated with a given tty number. If the
135 tty number is < 0, just return a dummy record. */
137 tty_list::get_tty (int n)
140 if (n == TTY_CONSOLE)
141 return fhandler_console::get_tty_stuff ();
143 return &cygwin_shared->tty.ttys[n];
149 dev_console::con_to_str (char *d, int dlen, WCHAR w)
151 return sys_cp_wcstombs (cygheap->locale.wctomb, cygheap->locale.charset,
156 dev_console::get_console_cp ()
158 /* The alternate charset is always 437, just as in the Linux console. */
159 return alternate_charset_active ? 437 : 0;
163 dev_console::str_to_con (mbtowc_p f_mbtowc, const char *charset,
164 PWCHAR d, const char *s, DWORD sz)
166 return sys_cp_mbstowcs (f_mbtowc, charset, d, CONVERT_LIMIT, s, sz);
170 fhandler_console::set_raw_win32_keyboard_mode (bool new_mode)
172 bool old_mode = dev_state->raw_win32_keyboard_mode;
173 dev_state->raw_win32_keyboard_mode = new_mode;
174 syscall_printf ("raw keyboard mode %sabled", dev_state->raw_win32_keyboard_mode ? "en" : "dis");
179 fhandler_console::set_cursor_maybe ()
181 CONSOLE_SCREEN_BUFFER_INFO now;
183 if (!GetConsoleScreenBufferInfo (get_output_handle (), &now))
186 if (dev_state->dwLastCursorPosition.X != now.dwCursorPosition.X ||
187 dev_state->dwLastCursorPosition.Y != now.dwCursorPosition.Y)
189 SetConsoleCursorPosition (get_output_handle (), now.dwCursorPosition);
190 dev_state->dwLastCursorPosition = now.dwCursorPosition;
195 fhandler_console::send_winch_maybe ()
197 SHORT y = dev_state->info.dwWinSize.Y;
198 SHORT x = dev_state->info.dwWinSize.X;
199 dev_state->fillin_info (get_output_handle ());
201 if (y != dev_state->info.dwWinSize.Y || x != dev_state->info.dwWinSize.X)
203 extern fhandler_tty_master *tty_master;
204 dev_state->scroll_region.Top = 0;
205 dev_state->scroll_region.Bottom = -1;
207 tty_master->set_winsize (true);
209 tc->kill_pgrp (SIGWINCH);
213 /* Check whether a mouse event is to be reported as an escape sequence */
215 fhandler_console::mouse_aware (MOUSE_EVENT_RECORD& mouse_event)
217 if (! dev_state->use_mouse)
220 /* Adjust mouse position by window scroll buffer offset
221 and remember adjusted position in state for use by read() */
222 CONSOLE_SCREEN_BUFFER_INFO now;
223 if (GetConsoleScreenBufferInfo (get_output_handle (), &now))
225 dev_state->dwMousePosition.X = mouse_event.dwMousePosition.X - now.srWindow.Left;
226 dev_state->dwMousePosition.Y = mouse_event.dwMousePosition.Y - now.srWindow.Top;
230 /* Cannot adjust position by window scroll buffer offset */
234 /* Check whether adjusted mouse position can be reported */
235 if (dev_state->dwMousePosition.X > 0xFF - ' ' - 1
236 || dev_state->dwMousePosition.Y > 0xFF - ' ' - 1)
238 /* Mouse position out of reporting range */
242 return ((mouse_event.dwEventFlags == 0 || mouse_event.dwEventFlags == DOUBLE_CLICK)
243 && mouse_event.dwButtonState != dev_state->dwLastButtonState)
244 || mouse_event.dwEventFlags == MOUSE_WHEELED
245 || (mouse_event.dwEventFlags == MOUSE_MOVED
246 && (dev_state->dwMousePosition.X != dev_state->dwLastMousePosition.X
247 || dev_state->dwMousePosition.Y != dev_state->dwLastMousePosition.Y)
248 && ((dev_state->use_mouse >= 2 && mouse_event.dwButtonState)
249 || dev_state->use_mouse >= 3));
253 fhandler_console::read (void *pv, size_t& buflen)
255 HANDLE h = get_io_handle ();
257 #define buf ((char *) pv)
262 int copied_chars = get_readahead_into_buffer (buf, buflen);
266 buflen = copied_chars;
275 if (&_my_tls != _main_tls)
279 w4[1] = signal_arrived;
287 if ((bgres = bg_check (SIGTTIN)) <= bg_eof)
293 set_cursor_maybe (); /* to make cursor appear on the screen immediately */
294 switch (WaitForMultipleObjects (nwait, w4, FALSE, INFINITE))
298 case WAIT_OBJECT_0 + 1:
305 INPUT_RECORD input_rec;
306 const char *toadd = NULL;
308 if (!ReadConsoleInputW (h, &input_rec, 1, &nread))
310 syscall_printf ("ReadConsoleInput failed, %E");
311 goto err; /* seems to be failure */
314 /* check the event that occurred */
315 switch (input_rec.EventType)
318 #define virtual_key_code (input_rec.Event.KeyEvent.wVirtualKeyCode)
319 #define control_key_state (input_rec.Event.KeyEvent.dwControlKeyState)
321 dev_state->nModifiers = 0;
324 /* allow manual switching to/from raw mode via ctrl-alt-scrolllock */
325 if (input_rec.Event.KeyEvent.bKeyDown &&
326 virtual_key_code == VK_SCROLL &&
327 ((control_key_state & (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED)) == (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED))
330 set_raw_win32_keyboard_mode (!dev_state->raw_win32_keyboard_mode);
335 if (dev_state->raw_win32_keyboard_mode)
337 __small_sprintf (tmp, "\033{%u;%u;%u;%u;%u;%luK",
338 input_rec.Event.KeyEvent.bKeyDown,
339 input_rec.Event.KeyEvent.wRepeatCount,
340 input_rec.Event.KeyEvent.wVirtualKeyCode,
341 input_rec.Event.KeyEvent.wVirtualScanCode,
342 input_rec.Event.KeyEvent.uChar.UnicodeChar,
343 input_rec.Event.KeyEvent.dwControlKeyState);
345 nread = strlen (toadd);
349 #define ich (input_rec.Event.KeyEvent.uChar.AsciiChar)
350 #define wch (input_rec.Event.KeyEvent.uChar.UnicodeChar)
351 #define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
352 #define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
354 /* Ignore key up events, except for left alt events with non-zero character
356 if (!input_rec.Event.KeyEvent.bKeyDown &&
358 Event for left alt, with a non-zero character, comes from
359 "alt + numerics" key sequence.
360 e.g. <left-alt> 0233 => é
363 // ?? experimentally determined on an XP system
364 && virtual_key_code == VK_MENU
365 // left alt -- see http://www.microsoft.com/hwdev/tech/input/Scancode.asp
366 && input_rec.Event.KeyEvent.wVirtualScanCode == 0x38))
369 if (control_key_state & SHIFT_PRESSED)
370 dev_state->nModifiers |= 1;
371 if (control_key_state & RIGHT_ALT_PRESSED)
372 dev_state->nModifiers |= 2;
373 if (control_key_state & CTRL_PRESSED)
374 dev_state->nModifiers |= 4;
375 if (control_key_state & LEFT_ALT_PRESSED)
376 dev_state->nModifiers |= 8;
378 if (input_rec.Event.KeyEvent.wVirtualScanCode == 14)
380 char c = dev_state->backspace_keycode;
382 if (control_key_state & ALT_PRESSED) {
383 if (dev_state->metabit)
392 /* Allow Ctrl-Space to emit ^@ */
393 else if (input_rec.Event.KeyEvent.wVirtualKeyCode == VK_SPACE
394 && (control_key_state & CTRL_PRESSED))
397 /* arrow/function keys */
398 || (input_rec.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
400 toadd = get_nonascii_key (input_rec, tmp);
403 dev_state->nModifiers = 0;
406 nread = strlen (toadd);
410 nread = dev_state->con_to_str (tmp + 1, 59, wch);
411 /* Determine if the keystroke is modified by META. The tricky
412 part is to distinguish whether the right Alt key should be
413 recognized as Alt, or as AltGr. */
415 /* Alt but not AltGr (= left ctrl + right alt)? */
416 (control_key_state & ALT_PRESSED) != 0
417 && ((control_key_state & CTRL_PRESSED) == 0
418 /* but also allow Alt-AltGr: */
419 || (control_key_state & ALT_PRESSED) == ALT_PRESSED
420 || (wch <= 0x1f || wch == 0x7f));
423 /* Determine if the character is in the current multibyte
424 charset. The test is easy. If the multibyte sequence
425 is > 1 and the first byte is ASCII CAN, the character
426 has been translated into the ASCII CAN + UTF-8 replacement
427 sequence. If so, just ignore the keypress.
428 FIXME: Is there a better solution? */
429 if (nread > 1 && tmp[1] == 0x18)
434 else if (dev_state->metabit)
442 tmp[1] = cyg_tolower (tmp[1]);
445 dev_state->nModifiers &= ~4;
457 MOUSE_EVENT_RECORD& mouse_event = input_rec.Event.MouseEvent;
458 /* As a unique guard for mouse report generation,
459 call mouse_aware() which is common with select(), so the result
460 of select() and the actual read() will be consistent on the
461 issue of whether input (i.e. a mouse escape sequence) will
462 be available or not */
463 if (mouse_aware (mouse_event))
465 /* Note: Reported mouse position was already retrieved by
466 mouse_aware() and adjusted by window scroll buffer offset */
468 /* Treat the double-click event like a regular button press */
469 if (mouse_event.dwEventFlags == DOUBLE_CLICK)
471 syscall_printf ("mouse: double-click -> click");
472 mouse_event.dwEventFlags = 0;
475 /* This code assumes Windows never reports multiple button
476 events at the same time. */
480 if (mouse_event.dwEventFlags == MOUSE_WHEELED)
482 if (mouse_event.dwButtonState & 0xFF800000)
485 strcpy (sz, "wheel down");
490 strcpy (sz, "wheel up");
495 /* Ignore unimportant mouse buttons */
496 mouse_event.dwButtonState &= 0x7;
498 if (mouse_event.dwEventFlags == MOUSE_MOVED)
500 b = dev_state->last_button_code;
502 else if (mouse_event.dwButtonState < dev_state->dwLastButtonState)
505 strcpy (sz, "btn up");
507 else if ((mouse_event.dwButtonState & 1) != (dev_state->dwLastButtonState & 1))
510 strcpy (sz, "btn1 down");
512 else if ((mouse_event.dwButtonState & 2) != (dev_state->dwLastButtonState & 2))
515 strcpy (sz, "btn2 down");
517 else if ((mouse_event.dwButtonState & 4) != (dev_state->dwLastButtonState & 4))
520 strcpy (sz, "btn3 down");
523 dev_state->last_button_code = b;
525 if (mouse_event.dwEventFlags == MOUSE_MOVED)
532 /* Remember the modified button state */
533 dev_state->dwLastButtonState = mouse_event.dwButtonState;
537 /* Remember mouse position */
538 dev_state->dwLastMousePosition.X = dev_state->dwMousePosition.X;
539 dev_state->dwLastMousePosition.Y = dev_state->dwMousePosition.Y;
541 /* Remember the modifiers */
542 dev_state->nModifiers = 0;
543 if (mouse_event.dwControlKeyState & SHIFT_PRESSED)
544 dev_state->nModifiers |= 0x4;
545 if (mouse_event.dwControlKeyState & (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED))
546 dev_state->nModifiers |= 0x8;
547 if (mouse_event.dwControlKeyState & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED))
548 dev_state->nModifiers |= 0x10;
550 /* Indicate the modifiers */
551 b |= dev_state->nModifiers;
553 /* We can now create the code. */
554 sprintf (tmp, "\033[M%c%c%c", b + ' ', dev_state->dwMousePosition.X + ' ' + 1, dev_state->dwMousePosition.Y + ' ' + 1);
555 syscall_printf ("mouse: %s at (%d,%d)", sz, dev_state->dwMousePosition.X, dev_state->dwMousePosition.Y);
564 if (dev_state->use_focus) {
565 if (input_rec.Event.FocusEvent.bSetFocus)
566 sprintf (tmp, "\033[I");
568 sprintf (tmp, "\033[O");
575 case WINDOW_BUFFER_SIZE_EVENT:
584 line_edit_status res = line_edit (toadd, nread, ti);
585 if (res == line_edit_signalled)
587 else if (res == line_edit_input_done)
593 if ((ch = get_readahead ()) < 0)
597 buf[copied_chars++] = (unsigned char)(ch & 0xff);
602 buflen = copied_chars;
607 buflen = (size_t) -1;
611 set_sig_errno (EINTR);
612 buflen = (size_t) -1;
616 fhandler_console::set_input_state ()
619 input_tcsetattr (0, &tc->ti);
623 dev_console::fillin_info (HANDLE h)
626 CONSOLE_SCREEN_BUFFER_INFO linfo;
628 if ((ret = GetConsoleScreenBufferInfo (h, &linfo)))
630 info.winTop = linfo.srWindow.Top;
631 info.winBottom = linfo.srWindow.Bottom;
632 info.dwWinSize.Y = 1 + linfo.srWindow.Bottom - linfo.srWindow.Top;
633 info.dwWinSize.X = 1 + linfo.srWindow.Right - linfo.srWindow.Left;
634 info.dwBufferSize = linfo.dwSize;
635 info.dwCursorPosition = linfo.dwCursorPosition;
636 info.wAttributes = linfo.wAttributes;
640 memset (&info, 0, sizeof info);
641 info.dwWinSize.Y = 25;
642 info.dwWinSize.X = 80;
650 fhandler_console::scroll_screen (int x1, int y1, int x2, int y2, int xn, int yn)
656 dev_state->fillin_info (get_output_handle ());
657 sr1.Left = x1 >= 0 ? x1 : dev_state->info.dwWinSize.X - 1;
659 sr1.Top = dev_state->info.winTop;
661 sr1.Top = y1 > 0 ? y1 : dev_state->info.winBottom;
662 sr1.Right = x2 >= 0 ? x2 : dev_state->info.dwWinSize.X - 1;
664 sr1.Bottom = dev_state->info.winTop;
666 sr1.Bottom = y2 > 0 ? y2 : dev_state->info.winBottom;
669 sr2.Bottom = srBottom;
670 sr2.Right = dev_state->info.dwWinSize.X - 1;
671 if (sr1.Bottom > sr2.Bottom && sr1.Top <= sr2.Bottom)
672 sr1.Bottom = sr2.Bottom;
673 dest.X = xn >= 0 ? xn : dev_state->info.dwWinSize.X - 1;
675 dest.Y = dev_state->info.winTop;
677 dest.Y = yn > 0 ? yn : dev_state->info.winBottom;
678 fill.Char.AsciiChar = ' ';
679 fill.Attributes = dev_state->current_win32_attr;
680 ScrollConsoleScreenBuffer (get_output_handle (), &sr1, &sr2, dest, &fill);
682 /* ScrollConsoleScreenBuffer on Windows 95 is buggy - when scroll distance
683 * is more than half of screen, filling doesn't work as expected */
685 if (sr1.Top == sr1.Bottom)
687 else if (dest.Y <= sr1.Top) /* forward scroll */
688 clear_screen (0, 1 + dest.Y + sr1.Bottom - sr1.Top, sr2.Right, sr2.Bottom);
689 else /* reverse scroll */
690 clear_screen (0, sr1.Top, sr2.Right, dest.Y - 1);
694 fhandler_console::open (int flags, mode_t)
698 tcinit (get_tty_stuff (flags), false);
700 set_io_handle (NULL);
701 set_output_handle (NULL);
703 set_flags ((flags & ~O_TEXT) | O_BINARY);
705 /* Open the input handle as handle_ */
706 h = CreateFile ("CONIN$", GENERIC_READ | GENERIC_WRITE,
707 FILE_SHARE_READ | FILE_SHARE_WRITE, sec_none_cloexec (flags),
708 OPEN_EXISTING, 0, 0);
710 if (h == INVALID_HANDLE_VALUE)
716 uninterruptible_io (true); // Handled explicitly in read code
718 h = CreateFile ("CONOUT$", GENERIC_READ | GENERIC_WRITE,
719 FILE_SHARE_READ | FILE_SHARE_WRITE, sec_none_cloexec (flags),
720 OPEN_EXISTING, 0, 0);
722 if (h == INVALID_HANDLE_VALUE)
727 set_output_handle (h);
729 if (dev_state->fillin_info (get_output_handle ()))
731 dev_state->current_win32_attr = dev_state->info.wAttributes;
732 if (!dev_state->default_color)
733 dev_state->default_color = dev_state->info.wAttributes;
734 dev_state->set_default_attr ();
739 cygheap->manage_console_count ("fhandler_console::open", 1);
742 if (GetConsoleMode (get_io_handle (), &cflags))
743 SetConsoleMode (get_io_handle (),
744 ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | cflags);
746 debug_printf ("opened conin$ %p, conout$ %p", get_io_handle (),
747 get_output_handle ());
753 fhandler_console::close ()
755 CloseHandle (get_io_handle ());
756 CloseHandle (get_output_handle ());
758 cygheap->manage_console_count ("fhandler_console::close", -1);
762 /* Special console dup to duplicate input and output handles. */
765 fhandler_console::dup (fhandler_base *child)
767 fhandler_console *fhc = (fhandler_console *) child;
769 if (!fhc->open (get_flags () & ~O_NOCTTY, 0))
770 system_printf ("error opening console, %E");
776 fhandler_console::ioctl (unsigned int cmd, void *buf)
783 st = dev_state->fillin_info (get_output_handle ());
786 /* *not* the buffer size, the actual screen size... */
787 /* based on Left Top Right Bottom of srWindow */
788 ((struct winsize *) buf)->ws_row = dev_state->info.dwWinSize.Y;
789 ((struct winsize *) buf)->ws_col = dev_state->info.dwWinSize.X;
790 syscall_printf ("WINSZ: (row=%d,col=%d)",
791 ((struct winsize *) buf)->ws_row,
792 ((struct winsize *) buf)->ws_col);
797 syscall_printf ("WINSZ failed");
806 *(int *) buf = (dev_state->metabit) ? K_METABIT : K_ESCPREFIX;
809 if ((int) buf == K_METABIT)
810 dev_state->metabit = TRUE;
811 else if ((int) buf == K_ESCPREFIX)
812 dev_state->metabit = FALSE;
820 if (*(unsigned char *) buf == 6)
822 *(unsigned char *) buf = (unsigned char) dev_state->nModifiers;
832 return fhandler_base::ioctl (cmd, buf);
836 fhandler_console::tcflush (int queue)
839 if (queue == TCIFLUSH
840 || queue == TCIOFLUSH)
842 if (!FlushConsoleInputBuffer (get_io_handle ()))
852 fhandler_console::output_tcsetattr (int, struct termios const *t)
854 /* All the output bits we can ignore */
856 DWORD flags = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
858 int res = SetConsoleMode (get_output_handle (), flags) ? 0 : -1;
859 syscall_printf ("%d = tcsetattr (,%x) (ENABLE FLAGS %x) (lflag %x oflag %x)",
860 res, t, flags, t->c_lflag, t->c_oflag);
865 fhandler_console::input_tcsetattr (int, struct termios const *t)
867 /* Ignore the optional_actions stuff, since all output is emitted
872 if (!GetConsoleMode (get_io_handle (), &oflags))
877 /* Enable/disable LF -> CRLF conversions */
878 rbinary ((t->c_iflag & INLCR) ? false : true);
881 /* There's some disparity between what we need and what's
882 available. We've got ECHO and ICANON, they've
883 got ENABLE_ECHO_INPUT and ENABLE_LINE_INPUT. */
887 if (t->c_lflag & ECHO)
889 flags |= ENABLE_ECHO_INPUT;
891 if (t->c_lflag & ICANON)
893 flags |= ENABLE_LINE_INPUT;
896 if (flags & ENABLE_ECHO_INPUT
897 && !(flags & ENABLE_LINE_INPUT))
899 /* This is illegal, so turn off the echo here, and fake it
900 when we read the characters */
902 flags &= ~ENABLE_ECHO_INPUT;
905 if (t->c_lflag & ISIG)
907 flags |= ENABLE_PROCESSED_INPUT;
912 flags = 0; // ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
917 flags |= ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT;
924 res = SetConsoleMode (get_io_handle (), flags) ? 0 : -1;
927 syscall_printf ("%d = tcsetattr (,%x) enable flags %p, c_lflag %p iflag %p",
928 res, t, flags, t->c_lflag, t->c_iflag);
936 fhandler_console::tcsetattr (int a, struct termios const *t)
938 int res = output_tcsetattr (a, t);
941 return input_tcsetattr (a, t);
945 fhandler_console::tcgetattr (struct termios *t)
954 if (!GetConsoleMode (get_io_handle (), &flags))
961 if (flags & ENABLE_ECHO_INPUT)
964 if (flags & ENABLE_LINE_INPUT)
965 t->c_lflag |= ICANON;
967 if (flags & ENABLE_PROCESSED_INPUT)
970 /* What about ENABLE_WINDOW_INPUT
971 and ENABLE_MOUSE_INPUT ? */
973 /* All the output bits we can ignore */
976 syscall_printf ("%d = tcgetattr (%p) enable flags %p, t->lflag %p, t->iflag %p",
977 res, t, flags, t->c_lflag, t->c_iflag);
981 fhandler_console::fhandler_console () :
988 dev_console::set_color (HANDLE h)
994 WORD save_fg = win_fg;
995 win_fg = (win_bg & BACKGROUND_RED ? FOREGROUND_RED : 0) |
996 (win_bg & BACKGROUND_GREEN ? FOREGROUND_GREEN : 0) |
997 (win_bg & BACKGROUND_BLUE ? FOREGROUND_BLUE : 0) |
998 (win_bg & BACKGROUND_INTENSITY ? FOREGROUND_INTENSITY : 0);
999 win_bg = (save_fg & FOREGROUND_RED ? BACKGROUND_RED : 0) |
1000 (save_fg & FOREGROUND_GREEN ? BACKGROUND_GREEN : 0) |
1001 (save_fg & FOREGROUND_BLUE ? BACKGROUND_BLUE : 0) |
1002 (save_fg & FOREGROUND_INTENSITY ? BACKGROUND_INTENSITY : 0);
1005 /* apply attributes */
1007 win_fg = underline_color;
1008 /* emulate blink with bright background */
1010 win_bg |= BACKGROUND_INTENSITY;
1011 if (intensity == INTENSITY_INVISIBLE)
1013 else if (intensity != INTENSITY_BOLD)
1014 /* nothing to do */;
1015 /* apply foreground intensity only in non-reverse mode! */
1017 win_bg |= BACKGROUND_INTENSITY;
1019 win_fg |= FOREGROUND_INTENSITY;
1021 current_win32_attr = win_fg | win_bg;
1023 SetConsoleTextAttribute (h, current_win32_attr);
1026 #define FOREGROUND_ATTR_MASK (FOREGROUND_RED | FOREGROUND_GREEN | \
1027 FOREGROUND_BLUE | FOREGROUND_INTENSITY)
1028 #define BACKGROUND_ATTR_MASK (BACKGROUND_RED | BACKGROUND_GREEN | \
1029 BACKGROUND_BLUE | BACKGROUND_INTENSITY)
1031 dev_console::set_default_attr ()
1033 blink = underline = reverse = false;
1034 intensity = INTENSITY_NORMAL;
1035 fg = default_color & FOREGROUND_ATTR_MASK;
1036 bg = default_color & BACKGROUND_ATTR_MASK;
1041 * Clear the screen context from x1/y1 to x2/y2 cell.
1042 * Negative values represents current screen dimensions
1045 fhandler_console::clear_screen (int x1, int y1, int x2, int y2)
1051 dev_state->fillin_info (get_output_handle ());
1054 x1 = dev_state->info.dwWinSize.X - 1;
1056 y1 = dev_state->info.winBottom;
1058 x2 = dev_state->info.dwWinSize.X - 1;
1060 y2 = dev_state->info.winBottom;
1062 num = abs (y1 - y2) * dev_state->info.dwBufferSize.X + abs (x1 - x2) + 1;
1064 if ((y2 * dev_state->info.dwBufferSize.X + x2) > (y1 * dev_state->info.dwBufferSize.X + x1))
1074 FillConsoleOutputCharacterA (get_output_handle (), ' ',
1078 FillConsoleOutputAttribute (get_output_handle (),
1079 dev_state->current_win32_attr,
1086 fhandler_console::cursor_set (bool rel_to_top, int x, int y)
1090 dev_state->fillin_info (get_output_handle ());
1091 if (y > dev_state->info.winBottom)
1092 y = dev_state->info.winBottom;
1095 else if (rel_to_top)
1096 y += dev_state->info.winTop;
1098 if (x > dev_state->info.dwWinSize.X)
1099 x = dev_state->info.dwWinSize.X - 1;
1105 SetConsoleCursorPosition (get_output_handle (), pos);
1109 fhandler_console::cursor_rel (int x, int y)
1111 dev_state->fillin_info (get_output_handle ());
1112 x += dev_state->info.dwCursorPosition.X;
1113 y += dev_state->info.dwCursorPosition.Y;
1114 cursor_set (false, x, y);
1118 fhandler_console::cursor_get (int *x, int *y)
1120 dev_state->fillin_info (get_output_handle ());
1121 *y = dev_state->info.dwCursorPosition.Y;
1122 *x = dev_state->info.dwCursorPosition.X;
1125 /* VT100 line drawing graphics mode maps `abcdefghijklmnopqrstuvwxyz{|}~ to
1126 graphical characters */
1127 static wchar_t __vt100_conv [31] = {
1128 0x25C6, /* Black Diamond */
1129 0x2592, /* Medium Shade */
1130 0x2409, /* Symbol for Horizontal Tabulation */
1131 0x240C, /* Symbol for Form Feed */
1132 0x240D, /* Symbol for Carriage Return */
1133 0x240A, /* Symbol for Line Feed */
1134 0x00B0, /* Degree Sign */
1135 0x00B1, /* Plus-Minus Sign */
1136 0x2424, /* Symbol for Newline */
1137 0x240B, /* Symbol for Vertical Tabulation */
1138 0x2518, /* Box Drawings Light Up And Left */
1139 0x2510, /* Box Drawings Light Down And Left */
1140 0x250C, /* Box Drawings Light Down And Right */
1141 0x2514, /* Box Drawings Light Up And Right */
1142 0x253C, /* Box Drawings Light Vertical And Horizontal */
1143 0x23BA, /* Horizontal Scan Line-1 */
1144 0x23BB, /* Horizontal Scan Line-3 */
1145 0x2500, /* Box Drawings Light Horizontal */
1146 0x23BC, /* Horizontal Scan Line-7 */
1147 0x23BD, /* Horizontal Scan Line-9 */
1148 0x251C, /* Box Drawings Light Vertical And Right */
1149 0x2524, /* Box Drawings Light Vertical And Left */
1150 0x2534, /* Box Drawings Light Up And Horizontal */
1151 0x252C, /* Box Drawings Light Down And Horizontal */
1152 0x2502, /* Box Drawings Light Vertical */
1153 0x2264, /* Less-Than Or Equal To */
1154 0x2265, /* Greater-Than Or Equal To */
1155 0x03C0, /* Greek Small Letter Pi */
1156 0x2260, /* Not Equal To */
1157 0x00A3, /* Pound Sign */
1158 0x00B7, /* Middle Dot */
1162 bool fhandler_console::write_console (PWCHAR buf, DWORD len, DWORD& done)
1164 if (dev_state->iso_2022_G1
1165 ? dev_state->vt100_graphics_mode_G1
1166 : dev_state->vt100_graphics_mode_G0)
1167 for (DWORD i = 0; i < len; i ++)
1168 if (buf[i] >= (unsigned char) '`' && buf[i] <= (unsigned char) '~')
1169 buf[i] = __vt100_conv[buf[i] - (unsigned char) '`'];
1173 DWORD nbytes = len > MAX_WRITE_CHARS ? MAX_WRITE_CHARS : len;
1174 if (!WriteConsoleW (get_output_handle (), buf, nbytes, &done, 0))
1196 #define TAB 8 /* We should't let the console deal with these */
1202 static const char base_chars[256] =
1204 /*00 01 02 03 04 05 06 07 */ IGN, ERR, ERR, NOR, NOR, NOR, NOR, BEL,
1205 /*08 09 0A 0B 0C 0D 0E 0F */ BAK, TAB, DWN, ERR, ERR, CR, SO, SI,
1206 /*10 11 12 13 14 15 16 17 */ NOR, NOR, ERR, ERR, ERR, ERR, ERR, ERR,
1207 /*18 19 1A 1B 1C 1D 1E 1F */ NOR, NOR, ERR, ESC, ERR, ERR, ERR, ERR,
1208 /* ! " # $ % & ' */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1209 /*( ) * + , - . / */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1210 /*0 1 2 3 4 5 6 7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1211 /*8 9 : ; < = > ? */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1212 /*@ A B C D E F G */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1213 /*H I J K L M N O */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1214 /*P Q R S T U V W */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1215 /*X Y Z [ \ ] ^ _ */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1216 /*` a b c d e f g */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1217 /*h i j k l m n o */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1218 /*p q r s t u v w */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1219 /*x y z { | } ~ 7F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1220 /*80 81 82 83 84 85 86 87 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1221 /*88 89 8A 8B 8C 8D 8E 8F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1222 /*90 91 92 93 94 95 96 97 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1223 /*98 99 9A 9B 9C 9D 9E 9F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1224 /*A0 A1 A2 A3 A4 A5 A6 A7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1225 /*A8 A9 AA AB AC AD AE AF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1226 /*B0 B1 B2 B3 B4 B5 B6 B7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1227 /*B8 B9 BA BB BC BD BE BF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1228 /*C0 C1 C2 C3 C4 C5 C6 C7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1229 /*C8 C9 CA CB CC CD CE CF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1230 /*D0 D1 D2 D3 D4 D5 D6 D7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1231 /*D8 D9 DA DB DC DD DE DF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1232 /*E0 E1 E2 E3 E4 E5 E6 E7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1233 /*E8 E9 EA EB EC ED EE EF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1234 /*F0 F1 F2 F3 F4 F5 F6 F7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1235 /*F8 F9 FA FB FC FD FE FF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR };
1238 fhandler_console::char_command (char c)
1245 case 'm': /* Set Graphics Rendition */
1246 for (int i = 0; i <= dev_state->nargs_; i++)
1247 switch (dev_state->args_[i])
1249 case 0: /* normal color */
1250 dev_state->set_default_attr ();
1253 dev_state->intensity = INTENSITY_BOLD;
1256 dev_state->intensity = INTENSITY_DIM;
1258 case 4: /* underlined */
1259 dev_state->underline = 1;
1261 case 5: /* blink mode */
1262 dev_state->blink = true;
1264 case 7: /* reverse */
1265 dev_state->reverse = true;
1267 case 8: /* invisible */
1268 dev_state->intensity = INTENSITY_INVISIBLE;
1270 case 10: /* end alternate charset */
1271 dev_state->alternate_charset_active = false;
1273 case 11: /* start alternate charset */
1274 dev_state->alternate_charset_active = true;
1278 dev_state->intensity = INTENSITY_NORMAL;
1281 dev_state->underline = false;
1284 dev_state->blink = false;
1287 dev_state->reverse = false;
1289 case 30: /* BLACK foreground */
1292 case 31: /* RED foreground */
1293 dev_state->fg = FOREGROUND_RED;
1295 case 32: /* GREEN foreground */
1296 dev_state->fg = FOREGROUND_GREEN;
1298 case 33: /* YELLOW foreground */
1299 dev_state->fg = FOREGROUND_RED | FOREGROUND_GREEN;
1301 case 34: /* BLUE foreground */
1302 dev_state->fg = FOREGROUND_BLUE;
1304 case 35: /* MAGENTA foreground */
1305 dev_state->fg = FOREGROUND_RED | FOREGROUND_BLUE;
1307 case 36: /* CYAN foreground */
1308 dev_state->fg = FOREGROUND_BLUE | FOREGROUND_GREEN;
1310 case 37: /* WHITE foreg */
1311 dev_state->fg = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
1314 dev_state->fg = dev_state->default_color & FOREGROUND_ATTR_MASK;
1316 case 40: /* BLACK background */
1319 case 41: /* RED background */
1320 dev_state->bg = BACKGROUND_RED;
1322 case 42: /* GREEN background */
1323 dev_state->bg = BACKGROUND_GREEN;
1325 case 43: /* YELLOW background */
1326 dev_state->bg = BACKGROUND_RED | BACKGROUND_GREEN;
1328 case 44: /* BLUE background */
1329 dev_state->bg = BACKGROUND_BLUE;
1331 case 45: /* MAGENTA background */
1332 dev_state->bg = BACKGROUND_RED | BACKGROUND_BLUE;
1334 case 46: /* CYAN background */
1335 dev_state->bg = BACKGROUND_BLUE | BACKGROUND_GREEN;
1337 case 47: /* WHITE background */
1338 dev_state->bg = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
1341 dev_state->bg = dev_state->default_color & BACKGROUND_ATTR_MASK;
1344 dev_state->set_color (get_output_handle ());
1348 if (!dev_state->saw_question_mark)
1350 switch (dev_state->args_[0])
1352 case 4: /* Insert mode */
1353 dev_state->insert_mode = (c == 'h') ? true : false;
1354 syscall_printf ("insert mode %sabled", dev_state->insert_mode ? "en" : "dis");
1359 switch (dev_state->args_[0])
1361 case 47: /* Save/Restore screen */
1362 if (c == 'h') /* save */
1364 CONSOLE_SCREEN_BUFFER_INFO now;
1365 COORD cob = { 0, 0 };
1367 if (!GetConsoleScreenBufferInfo (get_output_handle (), &now))
1370 dev_state->savebufsiz.X = now.srWindow.Right - now.srWindow.Left + 1;
1371 dev_state->savebufsiz.Y = now.srWindow.Bottom - now.srWindow.Top + 1;
1373 if (dev_state->savebuf)
1374 cfree (dev_state->savebuf);
1375 dev_state->savebuf = (PCHAR_INFO) cmalloc_abort (HEAP_1_BUF, sizeof (CHAR_INFO) *
1376 dev_state->savebufsiz.X * dev_state->savebufsiz.Y);
1378 ReadConsoleOutputW (get_output_handle (), dev_state->savebuf,
1379 dev_state->savebufsiz, cob, &now.srWindow);
1383 CONSOLE_SCREEN_BUFFER_INFO now;
1384 COORD cob = { 0, 0 };
1386 if (!GetConsoleScreenBufferInfo (get_output_handle (), &now))
1389 if (!dev_state->savebuf)
1392 WriteConsoleOutputW (get_output_handle (), dev_state->savebuf,
1393 dev_state->savebufsiz, cob, &now.srWindow);
1395 cfree (dev_state->savebuf);
1396 dev_state->savebuf = NULL;
1397 dev_state->savebufsiz.X = dev_state->savebufsiz.Y = 0;
1401 case 67: /* DECBKM ("DEC Backarrow Key Mode") */
1402 dev_state->backspace_keycode = (c == 'h' ? CTRL('H') : CERASE);
1405 case 1000: /* Mouse tracking */
1406 dev_state->use_mouse = (c == 'h') ? 1 : 0;
1407 syscall_printf ("mouse support set to mode %d", dev_state->use_mouse);
1410 case 1002: /* Mouse button event tracking */
1411 dev_state->use_mouse = (c == 'h') ? 2 : 0;
1412 syscall_printf ("mouse support set to mode %d", dev_state->use_mouse);
1415 case 1003: /* Mouse any event tracking */
1416 dev_state->use_mouse = (c == 'h') ? 3 : 0;
1417 syscall_printf ("mouse support set to mode %d", dev_state->use_mouse);
1420 case 1004: /* Focus in/out event reporting */
1421 dev_state->use_focus = (c == 'h') ? true : false;
1422 syscall_printf ("focus reporting set to %d", dev_state->use_focus);
1425 case 2000: /* Raw keyboard mode */
1426 set_raw_win32_keyboard_mode ((c == 'h') ? true : false);
1429 default: /* Ignore */
1430 syscall_printf ("unknown h/l command: %d", dev_state->args_[0]);
1435 switch (dev_state->args_[0])
1437 case 0: /* Clear to end of screen */
1438 cursor_get (&x, &y);
1439 clear_screen (x, y, -1, -1);
1441 case 1: /* Clear from beginning of screen to cursor */
1442 cursor_get (&x, &y);
1443 clear_screen (0, 0, x, y);
1445 case 2: /* Clear screen */
1446 clear_screen (0, 0, -1, -1);
1447 cursor_set (true, 0,0);
1455 cursor_rel (0, -(dev_state->args_[0] ? dev_state->args_[0] : 1));
1458 cursor_rel (0, dev_state->args_[0] ? dev_state->args_[0] : 1);
1461 cursor_rel (dev_state->args_[0] ? dev_state->args_[0] : 1, 0);
1464 cursor_rel (-(dev_state->args_[0] ? dev_state->args_[0] : 1),0);
1467 switch (dev_state->args_[0])
1469 case 0: /* Clear to end of line */
1470 cursor_get (&x, &y);
1471 clear_screen (x, y, -1, y);
1473 case 2: /* Clear line */
1474 cursor_get (&x, &y);
1475 clear_screen (0, y, -1, y);
1477 case 1: /* Clear from bol to cursor */
1478 cursor_get (&x, &y);
1479 clear_screen (0, y, x, y);
1487 cursor_set (true, (dev_state->args_[1] ? dev_state->args_[1] : 1) - 1,
1488 (dev_state->args_[0] ? dev_state->args_[0] : 1) - 1);
1490 case 'G': /* hpa - position cursor at column n - 1 */
1491 cursor_get (&x, &y);
1492 cursor_set (false, (dev_state->args_[0] ? dev_state->args_[0] - 1 : 0), y);
1494 case 'd': /* vpa - position cursor at line n */
1495 cursor_get (&x, &y);
1496 cursor_set (true, x, (dev_state->args_[0] ? dev_state->args_[0] - 1 : 0));
1498 case 's': /* Save cursor position */
1499 cursor_get (&dev_state->savex, &dev_state->savey);
1500 dev_state->savey -= dev_state->info.winTop;
1502 case 'u': /* Restore cursor position */
1503 cursor_set (true, dev_state->savex, dev_state->savey);
1506 cursor_get (&x, &y);
1507 cursor_set (false, 8 * (x / 8 + 1), y);
1509 case 'L': /* AL - insert blank lines */
1510 dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
1511 cursor_get (&x, &y);
1512 scroll_screen (0, y, -1, -1, 0, y + dev_state->args_[0]);
1514 case 'M': /* DL - delete lines */
1515 dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
1516 cursor_get (&x, &y);
1517 scroll_screen (0, y + dev_state->args_[0], -1, -1, 0, y);
1519 case '@': /* IC - insert chars */
1520 dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
1521 cursor_get (&x, &y);
1522 scroll_screen (x, y, -1, y, x + dev_state->args_[0], y);
1524 case 'P': /* DC - delete chars */
1525 dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
1526 cursor_get (&x, &y);
1527 scroll_screen (x + dev_state->args_[0], y, -1, y, x, y);
1529 case 'S': /* SF - Scroll forward */
1530 dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
1531 scroll_screen (0, dev_state->args_[0], -1, -1, 0, 0);
1533 case 'T': /* SR - Scroll down */
1534 dev_state->fillin_info (get_output_handle ());
1535 dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
1536 scroll_screen (0, 0, -1, -1, 0, dev_state->info.winTop + dev_state->args_[0]);
1538 case 'X': /* ec - erase chars */
1539 dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
1540 cursor_get (&x, &y);
1541 scroll_screen (x + dev_state->args_[0], y, -1, y, x, y);
1542 scroll_screen (x, y, -1, y, x + dev_state->args_[0], y);
1544 case 'Z': /* Back tab */
1545 cursor_get (&x, &y);
1546 cursor_set (false, ((8 * (x / 8 + 1)) - 8), y);
1548 case 'b': /* Repeat char #1 #2 times */
1549 if (dev_state->insert_mode)
1551 cursor_get (&x, &y);
1552 scroll_screen (x, y, -1, y, x + dev_state->args_[1], y);
1554 while (dev_state->args_[1]--)
1555 WriteFile (get_output_handle (), &dev_state->args_[0], 1, (DWORD *) &x, 0);
1557 case 'c': /* u9 - Terminal enquire string */
1558 if (dev_state->saw_greater_than_sign)
1559 /* Generate Secondary Device Attribute report, using 67 = ASCII 'C'
1560 to indicate Cygwin (convention used by Rxvt, Urxvt, Screen, Mintty),
1561 and cygwin version for terminal version. */
1562 __small_sprintf (buf, "\033[>67;%d%02d;0c", CYGWIN_VERSION_DLL_MAJOR, CYGWIN_VERSION_DLL_MINOR);
1564 strcpy (buf, "\033[?6c");
1565 /* The generated report needs to be injected for read-ahead into the
1566 fhandler_console object associated with standard input.
1567 The current call does not work. */
1568 puts_readahead (buf);
1571 switch (dev_state->args_[0])
1573 case 6: /* u7 - Cursor position request */
1574 cursor_get (&x, &y);
1575 y -= dev_state->info.winTop;
1576 /* x -= dev_state->info.winLeft; // not available yet */
1577 __small_sprintf (buf, "\033[%d;%dR", y + 1, x + 1);
1578 puts_readahead (buf);
1584 case 'r': /* Set Scroll region */
1585 dev_state->scroll_region.Top = dev_state->args_[0] ? dev_state->args_[0] - 1 : 0;
1586 dev_state->scroll_region.Bottom = dev_state->args_[1] ? dev_state->args_[1] - 1 : -1;
1587 cursor_set (true, 0, 0);
1589 case 'g': /* TAB set/clear */
1597 /* This gets called when we found an invalid input character. We just
1598 print a half filled square (UTF 0x2592). We have no chance to figure
1599 out the "meaning" of the input char anyway. */
1601 fhandler_console::write_replacement_char ()
1603 static const wchar_t replacement_char = 0x2592; /* Half filled square */
1605 WriteConsoleW (get_output_handle (), &replacement_char, 1, &done, 0);
1608 const unsigned char *
1609 fhandler_console::write_normal (const unsigned char *src,
1610 const unsigned char *end)
1612 /* Scan forward to see what a char which needs special treatment */
1615 const unsigned char *found = src;
1618 UINT cp = dev_state->get_console_cp ();
1619 const char *charset;
1624 /* The alternate charset is always 437, just as in the Linux console. */
1625 f_mbtowc = __cp_mbtowc;
1630 f_mbtowc = cygheap->locale.mbtowc;
1631 charset = cygheap->locale.charset;
1634 /* First check if we have cached lead bytes of a former try to write
1635 a truncated multibyte sequence. If so, process it. */
1638 const unsigned char *nfound;
1639 int cp_len = min (end - src, 4 - trunc_buf.len);
1640 memcpy (trunc_buf.buf + trunc_buf.len, src, cp_len);
1641 memset (&ps, 0, sizeof ps);
1642 switch (ret = f_mbtowc (_REENT, NULL, (const char *) trunc_buf.buf,
1643 trunc_buf.len + cp_len, charset, &ps))
1646 /* Still truncated multibyte sequence? Keep in trunc_buf. */
1647 trunc_buf.len += cp_len;
1650 /* Give up, print replacement chars for trunc_buf... */
1651 for (int i = 0; i < trunc_buf.len; ++i)
1652 write_replacement_char ();
1653 /* ... mark trunc_buf as unused... */
1655 /* ... and proceed. */
1659 nfound = trunc_buf.buf + 1;
1662 nfound = trunc_buf.buf + ret;
1665 /* Valid multibyte sequence? Process. */
1668 buf_len = dev_state->str_to_con (f_mbtowc, charset, write_buf,
1669 (const char *) trunc_buf.buf,
1670 nfound - trunc_buf.buf);
1671 if (!write_console (write_buf, buf_len, done))
1673 debug_printf ("multibyte sequence write failed, handle %p", get_output_handle ());
1676 found = src + (nfound - trunc_buf.buf - trunc_buf.len);
1682 /* Loop over src buffer as long as we have just simple characters. Stop
1683 as soon as we reach the conversion limit, or if we encounter a control
1684 character or a truncated or invalid mutibyte sequence. */
1685 memset (&ps, 0, sizeof ps);
1687 && found - src < CONVERT_LIMIT
1688 && base_chars[*found] == NOR)
1690 switch (ret = f_mbtowc (_REENT, NULL, (const char *) found,
1691 end - found, charset, &ps))
1693 case -2: /* Truncated multibyte sequence. Store for next write. */
1694 trunc_buf.len = end - found;
1695 memcpy (trunc_buf.buf, found, trunc_buf.len);
1697 case -1: /* Invalid multibyte sequence. Handled below. */
1710 /* Print all the base characters out */
1713 DWORD len = found - src;
1714 buf_len = dev_state->str_to_con (f_mbtowc, charset, write_buf,
1715 (const char *) src, len);
1718 debug_printf ("conversion error, handle %p",
1719 get_output_handle ());
1724 if (dev_state->insert_mode)
1727 cursor_get (&x, &y);
1728 scroll_screen (x, y, -1, y, x + buf_len, y);
1731 if (!write_console (write_buf, buf_len, done))
1733 debug_printf ("write failed, handle %p", get_output_handle ());
1736 /* Stop here if we reached the conversion limit. */
1737 if (len >= CONVERT_LIMIT)
1738 return found + trunc_buf.len;
1740 /* If there's still something in the src buffer, but it's not a truncated
1741 multibyte sequence, then we stumbled over a control character or an
1742 invalid multibyte sequence. Print it. */
1743 if (found < end && trunc_buf.len == 0)
1746 switch (base_chars[*found])
1748 case SO: /* Shift Out: Invoke G1 character set (ISO 2022) */
1749 dev_state->iso_2022_G1 = true;
1751 case SI: /* Shift In: Invoke G0 character set (ISO 2022) */
1752 dev_state->iso_2022_G1 = false;
1758 dev_state->state_ = gotesc;
1761 cursor_get (&x, &y);
1764 if (y >= dev_state->info.winBottom && !dev_state->scroll_region.Top)
1765 WriteConsoleW (get_output_handle (), L"\n", 1, &done, 0);
1768 scroll_screen (0, srTop + 1, -1, srBottom, 0, srTop);
1772 cursor_set (false, ((tc->ti.c_oflag & ONLCR) ? 0 : x), y + 1);
1781 cursor_get (&x, &y);
1782 cursor_set (false, 0, y);
1785 /* Don't print chars marked as ERR chars, except for a ASCII CAN
1786 sequence which is printed as singlebyte chars from the UTF
1787 Basic Latin and Latin 1 Supplement plains. */
1790 write_replacement_char ();
1791 if (found + 1 < end)
1793 ret = __utf8_mbtowc (_REENT, NULL, (const char *) found + 1,
1794 end - found - 1, NULL, &ps);
1795 if (ret != (size_t) -1)
1798 WCHAR w = *(found + 1);
1799 WriteConsoleW (get_output_handle (), &w, 1, &done, 0);
1806 cursor_get (&x, &y);
1807 cursor_set (false, 8 * (x / 8 + 1), y);
1810 write_replacement_char ();
1815 return found + trunc_buf.len;
1819 fhandler_console::write (const void *vsrc, size_t len)
1821 /* Run and check for ansi sequences */
1822 unsigned const char *src = (unsigned char *) vsrc;
1823 unsigned const char *end = src + len;
1824 /* This might look a bit far fetched, but using the TLS path buffer allows
1825 to allocate a big buffer without using the stack too much. Doing it here
1826 in write instead of in write_normal should be faster, too. */
1828 write_buf = tp.w_get ();
1830 debug_printf ("%x, %d", vsrc, len);
1834 debug_printf ("at %d(%c) state is %d", *src, isprint (*src) ? *src : ' ',
1836 switch (dev_state->state_)
1839 src = write_normal (src, end);
1840 if (!src) /* write_normal failed */
1844 if (*src == '[') /* CSI Control Sequence Introducer */
1846 dev_state->state_ = gotsquare;
1847 dev_state->saw_question_mark = false;
1848 dev_state->saw_greater_than_sign = false;
1849 for (dev_state->nargs_ = 0; dev_state->nargs_ < MAXARGS; dev_state->nargs_++)
1850 dev_state->args_[dev_state->nargs_] = 0;
1851 dev_state->nargs_ = 0;
1853 else if (*src == ']') /* OSC Operating System Command */
1855 dev_state->rarg = 0;
1856 dev_state->my_title_buf[0] = '\0';
1857 dev_state->state_ = gotrsquare;
1859 else if (*src == '(') /* Designate G0 character set */
1861 dev_state->state_ = gotparen;
1863 else if (*src == ')') /* Designate G1 character set */
1865 dev_state->state_ = gotrparen;
1867 else if (*src == 'M') /* Reverse Index (scroll down) */
1869 dev_state->fillin_info (get_output_handle ());
1870 scroll_screen (0, 0, -1, -1, 0, dev_state->info.winTop + 1);
1871 dev_state->state_ = normal;
1873 else if (*src == 'c') /* RIS Full Reset */
1875 dev_state->set_default_attr ();
1876 dev_state->vt100_graphics_mode_G0 = false;
1877 dev_state->vt100_graphics_mode_G1 = false;
1878 dev_state->iso_2022_G1 = false;
1879 clear_screen (0, 0, -1, -1);
1880 cursor_set (true, 0, 0);
1881 dev_state->state_ = normal;
1883 else if (*src == '8') /* DECRC Restore cursor position */
1885 cursor_set (true, dev_state->savex, dev_state->savey);
1886 dev_state->state_ = normal;
1888 else if (*src == '7') /* DECSC Save cursor position */
1890 cursor_get (&dev_state->savex, &dev_state->savey);
1891 dev_state->savey -= dev_state->info.winTop;
1892 dev_state->state_ = normal;
1894 else if (*src == 'R') /* ? */
1895 dev_state->state_ = normal;
1898 dev_state->state_ = normal;
1905 dev_state->args_[dev_state->nargs_] = dev_state->args_[dev_state->nargs_] * 10 + *src - '0';
1908 else if (*src == ';')
1911 dev_state->nargs_++;
1912 if (dev_state->nargs_ >= MAXARGS)
1913 dev_state->nargs_--;
1917 dev_state->state_ = gotcommand;
1921 char_command (*src++);
1922 dev_state->state_ = normal;
1926 dev_state->rarg = dev_state->rarg * 10 + (*src - '0');
1927 else if (*src == ';' && (dev_state->rarg == 2 || dev_state->rarg == 0))
1928 dev_state->state_ = gettitle;
1930 dev_state->state_ = eattitle;
1936 int n = strlen (dev_state->my_title_buf);
1939 if (*src == '\007' && dev_state->state_ == gettitle)
1942 strcpy (old_title, dev_state->my_title_buf);
1943 set_console_title (dev_state->my_title_buf);
1945 dev_state->state_ = normal;
1947 else if (n < TITLESIZE)
1949 dev_state->my_title_buf[n++] = *src;
1950 dev_state->my_title_buf[n] = '\0';
1958 dev_state->state_ = gotarg1;
1959 dev_state->nargs_++;
1962 else if (isalpha (*src))
1963 dev_state->state_ = gotcommand;
1964 else if (*src != '@' && !isalpha (*src) && !isdigit (*src))
1967 dev_state->saw_question_mark = true;
1968 else if (*src == '>')
1969 dev_state->saw_greater_than_sign = true;
1970 /* ignore any extra chars between [ and first arg or command */
1974 dev_state->state_ = gotarg1;
1976 case gotparen: /* Designate G0 Character Set (ISO 2022) */
1978 dev_state->vt100_graphics_mode_G0 = true;
1980 dev_state->vt100_graphics_mode_G0 = false;
1981 dev_state->state_ = normal;
1984 case gotrparen: /* Designate G1 Character Set (ISO 2022) */
1986 dev_state->vt100_graphics_mode_G1 = true;
1988 dev_state->vt100_graphics_mode_G1 = false;
1989 dev_state->state_ = normal;
1995 syscall_printf ("%d = fhandler_console::write (...)", len);
2003 } keytable[] NO_COPY = {
2004 /* NORMAL */ /* SHIFT */ /* CTRL */ /* CTRL-SHIFT */
2005 /* Unmodified and Alt-modified keypad keys comply with linux console
2006 SHIFT, CTRL, CTRL-SHIFT modifiers comply with xterm modifier usage */
2007 {VK_NUMPAD5, {"\033[G", "\033[1;2G", "\033[1;5G", "\033[1;6G"}},
2008 {VK_CLEAR, {"\033[G", "\033[1;2G", "\033[1;5G", "\033[1;6G"}},
2009 {VK_LEFT, {"\033[D", "\033[1;2D", "\033[1;5D", "\033[1;6D"}},
2010 {VK_RIGHT, {"\033[C", "\033[1;2C", "\033[1;5C", "\033[1;6C"}},
2011 {VK_UP, {"\033[A", "\033[1;2A", "\033[1;5A", "\033[1;6A"}},
2012 {VK_DOWN, {"\033[B", "\033[1;2B", "\033[1;5B", "\033[1;6B"}},
2013 {VK_PRIOR, {"\033[5~", "\033[5;2~", "\033[5;5~", "\033[5;6~"}},
2014 {VK_NEXT, {"\033[6~", "\033[6;2~", "\033[6;5~", "\033[6;6~"}},
2015 {VK_HOME, {"\033[1~", "\033[1;2~", "\033[1;5~", "\033[1;6~"}},
2016 {VK_END, {"\033[4~", "\033[4;2~", "\033[4;5~", "\033[4;6~"}},
2017 {VK_INSERT, {"\033[2~", "\033[2;2~", "\033[2;5~", "\033[2;6~"}},
2018 {VK_DELETE, {"\033[3~", "\033[3;2~", "\033[3;5~", "\033[3;6~"}},
2019 /* F1...F12, SHIFT-F1...SHIFT-F10 comply with linux console
2020 F6...F12, and all modified F-keys comply with rxvt (compatible extension) */
2021 {VK_F1, {"\033[[A", "\033[23~", "\033[11^", "\033[23^"}},
2022 {VK_F2, {"\033[[B", "\033[24~", "\033[12^", "\033[24^"}},
2023 {VK_F3, {"\033[[C", "\033[25~", "\033[13^", "\033[25^"}},
2024 {VK_F4, {"\033[[D", "\033[26~", "\033[14^", "\033[26^"}},
2025 {VK_F5, {"\033[[E", "\033[28~", "\033[15^", "\033[28^"}},
2026 {VK_F6, {"\033[17~", "\033[29~", "\033[17^", "\033[29^"}},
2027 {VK_F7, {"\033[18~", "\033[31~", "\033[18^", "\033[31^"}},
2028 {VK_F8, {"\033[19~", "\033[32~", "\033[19^", "\033[32^"}},
2029 {VK_F9, {"\033[20~", "\033[33~", "\033[20^", "\033[33^"}},
2030 {VK_F10, {"\033[21~", "\033[34~", "\033[21^", "\033[34^"}},
2031 {VK_F11, {"\033[23~", "\033[23$", "\033[23^", "\033[23@"}},
2032 {VK_F12, {"\033[24~", "\033[24$", "\033[24^", "\033[24@"}},
2033 /* CTRL-6 complies with Windows cmd console but should be fixed */
2034 {'6', {NULL, NULL, "\036", NULL}},
2035 /* Table end marker */
2040 get_nonascii_key (INPUT_RECORD& input_rec, char *tmp)
2045 /*#define CONTROLSHIFT 3*/
2047 int modifier_index = NORMAL;
2048 if (input_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)
2049 modifier_index = SHIFT;
2050 if (input_rec.Event.KeyEvent.dwControlKeyState &
2051 (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
2052 modifier_index += CONTROL;
2054 for (int i = 0; keytable[i].vk; i++)
2055 if (input_rec.Event.KeyEvent.wVirtualKeyCode == keytable[i].vk)
2057 if ((input_rec.Event.KeyEvent.dwControlKeyState &
2058 (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
2059 && keytable[i].val[modifier_index] != NULL)
2060 { /* Generic ESC prefixing if Alt is pressed */
2062 strcpy (tmp + 1, keytable[i].val[modifier_index]);
2066 return keytable[i].val[modifier_index];
2069 if (input_rec.Event.KeyEvent.uChar.AsciiChar)
2071 tmp[0] = input_rec.Event.KeyEvent.uChar.AsciiChar;
2079 fhandler_console::init (HANDLE f, DWORD a, mode_t bin)
2081 // this->fhandler_termios::init (f, mode, bin);
2082 /* Ensure both input and output console handles are open */
2085 a &= GENERIC_READ | GENERIC_WRITE;
2086 if (a == GENERIC_READ)
2088 if (a == GENERIC_WRITE)
2090 if (a == (GENERIC_READ | GENERIC_WRITE))
2092 open (flags | O_BINARY);
2093 if (f != INVALID_HANDLE_VALUE)
2094 CloseHandle (f); /* Reopened by open */
2096 return !tcsetattr (0, &tc->ti);
2100 fhandler_console::igncr_enabled ()
2102 return tc->ti.c_iflag & IGNCR;
2106 fhandler_console::set_close_on_exec (bool val)
2108 fhandler_base::set_close_on_exec (val);
2109 set_no_inheritance (output_handle, val);
2113 set_console_title (char *title)
2115 wchar_t buf[TITLESIZE + 1];
2116 sys_mbstowcs (buf, TITLESIZE + 1, title);
2117 lock_ttys here (15000);
2118 SetConsoleTitleW (buf);
2119 debug_printf ("title '%W'", buf);
2123 fhandler_console::fixup_after_fork_exec (bool execing)
2125 HANDLE h = get_handle ();
2126 HANDLE oh = get_output_handle ();
2128 if ((execing && close_on_exec ()) || open (O_NOCTTY | get_flags (), 0))
2129 cygheap->manage_console_count ("fhandler_console::fixup_after_fork_exec", -1);
2132 if (!get_io_handle ())
2133 system_printf ("error opening input console handle for %s after fork/exec, errno %d, %E", get_name (), get_errno ());
2134 if (!get_output_handle ())
2135 system_printf ("error opening output console handle for %s after fork/exec, errno %d, %E", get_name (), get_errno ());
2138 if (!close_on_exec ())
2145 bool NO_COPY fhandler_console::invisible_console;
2147 // #define WINSTA_ACCESS (WINSTA_READATTRIBUTES | STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | WINSTA_CREATEDESKTOP | WINSTA_EXITWINDOWS)
2148 #define WINSTA_ACCESS WINSTA_ALL_ACCESS
2150 /* Create a console in an invisible window station. This should work
2151 in all versions of Windows NT except Windows 7 (so far). */
2153 fhandler_console::create_invisible_console (HWINSTA horig)
2155 HWINSTA h = CreateWindowStationW (NULL, 0, WINSTA_ACCESS, NULL);
2156 termios_printf ("%p = CreateWindowStation(NULL), %E", h);
2160 b = SetProcessWindowStation (h);
2161 termios_printf ("SetProcessWindowStation %d, %E", b);
2163 b = AllocConsole (); /* will cause flashing if CreateWindowStation
2166 SetParent (GetConsoleWindow (), HWND_MESSAGE);
2167 if (horig && h && h != horig && SetProcessWindowStation (horig))
2168 CloseWindowStation (h);
2169 termios_printf ("%d = AllocConsole (), %E", b);
2170 invisible_console = true;
2174 /* Ugly workaround for Windows 7.
2176 First try to just attach to any console which may have started this
2177 app. If that works use this as our "invisible console".
2179 This will fail if not started from the command prompt. In that case, start
2180 a dummy console application in a hidden state so that we can use its console
2181 as our invisible console. This probably works everywhere but process
2182 creation is slow and to be avoided if possible so the window station method
2183 is vastly preferred.
2185 FIXME: This is not completely thread-safe since it creates two inheritable
2186 handles which are known only to this function. If another thread starts
2187 a process the new process will inherit these handles. However, since this
2188 function is currently only called at startup and during exec, it shouldn't
2191 fhandler_console::create_invisible_console_workaround ()
2193 if (!AttachConsole (-1))
2196 DWORD err = GetLastError ();
2197 path_conv helper ("/bin/cygwin-console-helper.exe");
2198 HANDLE hello = NULL;
2199 HANDLE goodbye = NULL;
2200 /* If err == ERROR_PROC_FOUND then this method won't work. But that's
2201 ok. The window station method should work ok when AttachConsole doesn't
2204 If the helper doesn't exist or we can't create event handles then we
2205 can't use this method. */
2206 if (err == ERROR_PROC_NOT_FOUND || !helper.exists ()
2207 || !(hello = CreateEvent (&sec_none, true, false, NULL))
2208 || !(goodbye = CreateEvent (&sec_none, true, false, NULL)))
2210 AllocConsole (); /* This is just sanity check code. We should
2211 never actually hit here unless we're running
2212 in an environment which lacks the helper
2218 STARTUPINFOW si = {};
2219 PROCESS_INFORMATION pi;
2220 size_t len = helper.get_wide_win32_path_len ();
2221 WCHAR cmd[len + (2 * strlen (" 0xffffffff")) + 1];
2222 WCHAR title[] = L"invisible cygwin console";
2224 helper.get_wide_win32_path (cmd);
2225 __small_swprintf (cmd + len, L" %p %p", hello, goodbye);
2227 si.cb = sizeof (si);
2228 si.dwFlags = STARTF_USESHOWWINDOW;
2229 si.wShowWindow = SW_HIDE;
2232 /* Create a new hidden process. Use the two event handles as
2233 argv[1] and argv[2]. */
2234 BOOL x = CreateProcessW (NULL, cmd, &sec_none_nih, &sec_none_nih, true,
2235 CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
2238 CloseHandle (pi.hProcess); /* Don't need */
2239 CloseHandle (pi.hThread); /* these. */
2242 /* Wait for subprocess to indicate that it is live. This may not
2243 actually be needed but it's hard to say since it is possible that
2244 there will be no console for a brief time after the process
2245 returns and there is no easy way to determine if/when this happens
2246 in Windows. So play it safe. */
2247 if (!x || (WaitForSingleObject (hello, 10000) != WAIT_OBJECT_0)
2248 || !AttachConsole (pi.dwProcessId))
2249 AllocConsole (); /* Oh well. Watch the flash. */
2253 /* Setting the owner of the console window to HWND_MESSAGE seems to
2254 hide it from the taskbar. Don't know if this method is faster than
2255 calling ShowWindowAsync but it should guarantee no taskbar presence
2256 for the hidden console. */
2257 SetParent (GetConsoleWindow (), HWND_MESSAGE);
2259 CloseHandle (hello);
2262 SetEvent (goodbye); /* Tell helper process it's ok to exit. */
2263 CloseHandle (goodbye);
2266 return invisible_console = true;
2270 fhandler_console::need_invisible ()
2273 if (GetConsoleCP ())
2274 invisible_console = false;
2278 /* The intent here is to allocate an "invisible" console if we have no
2279 controlling tty or to reuse the existing console if we already have
2280 a tty. So, first get the old window station. If there is no controlling
2281 terminal, create a new window station and then set it as the current
2282 window station. The subsequent AllocConsole will then be allocated
2283 invisibly. But, after doing that we have to restore any existing windows
2284 station or, strangely, characters will not be displayed in any windows
2285 drawn on the current screen. We only do this if we have changed to
2286 a new window station and if we had an existing windows station previously.
2287 We also close the previously opened window station even though AllocConsole
2288 is now "using" it. This doesn't seem to cause any problems.
2290 Things to watch out for if you make changes in this code:
2292 - Flashing, black consoles showing up when you start, e.g., ssh in
2294 - Non-displaying of characters in rxvt or xemacs if you start a
2295 process using setsid: bash -lc "setsid rxvt". */
2297 h = GetProcessWindowStation ();
2302 || !GetUserObjectInformationW (h, UOI_FLAGS, &oi, sizeof (oi), &len)
2303 || !(oi.dwFlags & WSF_VISIBLE))
2306 debug_printf ("window station is not visible");
2308 invisible_console = true;
2310 else if (wincap.has_broken_alloc_console ())
2311 b = create_invisible_console_workaround ();
2313 b = create_invisible_console (h);
2316 debug_printf ("invisible_console %d", invisible_console);