/* fhandler_console.cc Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc. This file is part of Cygwin. This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" #include <sys/termios.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <wingdi.h> #include <winuser.h> #include <wincon.h> #include <winnls.h> #include <ctype.h> #include <sys/cygwin.h> #include "cygerrno.h" #include "security.h" #include "path.h" #include "fhandler.h" #include "dtable.h" #include "cygheap.h" #include "sigproc.h" #include "pinfo.h" #include "shared_info.h" #include "cygtls.h" #define CONVERT_LIMIT 16384 static bool cp_convert (UINT destcp, char *dest, UINT srccp, const char *src, DWORD size) { if (!size) /* no action */; else if (destcp == srccp) { if (dest != src) memcpy (dest, src, size); } else { WCHAR wbuffer[CONVERT_LIMIT]; /* same size as the maximum input, s.b. */ if (!MultiByteToWideChar (srccp, 0, src, size, wbuffer, sizeof (wbuffer))) return false; if (!WideCharToMultiByte (destcp, 0, wbuffer, size, dest, size, NULL, NULL)) return false; } return true; } /* * Scroll the screen context. * x1, y1 - ul corner * x2, y2 - dr corner * xn, yn - new ul corner * Negative values represents current screen dimensions */ #define srTop (dev_state->info.winTop + dev_state->scroll_region.Top) #define srBottom ((dev_state->scroll_region.Bottom < 0) ? dev_state->info.winBottom : dev_state->info.winTop + dev_state->scroll_region.Bottom) #define use_tty ISSTATE (myself, PID_USETTY) const char * get_nonascii_key (INPUT_RECORD&, char *); static console_state NO_COPY *shared_console_info; dev_console NO_COPY *fhandler_console::dev_state; /* Allocate and initialize the shared record for the current console. Returns a pointer to shared_console_info. */ tty_min * fhandler_console::get_tty_stuff (int flags = 0) { if (dev_state) return &shared_console_info->tty_min_state; shared_console_info = (console_state *) open_shared (NULL, 0, cygheap->console_h, sizeof (*shared_console_info), SH_SHARED_CONSOLE); dev_state = &shared_console_info->dev_state; ProtectHandleINH (cygheap->console_h); if (!shared_console_info->tty_min_state.ntty) { shared_console_info->tty_min_state.setntty (TTY_CONSOLE); shared_console_info->tty_min_state.setsid (myself->sid); myself->set_ctty (&shared_console_info->tty_min_state, flags, NULL); dev_state->scroll_region.Bottom = -1; dev_state->dwLastCursorPosition.X = -1; dev_state->dwLastCursorPosition.Y = -1; dev_state->default_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; dev_state->underline_color = FOREGROUND_GREEN | FOREGROUND_BLUE; dev_state->dim_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; dev_state->meta_mask = LEFT_ALT_PRESSED; /* Set the mask that determines if an input keystroke is modified by META. We set this based on the keyboard layout language loaded for the current thread. The left <ALT> key always generates META, but the right <ALT> key only generates META if we are using an English keyboard because many "international" keyboards replace common shell symbols ('[', '{', etc.) with accented language-specific characters (umlaut, accent grave, etc.). On these keyboards right <ALT> (called AltGr) is used to produce the shell symbols and should not be interpreted as META. */ if (PRIMARYLANGID (LOWORD (GetKeyboardLayout (0))) == LANG_ENGLISH) dev_state->meta_mask |= RIGHT_ALT_PRESSED; } return &shared_console_info->tty_min_state; } void set_console_ctty () { (void) fhandler_console::get_tty_stuff (); } /* Return the tty structure associated with a given tty number. If the tty number is < 0, just return a dummy record. */ tty_min * tty_list::get_tty (int n) { static tty_min nada; if (n == TTY_CONSOLE) return fhandler_console::get_tty_stuff (); else if (n >= 0) return &cygwin_shared->tty.ttys[n]; else return &nada; } /* Determine if a console is associated with this process prior to a spawn. If it is, then we'll return 1. If the console has been initialized, then set it into a more friendly state for non-cygwin apps. */ int __stdcall set_console_state_for_spawn () { HANDLE h = CreateFile ("CONIN$", GENERIC_READ, FILE_SHARE_WRITE, &sec_none_nih, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) return 0; if (shared_console_info != NULL) { /* ACK. Temporarily define for use in TTYSETF macro */ SetConsoleMode (h, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT); shared_console_info->tty_min_state.rstcons (true); } CloseHandle (h); return 1; } /* The results of GetConsoleCP() and GetConsoleOutputCP() cannot be cached, because a program or the user can change these values at any time. */ inline bool dev_console::con_to_str (char *d, const char *s, DWORD sz) { return cp_convert (get_cp (), d, GetConsoleCP (), s, sz); } inline bool dev_console::str_to_con (char *d, const char *s, DWORD sz) { if (alternate_charset_active) { /* no translation when alternate charset is active */ memcpy(d, s, sz); return true; } return cp_convert (GetConsoleOutputCP (), d, get_cp (), s, sz); } bool fhandler_console::set_raw_win32_keyboard_mode (bool new_mode) { bool old_mode = dev_state->raw_win32_keyboard_mode; dev_state->raw_win32_keyboard_mode = new_mode; syscall_printf ("raw keyboard mode %sabled", dev_state->raw_win32_keyboard_mode ? "en" : "dis"); return old_mode; }; void fhandler_console::set_cursor_maybe () { CONSOLE_SCREEN_BUFFER_INFO now; if (!GetConsoleScreenBufferInfo (get_output_handle (), &now)) return; if (dev_state->dwLastCursorPosition.X != now.dwCursorPosition.X || dev_state->dwLastCursorPosition.Y != now.dwCursorPosition.Y) { SetConsoleCursorPosition (get_output_handle (), now.dwCursorPosition); dev_state->dwLastCursorPosition = now.dwCursorPosition; } } void fhandler_console::send_winch_maybe () { SHORT y = dev_state->info.dwWinSize.Y; SHORT x = dev_state->info.dwWinSize.X; fillin_info (); if (y != dev_state->info.dwWinSize.Y || x != dev_state->info.dwWinSize.X) { extern fhandler_tty_master *tty_master; dev_state->scroll_region.Top = 0; dev_state->scroll_region.Bottom = -1; if (tty_master) tty_master->set_winsize (true); else tc->kill_pgrp (SIGWINCH); } } void __stdcall fhandler_console::read (void *pv, size_t& buflen) { HANDLE h = get_io_handle (); #define buf ((char *) pv) int ch; set_input_state (); int copied_chars = get_readahead_into_buffer (buf, buflen); if (copied_chars) { buflen = copied_chars; return; } HANDLE w4[2]; DWORD nwait; char tmp[60]; w4[0] = h; if (&_my_tls != _main_tls) nwait = 1; else { w4[1] = signal_arrived; nwait = 2; } termios ti = tc->ti; for (;;) { int bgres; if ((bgres = bg_check (SIGTTIN)) <= bg_eof) { buflen = bgres; return; } set_cursor_maybe (); /* to make cursor appear on the screen immediately */ switch (WaitForMultipleObjects (nwait, w4, FALSE, INFINITE)) { case WAIT_OBJECT_0: break; case WAIT_OBJECT_0 + 1: goto sig_exit; default: goto err; } DWORD nread; INPUT_RECORD input_rec; const char *toadd = NULL; if (!ReadConsoleInput (h, &input_rec, 1, &nread)) { syscall_printf ("ReadConsoleInput failed, %E"); goto err; /* seems to be failure */ } /* check the event that occurred */ switch (input_rec.EventType) { case KEY_EVENT: #define virtual_key_code (input_rec.Event.KeyEvent.wVirtualKeyCode) #define control_key_state (input_rec.Event.KeyEvent.dwControlKeyState) dev_state->nModifiers = 0; #ifdef DEBUGGING /* allow manual switching to/from raw mode via ctrl-alt-scrolllock */ if (input_rec.Event.KeyEvent.bKeyDown && virtual_key_code == VK_SCROLL && control_key_state & (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED) == LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED ) { set_raw_win32_keyboard_mode (!dev_state->raw_win32_keyboard_mode); continue; } #endif if (dev_state->raw_win32_keyboard_mode) { __small_sprintf (tmp, "\033{%u;%u;%u;%u;%u;%luK", input_rec.Event.KeyEvent.bKeyDown, input_rec.Event.KeyEvent.wRepeatCount, input_rec.Event.KeyEvent.wVirtualKeyCode, input_rec.Event.KeyEvent.wVirtualScanCode, input_rec.Event.KeyEvent.uChar.UnicodeChar, input_rec.Event.KeyEvent.dwControlKeyState); toadd = tmp; nread = strlen (toadd); break; } #define ich (input_rec.Event.KeyEvent.uChar.AsciiChar) #define wch (input_rec.Event.KeyEvent.uChar.UnicodeChar) #define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) #define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) /* Ignore key up events, except for left alt events with non-zero character */ if (!input_rec.Event.KeyEvent.bKeyDown && /* Event for left alt, with a non-zero character, comes from "alt + numerics" key sequence. e.g. <left-alt> 0233 => é */ !(wch != 0 // ?? experimentally determined on an XP system && virtual_key_code == VK_MENU // left alt -- see http://www.microsoft.com/hwdev/tech/input/Scancode.asp && input_rec.Event.KeyEvent.wVirtualScanCode == 0x38)) continue; if (control_key_state & SHIFT_PRESSED) dev_state->nModifiers |= 1; if (control_key_state & RIGHT_ALT_PRESSED) dev_state->nModifiers |= 2; if (control_key_state & CTRL_PRESSED) dev_state->nModifiers |= 4; if (control_key_state & LEFT_ALT_PRESSED) dev_state->nModifiers |= 8; if (wch == 0 || /* arrow/function keys */ (input_rec.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY)) { toadd = get_nonascii_key (input_rec, tmp); if (!toadd) { dev_state->nModifiers = 0; continue; } nread = strlen (toadd); } else { tmp[1] = ich; /* Need this check since US code page seems to have a bug when converting a CTRL-U. */ if ((unsigned char) ich > 0x7f) dev_state->con_to_str (tmp + 1, tmp + 1, 1); /* Determine if the keystroke is modified by META. The tricky part is to distinguish whether the right Alt key should be recognized as Alt, or as AltGr. */ bool meta; if (wincap.altgr_is_ctrl_alt ()) /* WinNT: AltGr is reported as Ctrl+Alt, and Ctrl+Alt is treated just like AltGr. However, if Ctrl+Alt+key generates an ASCII control character, interpret is as META. */ meta = (control_key_state & ALT_PRESSED) != 0 && ((control_key_state & CTRL_PRESSED) == 0 || (ich >= 0 && ich <= 0x1f || ich == 0x7f)); else /* Win9x: there's no way to distinguish Alt from AltGr, so rely on dev_state->meta_mask heuristic (see fhandler_console constructor). */ meta = (control_key_state & dev_state->meta_mask) != 0; if (!meta) toadd = tmp + 1; else { tmp[0] = '\033'; tmp[1] = cyg_tolower (tmp[1]); toadd = tmp; nread++; dev_state->nModifiers &= ~4; } } #undef ich #undef wch #undef ALT_PRESSED #undef CTRL_PRESSED break; case MOUSE_EVENT: send_winch_maybe (); if (dev_state->use_mouse) { MOUSE_EVENT_RECORD& mouse_event = input_rec.Event.MouseEvent; /* Treat the double-click event like a regular button press */ if (mouse_event.dwEventFlags == DOUBLE_CLICK) { syscall_printf ("mouse: double-click -> click"); mouse_event.dwEventFlags = 0; } /* Did something other than a click occur? */ if (mouse_event.dwEventFlags) continue; /* Retrieve reported mouse position */ int x = mouse_event.dwMousePosition.X; int y = mouse_event.dwMousePosition.Y; /* Adjust mouse position by scroll buffer offset */ CONSOLE_SCREEN_BUFFER_INFO now; if (GetConsoleScreenBufferInfo (get_output_handle (), &now)) { y -= now.srWindow.Top; x -= now.srWindow.Left; } else { syscall_printf ("mouse: cannot adjust position by scroll buffer offset"); continue; } /* If the mouse event occurred out of the area we can handle, ignore it. */ if ((x + ' ' + 1 > 0xFF) || (y + ' ' + 1 > 0xFF)) { syscall_printf ("mouse: position out of range"); continue; } /* Ignore unimportant mouse buttons */ mouse_event.dwButtonState &= 0x7; /* This code assumes Windows never reports multiple button events at the same time. */ int b = 0; char sz[32]; if (mouse_event.dwButtonState == dev_state->dwLastButtonState) { syscall_printf ("mouse: button state unchanged"); continue; } else if (mouse_event.dwButtonState < dev_state->dwLastButtonState) { b = 3; strcpy (sz, "btn up"); } else if ((mouse_event.dwButtonState & 1) != (dev_state->dwLastButtonState & 1)) { b = 0; strcpy (sz, "btn1 down"); } else if ((mouse_event.dwButtonState & 2) != (dev_state->dwLastButtonState & 2)) { b = 2; strcpy (sz, "btn2 down"); } else if ((mouse_event.dwButtonState & 4) != (dev_state->dwLastButtonState & 4)) { b = 1; strcpy (sz, "btn3 down"); } /* Remember the current button state */ dev_state->dwLastButtonState = mouse_event.dwButtonState; /* If a button was pressed, remember the modifiers */ if (b != 3) { dev_state->nModifiers = 0; if (mouse_event.dwControlKeyState & SHIFT_PRESSED) dev_state->nModifiers |= 0x4; if (mouse_event.dwControlKeyState & (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED)) dev_state->nModifiers |= 0x8; if (mouse_event.dwControlKeyState & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED)) dev_state->nModifiers |= 0x10; } b |= dev_state->nModifiers; /* We can now create the code. */ sprintf (tmp, "\033[M%c%c%c", b + ' ', x + ' ' + 1, y + ' ' + 1); syscall_printf ("mouse: %s at (%d,%d)", sz, x, y); toadd = tmp; nread = 6; } break; case FOCUS_EVENT: case WINDOW_BUFFER_SIZE_EVENT: send_winch_maybe (); /* fall through */ default: continue; } if (toadd) { line_edit_status res = line_edit (toadd, nread, ti); if (res == line_edit_signalled) goto sig_exit; else if (res == line_edit_input_done) break; } #undef ich } while (buflen) if ((ch = get_readahead ()) < 0) break; else { buf[copied_chars++] = (unsigned char)(ch & 0xff); buflen--; } #undef buf buflen = copied_chars; return; err: __seterrno (); buflen = (size_t) -1; return; sig_exit: set_sig_errno (EINTR); buflen = (size_t) -1; return; } void fhandler_console::set_input_state () { if (tc->rstcons ()) input_tcsetattr (0, &tc->ti); } bool fhandler_console::fillin_info (void) { bool ret; CONSOLE_SCREEN_BUFFER_INFO linfo; if ((ret = GetConsoleScreenBufferInfo (get_output_handle (), &linfo))) { dev_state->info.winTop = linfo.srWindow.Top; dev_state->info.winBottom = linfo.srWindow.Bottom; dev_state->info.dwWinSize.Y = 1 + linfo.srWindow.Bottom - linfo.srWindow.Top; dev_state->info.dwWinSize.X = 1 + linfo.srWindow.Right - linfo.srWindow.Left; dev_state->info.dwBufferSize = linfo.dwSize; dev_state->info.dwCursorPosition = linfo.dwCursorPosition; dev_state->info.wAttributes = linfo.wAttributes; } else { memset (&dev_state->info, 0, sizeof dev_state->info); dev_state->info.dwWinSize.Y = 25; dev_state->info.dwWinSize.X = 80; dev_state->info.winBottom = 24; } return ret; } void fhandler_console::scroll_screen (int x1, int y1, int x2, int y2, int xn, int yn) { SMALL_RECT sr1, sr2; CHAR_INFO fill; COORD dest; (void) fillin_info (); sr1.Left = x1 >= 0 ? x1 : dev_state->info.dwWinSize.X - 1; if (y1 == 0) sr1.Top = dev_state->info.winTop; else sr1.Top = y1 > 0 ? y1 : dev_state->info.winBottom; sr1.Right = x2 >= 0 ? x2 : dev_state->info.dwWinSize.X - 1; if (y2 == 0) sr1.Bottom = dev_state->info.winTop; else sr1.Bottom = y2 > 0 ? y2 : dev_state->info.winBottom; sr2.Top = srTop; sr2.Left = 0; sr2.Bottom = srBottom; sr2.Right = dev_state->info.dwWinSize.X - 1; if (sr1.Bottom > sr2.Bottom && sr1.Top <= sr2.Bottom) sr1.Bottom = sr2.Bottom; dest.X = xn >= 0 ? xn : dev_state->info.dwWinSize.X - 1; if (yn == 0) dest.Y = dev_state->info.winTop; else dest.Y = yn > 0 ? yn : dev_state->info.winBottom; fill.Char.AsciiChar = ' '; fill.Attributes = dev_state->current_win32_attr; ScrollConsoleScreenBuffer (get_output_handle (), &sr1, &sr2, dest, &fill); /* ScrollConsoleScreenBuffer on Windows 95 is buggy - when scroll distance * is more than half of screen, filling doesn't work as expected */ if (sr1.Top != sr1.Bottom) if (dest.Y <= sr1.Top) /* forward scroll */ clear_screen (0, 1 + dest.Y + sr1.Bottom - sr1.Top, sr2.Right, sr2.Bottom); else /* reverse scroll */ clear_screen (0, sr1.Top, sr2.Right, dest.Y - 1); } int fhandler_console::open (int flags, mode_t) { HANDLE h; tcinit (get_tty_stuff (flags)); set_io_handle (NULL); set_output_handle (NULL); set_flags ((flags & ~O_TEXT) | O_BINARY); /* Open the input handle as handle_ */ h = CreateFile ("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &sec_none, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) { __seterrno (); return 0; } set_io_handle (h); uninterruptible_io (true); // Handled explicitly in read code h = CreateFile ("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &sec_none, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) { __seterrno (); return 0; } set_output_handle (h); if (fillin_info ()) dev_state->default_color = dev_state->info.wAttributes; set_default_attr (); DWORD cflags; if (GetConsoleMode (get_io_handle (), &cflags)) { cflags |= ENABLE_PROCESSED_INPUT; SetConsoleMode (get_io_handle (), ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | cflags); } tc->rstcons (false); set_open_status (); cygheap->open_fhs++; debug_printf ("incremented open_fhs, now %d", cygheap->open_fhs); debug_printf ("opened conin$ %p, conout$ %p", get_io_handle (), get_output_handle ()); return 1; } int fhandler_console::close (void) { CloseHandle (get_io_handle ()); CloseHandle (get_output_handle ()); set_io_handle (NULL); set_output_handle (NULL); if (--(cygheap->open_fhs) <= 0 && myself->ctty != TTY_CONSOLE) { syscall_printf ("open_fhs %d", cygheap->open_fhs); FreeConsole (); } debug_printf ("decremented open_fhs, now %d", cygheap->open_fhs); return 0; } /* * Special console dup to duplicate input and output * handles. */ int fhandler_console::dup (fhandler_base *child) { fhandler_console *fhc = (fhandler_console *) child; if (!fhc->open (get_flags () & ~O_NOCTTY, 0)) system_printf ("error opening console, %E"); return 0; } int fhandler_console::ioctl (unsigned int cmd, void *buf) { switch (cmd) { case TIOCGWINSZ: int st; st = fillin_info (); if (st) { /* *not* the buffer size, the actual screen size... */ /* based on Left Top Right Bottom of srWindow */ ((struct winsize *) buf)->ws_row = dev_state->info.dwWinSize.Y; ((struct winsize *) buf)->ws_col = dev_state->info.dwWinSize.X; syscall_printf ("WINSZ: (row=%d,col=%d)", ((struct winsize *) buf)->ws_row, ((struct winsize *) buf)->ws_col); return 0; } else { syscall_printf ("WINSZ failed"); __seterrno (); return -1; } return 0; case TIOCSWINSZ: (void) bg_check (SIGTTOU); return 0; case TIOCLINUX: if (* (int *) buf == 6) { * (int *) buf = dev_state->nModifiers; return 0; } else { set_errno (EINVAL); return -1; } } return fhandler_base::ioctl (cmd, buf); } int fhandler_console::tcflush (int queue) { int res = 0; if (queue == TCIFLUSH || queue == TCIOFLUSH) { if (!FlushConsoleInputBuffer (get_io_handle ())) { __seterrno (); res = -1; } } return res; } int fhandler_console::output_tcsetattr (int, struct termios const *t) { /* All the output bits we can ignore */ DWORD flags = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT; int res = SetConsoleMode (get_output_handle (), flags) ? 0 : -1; syscall_printf ("%d = tcsetattr (,%x) (ENABLE FLAGS %x) (lflag %x oflag %x)", res, t, flags, t->c_lflag, t->c_oflag); return res; } int fhandler_console::input_tcsetattr (int, struct termios const *t) { /* Ignore the optional_actions stuff, since all output is emitted instantly */ DWORD oflags; if (!GetConsoleMode (get_io_handle (), &oflags)) oflags = 0; DWORD flags = 0; #if 0 /* Enable/disable LF -> CRLF conversions */ rbinary ((t->c_iflag & INLCR) ? false : true); #endif /* There's some disparity between what we need and what's available. We've got ECHO and ICANON, they've got ENABLE_ECHO_INPUT and ENABLE_LINE_INPUT. */ tc->ti = *t; if (t->c_lflag & ECHO) { flags |= ENABLE_ECHO_INPUT; } if (t->c_lflag & ICANON) { flags |= ENABLE_LINE_INPUT; } if (flags & ENABLE_ECHO_INPUT && !(flags & ENABLE_LINE_INPUT)) { /* This is illegal, so turn off the echo here, and fake it when we read the characters */ flags &= ~ENABLE_ECHO_INPUT; } if (t->c_lflag & ISIG) { flags |= ENABLE_PROCESSED_INPUT; } if (use_tty) { flags = 0; // ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT; tc->ti.c_iflag = 0; tc->ti.c_lflag = 0; } flags |= ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT; int res; if (flags == oflags) res = 0; else { res = SetConsoleMode (get_io_handle (), flags) ? 0 : -1; if (res < 0) __seterrno (); syscall_printf ("%d = tcsetattr (,%x) enable flags %p, c_lflag %p iflag %p", res, t, flags, t->c_lflag, t->c_iflag); } tc->rstcons (false); return res; } int fhandler_console::tcsetattr (int a, struct termios const *t) { int res = output_tcsetattr (a, t); if (res != 0) return res; return input_tcsetattr (a, t); } int fhandler_console::tcgetattr (struct termios *t) { int res; *t = tc->ti; t->c_cflag |= CS8; DWORD flags; if (!GetConsoleMode (get_io_handle (), &flags)) { __seterrno (); res = -1; } else { if (flags & ENABLE_ECHO_INPUT) t->c_lflag |= ECHO; if (flags & ENABLE_LINE_INPUT) t->c_lflag |= ICANON; if (flags & ENABLE_PROCESSED_INPUT) t->c_lflag |= ISIG; /* What about ENABLE_WINDOW_INPUT and ENABLE_MOUSE_INPUT ? */ /* All the output bits we can ignore */ res = 0; } syscall_printf ("%d = tcgetattr (%p) enable flags %p, t->lflag %p, t->iflag %p", res, t, flags, t->c_lflag, t->c_iflag); return res; } fhandler_console::fhandler_console () : fhandler_termios () { } #define FOREGROUND_ATTR_MASK (FOREGROUND_RED | FOREGROUND_GREEN | \ FOREGROUND_BLUE | FOREGROUND_INTENSITY) #define BACKGROUND_ATTR_MASK (BACKGROUND_RED | BACKGROUND_GREEN | \ BACKGROUND_BLUE | BACKGROUND_INTENSITY) void fhandler_console::set_default_attr () { dev_state->blink = dev_state->underline = dev_state->reverse = false; dev_state->intensity = INTENSITY_NORMAL; dev_state->fg = dev_state->default_color & FOREGROUND_ATTR_MASK; dev_state->bg = dev_state->default_color & BACKGROUND_ATTR_MASK; dev_state->current_win32_attr = get_win32_attr (); SetConsoleTextAttribute (get_output_handle (), dev_state->current_win32_attr); } WORD fhandler_console::get_win32_attr () { WORD win_fg = dev_state->fg; WORD win_bg = dev_state->bg; if (dev_state->reverse) { WORD save_fg = win_fg; win_fg = (win_bg & BACKGROUND_RED ? FOREGROUND_RED : 0) | (win_bg & BACKGROUND_GREEN ? FOREGROUND_GREEN : 0) | (win_bg & BACKGROUND_BLUE ? FOREGROUND_BLUE : 0) | (win_bg & BACKGROUND_INTENSITY ? FOREGROUND_INTENSITY : 0); win_bg = (save_fg & FOREGROUND_RED ? BACKGROUND_RED : 0) | (save_fg & FOREGROUND_GREEN ? BACKGROUND_GREEN : 0) | (save_fg & FOREGROUND_BLUE ? BACKGROUND_BLUE : 0) | (save_fg & FOREGROUND_INTENSITY ? BACKGROUND_INTENSITY : 0); } if (dev_state->underline) win_fg = dev_state->underline_color; /* emulate blink with bright background */ if (dev_state->blink) win_bg |= BACKGROUND_INTENSITY; if (dev_state->intensity == INTENSITY_INVISIBLE) win_fg = win_bg; else if (dev_state->intensity == INTENSITY_BOLD) win_fg |= FOREGROUND_INTENSITY; return (win_fg | win_bg); } /* * Clear the screen context from x1/y1 to x2/y2 cell. * Negative values represents current screen dimensions */ void fhandler_console::clear_screen (int x1, int y1, int x2, int y2) { COORD tlc; DWORD done; int num; (void)fillin_info (); if (x1 < 0) x1 = dev_state->info.dwWinSize.X - 1; if (y1 < 0) y1 = dev_state->info.winBottom; if (x2 < 0) x2 = dev_state->info.dwWinSize.X - 1; if (y2 < 0) y2 = dev_state->info.winBottom; num = abs (y1 - y2) * dev_state->info.dwBufferSize.X + abs (x1 - x2) + 1; if ((y2 * dev_state->info.dwBufferSize.X + x2) > (y1 * dev_state->info.dwBufferSize.X + x1)) { tlc.X = x1; tlc.Y = y1; } else { tlc.X = x2; tlc.Y = y2; } FillConsoleOutputCharacterA (get_output_handle (), ' ', num, tlc, &done); FillConsoleOutputAttribute (get_output_handle (), dev_state->current_win32_attr, num, tlc, &done); } void fhandler_console::cursor_set (bool rel_to_top, int x, int y) { COORD pos; (void) fillin_info (); if (y > dev_state->info.winBottom) y = dev_state->info.winBottom; else if (y < 0) y = 0; else if (rel_to_top) y += dev_state->info.winTop; if (x > dev_state->info.dwWinSize.X) x = dev_state->info.dwWinSize.X - 1; else if (x < 0) x = 0; pos.X = x; pos.Y = y; SetConsoleCursorPosition (get_output_handle (), pos); } void fhandler_console::cursor_rel (int x, int y) { fillin_info (); x += dev_state->info.dwCursorPosition.X; y += dev_state->info.dwCursorPosition.Y; cursor_set (false, x, y); } void fhandler_console::cursor_get (int *x, int *y) { fillin_info (); *y = dev_state->info.dwCursorPosition.Y; *x = dev_state->info.dwCursorPosition.X; } #define BAK 1 #define ESC 2 #define NOR 0 #define IGN 4 #if 0 #define ERR 5 #else #define ERR NOR #endif #define DWN 6 #define BEL 7 #define TAB 8 /* We should't let the console deal with these */ #define CR 13 #define LF 10 static const char base_chars[256] = { /*00 01 02 03 04 05 06 07 */ IGN, ERR, ERR, NOR, NOR, NOR, NOR, BEL, /*08 09 0A 0B 0C 0D 0E 0F */ BAK, TAB, DWN, ERR, ERR, CR, ERR, IGN, /*10 11 12 13 14 15 16 17 */ NOR, NOR, ERR, ERR, ERR, ERR, ERR, ERR, /*18 19 1A 1B 1C 1D 1E 1F */ NOR, NOR, ERR, ESC, ERR, ERR, ERR, ERR, /* ! " # $ % & ' */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*() * + , - . / */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*0 1 2 3 4 5 6 7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*8 9 : ; < = > ? */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*@ A B C D E F G */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*H I J K L M N O */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*P Q R S T U V W */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*X Y Z [ \ ] ^ _ */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*` a b c d e f g */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*h i j k l m n o */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*p q r s t u v w */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*x y z { | } ~ 7F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*80 81 82 83 84 85 86 87 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*88 89 8A 8B 8C 8D 8E 8F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*90 91 92 93 94 95 96 97 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*98 99 9A 9B 9C 9D 9E 9F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*A0 A1 A2 A3 A4 A5 A6 A7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*A8 A9 AA AB AC AD AE AF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*B0 B1 B2 B3 B4 B5 B6 B7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*B8 B9 BA BB BC BD BE BF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*C0 C1 C2 C3 C4 C5 C6 C7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*C8 C9 CA CB CC CD CE CF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*D0 D1 D2 D3 D4 D5 D6 D7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*D8 D9 DA DB DC DD DE DF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*E0 E1 E2 E3 E4 E5 E6 E7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*E8 E9 EA EB EC ED EE EF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*F0 F1 F2 F3 F4 F5 F6 F7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*F8 F9 FA FB FC FD FE FF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR }; void fhandler_console::char_command (char c) { int x, y; char buf[40]; switch (c) { case 'm': /* Set Graphics Rendition */ int i; for (i = 0; i <= dev_state->nargs_; i++) switch (dev_state->args_[i]) { case 0: /* normal color */ set_default_attr (); break; case 1: /* bold */ dev_state->intensity = INTENSITY_BOLD; break; case 4: dev_state->underline = 1; break; case 5: /* blink mode */ dev_state->blink = true; break; case 7: /* reverse */ dev_state->reverse = true; break; case 8: /* invisible */ dev_state->intensity = INTENSITY_INVISIBLE; break; case 9: /* dim */ dev_state->intensity = INTENSITY_DIM; break; case 10: /* end alternate charset */ dev_state->alternate_charset_active = false; break; case 11: /* start alternate charset */ dev_state->alternate_charset_active = true; break; case 24: dev_state->underline = false; break; case 27: dev_state->reverse = false; break; case 30: /* BLACK foreground */ dev_state->fg = 0; break; case 31: /* RED foreground */ dev_state->fg = FOREGROUND_RED; break; case 32: /* GREEN foreground */ dev_state->fg = FOREGROUND_GREEN; break; case 33: /* YELLOW foreground */ dev_state->fg = FOREGROUND_RED | FOREGROUND_GREEN; break; case 34: /* BLUE foreground */ dev_state->fg = FOREGROUND_BLUE; break; case 35: /* MAGENTA foreground */ dev_state->fg = FOREGROUND_RED | FOREGROUND_BLUE; break; case 36: /* CYAN foreground */ dev_state->fg = FOREGROUND_BLUE | FOREGROUND_GREEN; break; case 37: /* WHITE foreg */ dev_state->fg = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; break; case 39: dev_state->fg = dev_state->default_color & FOREGROUND_ATTR_MASK; break; case 40: /* BLACK background */ dev_state->bg = 0; break; case 41: /* RED background */ dev_state->bg = BACKGROUND_RED; break; case 42: /* GREEN background */ dev_state->bg = BACKGROUND_GREEN; break; case 43: /* YELLOW background */ dev_state->bg = BACKGROUND_RED | BACKGROUND_GREEN; break; case 44: /* BLUE background */ dev_state->bg = BACKGROUND_BLUE; break; case 45: /* MAGENTA background */ dev_state->bg = BACKGROUND_RED | BACKGROUND_BLUE; break; case 46: /* CYAN background */ dev_state->bg = BACKGROUND_BLUE | BACKGROUND_GREEN; break; case 47: /* WHITE background */ dev_state->bg = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED; break; case 49: dev_state->bg = dev_state->default_color & BACKGROUND_ATTR_MASK; break; } dev_state->current_win32_attr = get_win32_attr (); SetConsoleTextAttribute (get_output_handle (), dev_state->current_win32_attr); break; case 'h': case 'l': if (!dev_state->saw_question_mark) { switch (dev_state->args_[0]) { case 4: /* Insert mode */ dev_state->insert_mode = (c == 'h') ? true : false; syscall_printf ("insert mode %sabled", dev_state->insert_mode ? "en" : "dis"); break; } break; } switch (dev_state->args_[0]) { case 47: /* Save/Restore screen */ if (c == 'h') /* save */ { CONSOLE_SCREEN_BUFFER_INFO now; COORD cob = { 0, 0 }; if (!GetConsoleScreenBufferInfo (get_output_handle (), &now)) break; dev_state->savebufsiz.X = now.srWindow.Right - now.srWindow.Left + 1; dev_state->savebufsiz.Y = now.srWindow.Bottom - now.srWindow.Top + 1; if (dev_state->savebuf) cfree (dev_state->savebuf); dev_state->savebuf = (PCHAR_INFO) cmalloc (HEAP_1_BUF, sizeof (CHAR_INFO) * dev_state->savebufsiz.X * dev_state->savebufsiz.Y); ReadConsoleOutputA (get_output_handle (), dev_state->savebuf, dev_state->savebufsiz, cob, &now.srWindow); } else /* restore */ { CONSOLE_SCREEN_BUFFER_INFO now; COORD cob = { 0, 0 }; if (!GetConsoleScreenBufferInfo (get_output_handle (), &now)) break; if (!dev_state->savebuf) break; WriteConsoleOutputA (get_output_handle (), dev_state->savebuf, dev_state->savebufsiz, cob, &now.srWindow); cfree (dev_state->savebuf); dev_state->savebuf = NULL; dev_state->savebufsiz.X = dev_state->savebufsiz.Y = 0; } break; case 1000: /* Mouse support */ dev_state->use_mouse = (c == 'h') ? true : false; syscall_printf ("mouse support %sabled", dev_state->use_mouse ? "en" : "dis"); break; case 2000: /* Raw keyboard mode */ set_raw_win32_keyboard_mode ((c == 'h') ? true : false); break; default: /* Ignore */ syscall_printf ("unknown h/l command: %d", dev_state->args_[0]); break; } break; case 'J': switch (dev_state->args_[0]) { case 0: /* Clear to end of screen */ cursor_get (&x, &y); clear_screen (x, y, -1, -1); break; case 1: /* Clear from beginning of screen to cursor */ cursor_get (&x, &y); clear_screen (0, 0, x, y); break; case 2: /* Clear screen */ clear_screen (0, 0, -1, -1); cursor_set (true, 0,0); break; default: goto bad_escape; } break; case 'A': cursor_rel (0, -(dev_state->args_[0] ? dev_state->args_[0] : 1)); break; case 'B': cursor_rel (0, dev_state->args_[0] ? dev_state->args_[0] : 1); break; case 'C': cursor_rel (dev_state->args_[0] ? dev_state->args_[0] : 1, 0); break; case 'D': cursor_rel (-(dev_state->args_[0] ? dev_state->args_[0] : 1),0); break; case 'K': switch (dev_state->args_[0]) { case 0: /* Clear to end of line */ cursor_get (&x, &y); clear_screen (x, y, -1, y); break; case 2: /* Clear line */ cursor_get (&x, &y); clear_screen (0, y, -1, y); break; case 1: /* Clear from bol to cursor */ cursor_get (&x, &y); clear_screen (0, y, x, y); break; default: goto bad_escape; } break; case 'H': case 'f': cursor_set (true, (dev_state->args_[1] ? dev_state->args_[1] : 1) - 1, (dev_state->args_[0] ? dev_state->args_[0] : 1) - 1); break; case 'G': /* hpa - position cursor at column n - 1 */ cursor_get (&x, &y); cursor_set (false, (dev_state->args_[0] ? dev_state->args_[0] - 1 : 0), y); break; case 'd': /* vpa - position cursor at line n */ cursor_get (&x, &y); cursor_set (true, x, (dev_state->args_[0] ? dev_state->args_[0] - 1 : 0)); break; case 's': /* Save cursor position */ cursor_get (&dev_state->savex, &dev_state->savey); dev_state->savey -= dev_state->info.winTop; break; case 'u': /* Restore cursor position */ cursor_set (true, dev_state->savex, dev_state->savey); break; case 'I': /* TAB */ cursor_get (&x, &y); cursor_set (false, 8 * (x / 8 + 1), y); break; case 'L': /* AL - insert blank lines */ dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1; cursor_get (&x, &y); scroll_screen (0, y, -1, -1, 0, y + dev_state->args_[0]); break; case 'M': /* DL - delete lines */ dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1; cursor_get (&x, &y); scroll_screen (0, y + dev_state->args_[0], -1, -1, 0, y); break; case '@': /* IC - insert chars */ dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1; cursor_get (&x, &y); scroll_screen (x, y, -1, y, x + dev_state->args_[0], y); break; case 'P': /* DC - delete chars */ dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1; cursor_get (&x, &y); scroll_screen (x + dev_state->args_[0], y, -1, y, x, y); break; case 'S': /* SF - Scroll forward */ dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1; scroll_screen (0, dev_state->args_[0], -1, -1, 0, 0); break; case 'T': /* SR - Scroll down */ fillin_info (); dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1; scroll_screen (0, 0, -1, -1, 0, dev_state->info.winTop + dev_state->args_[0]); break; case 'X': /* ec - erase chars */ dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1; cursor_get (&x, &y); scroll_screen (x + dev_state->args_[0], y, -1, y, x, y); scroll_screen (x, y, -1, y, x + dev_state->args_[0], y); break; case 'Z': /* Back tab */ cursor_get (&x, &y); cursor_set (false, ((8 * (x / 8 + 1)) - 8), y); break; case 'b': /* Repeat char #1 #2 times */ if (dev_state->insert_mode) { cursor_get (&x, &y); scroll_screen (x, y, -1, y, x + dev_state->args_[1], y); } while (dev_state->args_[1]--) WriteFile (get_output_handle (), &dev_state->args_[0], 1, (DWORD *) &x, 0); break; case 'c': /* u9 - Terminal enquire string */ strcpy (buf, "\033[?6c"); puts_readahead (buf); break; case 'n': switch (dev_state->args_[0]) { case 6: /* u7 - Cursor position request */ cursor_get (&x, &y); y -= dev_state->info.winTop; /* x -= dev_state->info.winLeft; // not available yet */ __small_sprintf (buf, "\033[%d;%dR", y + 1, x + 1); puts_readahead (buf); break; default: goto bad_escape; } break; case 'r': /* Set Scroll region */ dev_state->scroll_region.Top = dev_state->args_[0] ? dev_state->args_[0] - 1 : 0; dev_state->scroll_region.Bottom = dev_state->args_[1] ? dev_state->args_[1] - 1 : -1; cursor_set (true, 0, 0); break; case 'g': /* TAB set/clear */ break; default: bad_escape: break; } } const unsigned char * fhandler_console::write_normal (const unsigned char *src, const unsigned char *end) { /* Scan forward to see what a char which needs special treatment */ DWORD done; const unsigned char *found = src; while (found < end) { if (base_chars[*found] != NOR) break; found++; } /* Print all the base ones out */ if (found != src) { DWORD len = found - src; do { DWORD buf_len; char buf[CONVERT_LIMIT]; done = buf_len = min (sizeof (buf), len); if (!dev_state->str_to_con (buf, (const char *) src, buf_len)) { debug_printf ("conversion error, handle %p", get_output_handle ()); __seterrno (); return 0; } if (dev_state->insert_mode) { int x, y; cursor_get (&x, &y); scroll_screen (x, y, -1, y, x + buf_len, y); } if (!WriteFile (get_output_handle (), buf, buf_len, &done, 0)) { debug_printf ("write failed, handle %p", get_output_handle ()); __seterrno (); return 0; } len -= done; src += done; } while (len > 0); } if (src < end) { int x, y; switch (base_chars[*src]) { case BEL: MessageBeep (0xFFFFFFFF); break; case ESC: dev_state->state_ = gotesc; break; case DWN: cursor_get (&x, &y); if (y >= srBottom) { if (y >= dev_state->info.winBottom && !dev_state->scroll_region.Top) WriteFile (get_output_handle (), "\n", 1, &done, 0); else { scroll_screen (0, srTop + 1, -1, srBottom, 0, srTop); y--; } } cursor_set (false, ((tc->ti.c_oflag & ONLCR) ? 0 : x), y + 1); break; case BAK: cursor_rel (-1, 0); break; case IGN: cursor_rel (1, 0); break; case CR: cursor_get (&x, &y); cursor_set (false, 0, y); break; case ERR: WriteFile (get_output_handle (), src, 1, &done, 0); break; case TAB: cursor_get (&x, &y); cursor_set (false, 8 * (x / 8 + 1), y); break; } src ++; } return src; } int fhandler_console::write (const void *vsrc, size_t len) { /* Run and check for ansi sequences */ unsigned const char *src = (unsigned char *) vsrc; unsigned const char *end = src + len; debug_printf ("%x, %d", vsrc, len); while (src < end) { debug_printf ("at %d(%c) state is %d", *src, isprint (*src) ? *src : ' ', dev_state->state_); switch (dev_state->state_) { case normal: src = write_normal (src, end); if (!src) /* write_normal failed */ return -1; break; case gotesc: if (*src == '[') { dev_state->state_ = gotsquare; dev_state->saw_question_mark = false; for (dev_state->nargs_ = 0; dev_state->nargs_ < MAXARGS; dev_state->nargs_++) dev_state->args_[dev_state->nargs_] = 0; dev_state->nargs_ = 0; } else if (*src == ']') { dev_state->rarg = 0; dev_state->my_title_buf[0] = '\0'; dev_state->state_ = gotrsquare; } else if (*src == 'M') /* Reverse Index */ { fillin_info (); scroll_screen (0, 0, -1, -1, 0, dev_state->info.winTop + 1); dev_state->state_ = normal; } else if (*src == 'c') /* Reset Linux terminal */ { set_default_attr (); clear_screen (0, 0, -1, -1); cursor_set (true, 0, 0); dev_state->state_ = normal; } else if (*src == '8') /* Restore cursor position */ { cursor_set (true, dev_state->savex, dev_state->savey); dev_state->state_ = normal; } else if (*src == '7') /* Save cursor position */ { cursor_get (&dev_state->savex, &dev_state->savey); dev_state->savey -= dev_state->info.winTop; dev_state->state_ = normal; } else if (*src == 'R') dev_state->state_ = normal; else { dev_state->state_ = normal; } src++; break; case gotarg1: if (isdigit (*src)) { dev_state->args_[dev_state->nargs_] = dev_state->args_[dev_state->nargs_] * 10 + *src - '0'; src++; } else if (*src == ';') { src++; dev_state->nargs_++; if (dev_state->nargs_ >= MAXARGS) dev_state->nargs_--; } else { dev_state->state_ = gotcommand; } break; case gotcommand: char_command (*src++); dev_state->state_ = normal; break; case gotrsquare: if (isdigit (*src)) dev_state->rarg = dev_state->rarg * 10 + (*src - '0'); else if (*src == ';' && (dev_state->rarg == 2 || dev_state->rarg == 0)) dev_state->state_ = gettitle; else dev_state->state_ = eattitle; src++; break; case eattitle: case gettitle: { int n = strlen (dev_state->my_title_buf); if (*src < ' ') { if (*src == '\007' && dev_state->state_ == gettitle) { if (old_title) strcpy (old_title, dev_state->my_title_buf); set_console_title (dev_state->my_title_buf); } dev_state->state_ = normal; } else if (n < TITLESIZE) { dev_state->my_title_buf[n++] = *src; dev_state->my_title_buf[n] = '\0'; } src++; break; } case gotsquare: if (*src == ';') { dev_state->state_ = gotarg1; dev_state->nargs_++; src++; } else if (isalpha (*src)) dev_state->state_ = gotcommand; else if (*src != '@' && !isalpha (*src) && !isdigit (*src)) { if (*src == '?') dev_state->saw_question_mark = true; /* ignore any extra chars between [ and first arg or command */ src++; } else dev_state->state_ = gotarg1; break; } } syscall_printf ("%d = write_console (,..%d)", len, len); return len; } static struct { int vk; const char *val[4]; } keytable[] NO_COPY = { /* NORMAL */ /* SHIFT */ /* CTRL */ /* ALT */ {VK_LEFT, {"\033[D", "\033[D", "\033[D", "\033\033[D"}}, {VK_RIGHT, {"\033[C", "\033[C", "\033[C", "\033\033[C"}}, {VK_UP, {"\033[A", "\033[A", "\033[A", "\033\033[A"}}, {VK_DOWN, {"\033[B", "\033[B", "\033[B", "\033\033[B"}}, {VK_PRIOR, {"\033[5~", "\033[5~", "\033[5~", "\033\033[5~"}}, {VK_NEXT, {"\033[6~", "\033[6~", "\033[6~", "\033\033[6~"}}, {VK_HOME, {"\033[1~", "\033[1~", "\033[1~", "\033\033[1~"}}, {VK_END, {"\033[4~", "\033[4~", "\033[4~", "\033\033[4~"}}, {VK_INSERT, {"\033[2~", "\033[2~", "\033[2~", "\033\033[2~"}}, {VK_DELETE, {"\033[3~", "\033[3~", "\033[3~", "\033\033[3~"}}, {VK_F1, {"\033[[A", "\033[23~", NULL, NULL}}, {VK_F2, {"\033[[B", "\033[24~", NULL, NULL}}, {VK_F3, {"\033[[C", "\033[25~", NULL, NULL}}, {VK_F4, {"\033[[D", "\033[26~", NULL, NULL}}, {VK_F5, {"\033[[E", "\033[28~", NULL, NULL}}, {VK_F6, {"\033[17~", "\033[29~", "\036", NULL}}, {VK_F7, {"\033[18~", "\033[31~", NULL, NULL}}, {VK_F8, {"\033[19~", "\033[32~", NULL, NULL}}, {VK_F9, {"\033[20~", "\033[33~", NULL, NULL}}, {VK_F10, {"\033[21~", "\033[34~", NULL, NULL}}, {VK_F11, {"\033[23~", NULL, NULL, NULL}}, {VK_F12, {"\033[24~", NULL, NULL, NULL}}, {VK_NUMPAD5, {"\033[G", NULL, NULL, NULL}}, {VK_CLEAR, {"\033[G", NULL, NULL, NULL}}, {'6', {NULL, NULL, "\036", NULL}}, {0, {"", NULL, NULL, NULL}} }; const char * get_nonascii_key (INPUT_RECORD& input_rec, char *tmp) { #define NORMAL 0 #define SHIFT 1 #define CONTROL 2 #define ALT 3 int modifier_index = NORMAL; if (input_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) modifier_index = SHIFT; else if (input_rec.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) modifier_index = CONTROL; else if (input_rec.Event.KeyEvent.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) modifier_index = ALT; for (int i = 0; keytable[i].vk; i++) if (input_rec.Event.KeyEvent.wVirtualKeyCode == keytable[i].vk) return keytable[i].val[modifier_index]; if (input_rec.Event.KeyEvent.uChar.AsciiChar) { tmp[0] = input_rec.Event.KeyEvent.uChar.AsciiChar; tmp[1] = '\0'; return tmp; } return NULL; } void fhandler_console::init (HANDLE f, DWORD a, mode_t bin) { // this->fhandler_termios::init (f, mode, bin); /* Ensure both input and output console handles are open */ int flags = 0; a &= GENERIC_READ | GENERIC_WRITE; if (a == GENERIC_READ) flags = O_RDONLY; if (a == GENERIC_WRITE) flags = O_WRONLY; if (a == (GENERIC_READ | GENERIC_WRITE)) flags = O_RDWR; open (flags | O_BINARY); if (f != INVALID_HANDLE_VALUE) CloseHandle (f); /* Reopened by open */ tcsetattr (0, &tc->ti); } int fhandler_console::igncr_enabled (void) { return tc->ti.c_iflag & IGNCR; } void fhandler_console::set_close_on_exec (bool val) { fhandler_base::set_close_on_exec (val); set_no_inheritance (output_handle, val); } void fhandler_console::fixup_after_fork (HANDLE) { HANDLE h = get_handle (); HANDLE oh = get_output_handle (); /* Windows does not allow duplication of console handles between processes so open the console explicitly. */ cygheap->open_fhs--; /* The downside of storing this in cygheap. */ if (!open (O_NOCTTY | get_flags (), 0)) system_printf ("error opening console after fork, %E"); if (!close_on_exec ()) { CloseHandle (h); CloseHandle (oh); } } void __stdcall set_console_title (char *title) { int rc; char buf[257]; strncpy (buf, title, sizeof (buf) - 1); buf[sizeof (buf) - 1] = '\0'; if ((rc = WaitForSingleObject (tty_mutex, 15000)) != WAIT_OBJECT_0) sigproc_printf ("wait for title mutex failed rc %d, %E", rc); SetConsoleTitle (buf); ReleaseMutex (tty_mutex); debug_printf ("title '%s'", buf); } void fhandler_console::fixup_after_exec () { HANDLE h = get_handle (); HANDLE oh = get_output_handle (); cygheap->open_fhs--; /* The downside of storing this in cygheap. */ if (!open (O_NOCTTY | get_flags (), 0)) { bool sawerr = false; if (!get_io_handle ()) { system_printf ("error opening input console handle after exec, errno %d, %E", get_errno ()); sawerr = true; } if (!get_output_handle ()) { system_printf ("error opening output console handle after exec, errno %d, %E", get_errno ()); sawerr = true; } if (!sawerr) system_printf ("error opening console after exec, errno %d, %E", get_errno ()); } CloseHandle (h); CloseHandle (oh); return; }