2644 lines
		
	
	
		
			73 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			2644 lines
		
	
	
		
			73 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* fhandler_console.cc
 | |
| 
 | |
|    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
 | |
|    2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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 "miscfuncs.h"
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <wchar.h>
 | |
| #include <ctype.h>
 | |
| #include <sys/param.h>
 | |
| #include <sys/cygwin.h>
 | |
| #include <cygwin/kd.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"
 | |
| #include "tls_pbuf.h"
 | |
| #include "registry.h"
 | |
| #include <asm/socket.h>
 | |
| #include "sync.h"
 | |
| #include "child_info.h"
 | |
| #include "cygwait.h"
 | |
| 
 | |
| /* Don't make this bigger than NT_MAX_PATH as long as the temporary buffer
 | |
|    is allocated using tmp_pathbuf!!! */
 | |
| #define CONVERT_LIMIT NT_MAX_PATH
 | |
| 
 | |
| #define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
 | |
| #define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
 | |
| 
 | |
| #define con (shared_console_info->con)
 | |
| #define srTop (con.b.srWindow.Top + con.scroll_region.Top)
 | |
| #define srBottom ((con.scroll_region.Bottom < 0) ? con.b.srWindow.Bottom : con.b.srWindow.Top + con.scroll_region.Bottom)
 | |
| 
 | |
| const char *get_nonascii_key (INPUT_RECORD&, char *);
 | |
| 
 | |
| const unsigned fhandler_console::MAX_WRITE_CHARS = 16384;
 | |
| 
 | |
| fhandler_console::console_state NO_COPY *fhandler_console::shared_console_info;
 | |
| 
 | |
| bool NO_COPY fhandler_console::invisible_console;
 | |
| 
 | |
| static void
 | |
| beep ()
 | |
| {
 | |
|   const WCHAR ding[] = L"\\media\\ding.wav";
 | |
|   reg_key r (HKEY_CURRENT_USER, KEY_ALL_ACCESS, L"AppEvents", L"Schemes",
 | |
| 	     L"Apps", L".Default", L".Default", L".Current", NULL);
 | |
|   if (r.created ())
 | |
|     {
 | |
|       PWCHAR buf = NULL;
 | |
|       UINT len = GetSystemWindowsDirectoryW (buf, 0) * sizeof (WCHAR);
 | |
|       buf = (PWCHAR) alloca (len += sizeof (ding));
 | |
|       UINT res = GetSystemWindowsDirectoryW (buf, len);
 | |
|       if (res && res <= len)
 | |
| 	r.set_string (L"", wcscat (buf, ding));
 | |
|     }
 | |
|   MessageBeep (MB_OK);
 | |
| }
 | |
| 
 | |
| fhandler_console::console_state *
 | |
| fhandler_console::open_shared_console (HWND hw, HANDLE& h, bool& create)
 | |
| {
 | |
|   wchar_t namebuf[(sizeof "XXXXXXXXXXXXXXXXXX-consNNNNNNNNNN")];
 | |
|   __small_swprintf (namebuf, L"%S-cons%p", &cygheap->installation_key, hw);
 | |
| 
 | |
|   shared_locations m = create ? SH_SHARED_CONSOLE : SH_JUSTOPEN;
 | |
|   console_state *res = (console_state *)
 | |
|     open_shared (namebuf, 0, h, sizeof (*shared_console_info), &m);
 | |
|   create = m != SH_JUSTOPEN;
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| class console_unit
 | |
| {
 | |
|   int n;
 | |
|   unsigned long bitmask;
 | |
|   HWND me;
 | |
| 
 | |
| public:
 | |
|   operator int () const {return n;}
 | |
|   console_unit (HWND);
 | |
|   friend BOOL CALLBACK enum_windows (HWND, LPARAM);
 | |
| };
 | |
| 
 | |
| BOOL CALLBACK
 | |
| enum_windows (HWND hw, LPARAM lp)
 | |
| {
 | |
|   console_unit *this1 = (console_unit *) lp;
 | |
|   if (hw == this1->me)
 | |
|     return TRUE;
 | |
|   HANDLE h = NULL;
 | |
|   fhandler_console::console_state *cs;
 | |
|   if ((cs = fhandler_console::open_shared_console (hw, h)))
 | |
|     {
 | |
|       this1->bitmask ^= 1 << cs->tty_min_state.getntty ();
 | |
|       UnmapViewOfFile ((void *) cs);
 | |
|       CloseHandle (h);
 | |
|     }
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| console_unit::console_unit (HWND me0):
 | |
|   bitmask (0xffffffff), me (me0)
 | |
| {
 | |
|   EnumWindows (enum_windows, (LPARAM) this);
 | |
|   n = (_minor_t) ffs (bitmask) - 1;
 | |
|   if (n < 0)
 | |
|     api_fatal ("console device allocation failure - too many consoles in use, max consoles is 32");
 | |
| }
 | |
| 
 | |
| bool
 | |
| fhandler_console::set_unit ()
 | |
| {
 | |
|   bool created;
 | |
|   fh_devices devset;
 | |
|   lock_ttys here;
 | |
|   HWND me;
 | |
|   fh_devices this_unit = dev ();
 | |
|   bool generic_console = this_unit == FH_CONIN || this_unit == FH_CONOUT;
 | |
|   if (shared_console_info)
 | |
|     {
 | |
|       fh_devices shared_unit =
 | |
| 	(fh_devices) shared_console_info->tty_min_state.getntty ();
 | |
|       devset = (shared_unit == this_unit || this_unit == FH_CONSOLE
 | |
| 		|| generic_console
 | |
| 		|| this_unit == FH_TTY) ?
 | |
| 		shared_unit : FH_ERROR;
 | |
|       created = false;
 | |
|     }
 | |
|   else if ((!generic_console && (myself->ctty != -1 && !iscons_dev (myself->ctty)))
 | |
| 	   || !(me = GetConsoleWindow ()))
 | |
|     devset = FH_ERROR;
 | |
|   else
 | |
|     {
 | |
|       created = true;
 | |
|       shared_console_info = open_shared_console (me, cygheap->console_h, created);
 | |
|       ProtectHandleINH (cygheap->console_h);
 | |
|       if (created)
 | |
| 	shared_console_info->tty_min_state.setntty (DEV_CONS_MAJOR, console_unit (me));
 | |
|       devset = (fh_devices) shared_console_info->tty_min_state.getntty ();
 | |
|     }
 | |
| 
 | |
|   dev ().parse (devset);
 | |
|   if (devset != FH_ERROR)
 | |
|     pc.file_attributes (FILE_ATTRIBUTE_NORMAL);
 | |
|   else
 | |
|     {
 | |
|       set_io_handle (NULL);
 | |
|       set_output_handle (NULL);
 | |
|       created = false;
 | |
|     }
 | |
|   return created;
 | |
| }
 | |
| 
 | |
| /* Allocate and initialize the shared record for the current console. */
 | |
| void
 | |
| fhandler_console::setup ()
 | |
| {
 | |
|   if (set_unit ())
 | |
|       {
 | |
| 	con.scroll_region.Bottom = -1;
 | |
| 	con.dwLastCursorPosition.X = -1;
 | |
| 	con.dwLastCursorPosition.Y = -1;
 | |
| 	con.dwLastMousePosition.X = -1;
 | |
| 	con.dwLastMousePosition.Y = -1;
 | |
| 	con.dwLastButtonState = 0;	/* none pressed */
 | |
| 	con.last_button_code = 3;	/* released */
 | |
| 	con.underline_color = FOREGROUND_GREEN | FOREGROUND_BLUE;
 | |
| 	con.dim_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
 | |
| 	con.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)
 | |
| 	  con.meta_mask |= RIGHT_ALT_PRESSED;
 | |
| 	con.set_default_attr ();
 | |
| 	con.backspace_keycode = CERASE;
 | |
| 	shared_console_info->tty_min_state.is_console = true;
 | |
|       }
 | |
| }
 | |
| 
 | |
| /* 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_cttyp ()
 | |
| {
 | |
|   dev_t n = myself->ctty;
 | |
|   if (iscons_dev (n))
 | |
|     return fhandler_console::shared_console_info ?
 | |
|       &fhandler_console::shared_console_info->tty_min_state : NULL;
 | |
|   else if (istty_slave_dev (n))
 | |
|     return &ttys[device::minor (n)];
 | |
|   else
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| inline DWORD
 | |
| dev_console::con_to_str (char *d, int dlen, WCHAR w)
 | |
| {
 | |
|   return sys_cp_wcstombs (cygheap->locale.wctomb, cygheap->locale.charset,
 | |
| 			  d, dlen, &w, 1);
 | |
| }
 | |
| 
 | |
| inline UINT
 | |
| dev_console::get_console_cp ()
 | |
| {
 | |
|   /* The alternate charset is always 437, just as in the Linux console. */
 | |
|   return alternate_charset_active ? 437 : 0;
 | |
| }
 | |
| 
 | |
| inline DWORD
 | |
| dev_console::str_to_con (mbtowc_p f_mbtowc, const char *charset,
 | |
| 			 PWCHAR d, const char *s, DWORD sz)
 | |
| {
 | |
|   return sys_cp_mbstowcs (f_mbtowc, charset, d, CONVERT_LIMIT, s, sz);
 | |
| }
 | |
| 
 | |
| bool
 | |
| fhandler_console::set_raw_win32_keyboard_mode (bool new_mode)
 | |
| {
 | |
|   bool old_mode = con.raw_win32_keyboard_mode;
 | |
|   con.raw_win32_keyboard_mode = new_mode;
 | |
|   syscall_printf ("raw keyboard mode %sabled", con.raw_win32_keyboard_mode ? "en" : "dis");
 | |
|   return old_mode;
 | |
| };
 | |
| 
 | |
| void
 | |
| fhandler_console::set_cursor_maybe ()
 | |
| {
 | |
|   con.fillin (get_output_handle ());
 | |
|   if (con.dwLastCursorPosition.X != con.b.dwCursorPosition.X ||
 | |
|       con.dwLastCursorPosition.Y != con.b.dwCursorPosition.Y)
 | |
|     {
 | |
|       SetConsoleCursorPosition (get_output_handle (), con.b.dwCursorPosition);
 | |
|       con.dwLastCursorPosition = con.b.dwCursorPosition;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::send_winch_maybe ()
 | |
| {
 | |
|   SHORT y = con.dwWinSize.Y;
 | |
|   SHORT x = con.dwWinSize.X;
 | |
|   con.fillin (get_output_handle ());
 | |
| 
 | |
|   if (y != con.dwWinSize.Y || x != con.dwWinSize.X)
 | |
|     {
 | |
|       con.scroll_region.Top = 0;
 | |
|       con.scroll_region.Bottom = -1;
 | |
|       get_ttyp ()->kill_pgrp (SIGWINCH);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Check whether a mouse event is to be reported as an escape sequence */
 | |
| bool
 | |
| fhandler_console::mouse_aware (MOUSE_EVENT_RECORD& mouse_event)
 | |
| {
 | |
|   if (!con.use_mouse)
 | |
|     return 0;
 | |
| 
 | |
|   /* Adjust mouse position by window scroll buffer offset
 | |
|      and remember adjusted position in state for use by read() */
 | |
|   CONSOLE_SCREEN_BUFFER_INFO now;
 | |
|   if (!GetConsoleScreenBufferInfo (get_output_handle (), &now))
 | |
|     /* Cannot adjust position by window scroll buffer offset */
 | |
|     return 0;
 | |
| 
 | |
|   con.dwMousePosition.X = mouse_event.dwMousePosition.X - now.srWindow.Left;
 | |
|   con.dwMousePosition.Y = mouse_event.dwMousePosition.Y - now.srWindow.Top;
 | |
| 
 | |
|   return ((mouse_event.dwEventFlags == 0 || mouse_event.dwEventFlags == DOUBLE_CLICK)
 | |
| 	  && mouse_event.dwButtonState != con.dwLastButtonState)
 | |
| 	 || mouse_event.dwEventFlags == MOUSE_WHEELED
 | |
| 	 || (mouse_event.dwEventFlags == MOUSE_MOVED
 | |
| 	     && (con.dwMousePosition.X != con.dwLastMousePosition.X
 | |
| 		 || con.dwMousePosition.Y != con.dwLastMousePosition.Y)
 | |
| 	     && ((con.use_mouse >= 2 && mouse_event.dwButtonState)
 | |
| 		 || con.use_mouse >= 3));
 | |
| }
 | |
| 
 | |
| void __reg3
 | |
| fhandler_console::read (void *pv, size_t& buflen)
 | |
| {
 | |
|   push_process_state process_state (PID_TTYIN);
 | |
| 
 | |
|   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;
 | |
|     }
 | |
| 
 | |
|   DWORD timeout = is_nonblocking () ? 0 : INFINITE;
 | |
|   char tmp[60];
 | |
| 
 | |
|   termios ti = get_ttyp ()->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 (cygwait (h, timeout))
 | |
| 	{
 | |
| 	case WAIT_OBJECT_0:
 | |
| 	  break;
 | |
| 	case WAIT_SIGNALED:
 | |
| 	  goto sig_exit;
 | |
| 	case WAIT_CANCELED:
 | |
| 	  process_state.pop ();
 | |
| 	  pthread::static_cancel_self ();
 | |
| 	  /*NOTREACHED*/
 | |
| 	case WAIT_TIMEOUT:
 | |
| 	  set_sig_errno (EAGAIN);
 | |
| 	  buflen = (size_t) -1;
 | |
| 	  return;
 | |
| 	default:
 | |
| 	  goto err;
 | |
| 	}
 | |
| 
 | |
|       DWORD nread;
 | |
|       INPUT_RECORD input_rec;
 | |
|       const char *toadd = NULL;
 | |
| 
 | |
|       if (!ReadConsoleInputW (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)
 | |
| 
 | |
| 	  con.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 (!con.raw_win32_keyboard_mode);
 | |
| 	      continue;
 | |
| 	    }
 | |
| #endif
 | |
| 
 | |
| 	  if (con.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)
 | |
| 
 | |
| 	  /* 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)
 | |
| 	    con.nModifiers |= 1;
 | |
| 	  if (control_key_state & RIGHT_ALT_PRESSED)
 | |
| 	    con.nModifiers |= 2;
 | |
| 	  if (control_key_state & CTRL_PRESSED)
 | |
| 	    con.nModifiers |= 4;
 | |
| 	  if (control_key_state & LEFT_ALT_PRESSED)
 | |
| 	    con.nModifiers |= 8;
 | |
| 
 | |
| 	  /* Allow Backspace to emit ^? and escape sequences. */
 | |
| 	  if (input_rec.Event.KeyEvent.wVirtualKeyCode == VK_BACK)
 | |
| 	    {
 | |
| 	      char c = con.backspace_keycode;
 | |
| 	      nread = 0;
 | |
| 	      if (control_key_state & ALT_PRESSED)
 | |
| 		{
 | |
| 		  if (con.metabit)
 | |
| 		    c |= 0x80;
 | |
| 		  else
 | |
| 		    tmp[nread++] = '\e';
 | |
| 		}
 | |
| 	      tmp[nread++] = c;
 | |
| 	      tmp[nread] = 0;
 | |
| 	      toadd = tmp;
 | |
| 	    }
 | |
| 	  /* Allow Ctrl-Space to emit ^@ */
 | |
| 	  else if (input_rec.Event.KeyEvent.wVirtualKeyCode == VK_SPACE
 | |
| 		   && (control_key_state & CTRL_PRESSED)
 | |
| 		   && !(control_key_state & ALT_PRESSED))
 | |
| 	    toadd = "";
 | |
| 	  else if (wch == 0
 | |
| 	      /* arrow/function keys */
 | |
| 	      || (input_rec.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
 | |
| 	    {
 | |
| 	      toadd = get_nonascii_key (input_rec, tmp);
 | |
| 	      if (!toadd)
 | |
| 		{
 | |
| 		  con.nModifiers = 0;
 | |
| 		  continue;
 | |
| 		}
 | |
| 	      nread = strlen (toadd);
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      nread = con.con_to_str (tmp + 1, 59, wch);
 | |
| 	      /* 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 =
 | |
| 		     /* Alt but not AltGr (= left ctrl + right alt)? */
 | |
| 		     (control_key_state & ALT_PRESSED) != 0
 | |
| 		     && ((control_key_state & CTRL_PRESSED) == 0
 | |
| 			    /* but also allow Alt-AltGr: */
 | |
| 			 || (control_key_state & ALT_PRESSED) == ALT_PRESSED
 | |
| 			 || (wch <= 0x1f || wch == 0x7f));
 | |
| 	      if (!meta)
 | |
| 		{
 | |
| 		  /* Determine if the character is in the current multibyte
 | |
| 		     charset.  The test is easy.  If the multibyte sequence
 | |
| 		     is > 1 and the first byte is ASCII CAN, the character
 | |
| 		     has been translated into the ASCII CAN + UTF-8 replacement
 | |
| 		     sequence.  If so, just ignore the keypress.
 | |
| 		     FIXME: Is there a better solution? */
 | |
| 		  if (nread > 1 && tmp[1] == 0x18)
 | |
| 		    beep ();
 | |
| 		  else
 | |
| 		    toadd = tmp + 1;
 | |
| 		}
 | |
| 	      else if (con.metabit)
 | |
| 		{
 | |
| 		  tmp[1] |= 0x80;
 | |
| 		  toadd = tmp + 1;
 | |
| 		}
 | |
| 	      else
 | |
| 		{
 | |
| 		  tmp[0] = '\033';
 | |
| 		  tmp[1] = cyg_tolower (tmp[1]);
 | |
| 		  toadd = tmp;
 | |
| 		  nread++;
 | |
| 		  con.nModifiers &= ~4;
 | |
| 		}
 | |
| 	    }
 | |
| #undef ich
 | |
| #undef wch
 | |
| 	  break;
 | |
| 
 | |
| 	case MOUSE_EVENT:
 | |
| 	  send_winch_maybe ();
 | |
| 	  {
 | |
| 	    MOUSE_EVENT_RECORD& mouse_event = input_rec.Event.MouseEvent;
 | |
| 	    /* As a unique guard for mouse report generation,
 | |
| 	       call mouse_aware() which is common with select(), so the result
 | |
| 	       of select() and the actual read() will be consistent on the
 | |
| 	       issue of whether input (i.e. a mouse escape sequence) will
 | |
| 	       be available or not */
 | |
| 	    if (mouse_aware (mouse_event))
 | |
| 	      {
 | |
| 		/* Note: Reported mouse position was already retrieved by
 | |
| 		   mouse_aware() and adjusted by window scroll buffer offset */
 | |
| 
 | |
| 		/* 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;
 | |
| 		  }
 | |
| 
 | |
| 		/* This code assumes Windows never reports multiple button
 | |
| 		   events at the same time. */
 | |
| 		int b = 0;
 | |
| 		char sz[32];
 | |
| 		char mode6_term = 'M';
 | |
| 
 | |
| 		if (mouse_event.dwEventFlags == MOUSE_WHEELED)
 | |
| 		  {
 | |
| 		    if (mouse_event.dwButtonState & 0xFF800000)
 | |
| 		      {
 | |
| 			b = 0x41;
 | |
| 			strcpy (sz, "wheel down");
 | |
| 		      }
 | |
| 		    else
 | |
| 		      {
 | |
| 			b = 0x40;
 | |
| 			strcpy (sz, "wheel up");
 | |
| 		      }
 | |
| 		  }
 | |
| 		else
 | |
| 		  {
 | |
| 		    /* Ignore unimportant mouse buttons */
 | |
| 		    mouse_event.dwButtonState &= 0x7;
 | |
| 
 | |
| 		    if (mouse_event.dwEventFlags == MOUSE_MOVED)
 | |
| 		      {
 | |
| 			b = con.last_button_code;
 | |
| 		      }
 | |
| 		    else if (mouse_event.dwButtonState < con.dwLastButtonState && !con.ext_mouse_mode6)
 | |
| 		      {
 | |
| 			b = 3;
 | |
| 			strcpy (sz, "btn up");
 | |
| 		      }
 | |
| 		    else if ((mouse_event.dwButtonState & 1) != (con.dwLastButtonState & 1))
 | |
| 		      {
 | |
| 			b = 0;
 | |
| 			strcpy (sz, "btn1 down");
 | |
| 		      }
 | |
| 		    else if ((mouse_event.dwButtonState & 2) != (con.dwLastButtonState & 2))
 | |
| 		      {
 | |
| 			b = 2;
 | |
| 			strcpy (sz, "btn2 down");
 | |
| 		      }
 | |
| 		    else if ((mouse_event.dwButtonState & 4) != (con.dwLastButtonState & 4))
 | |
| 		      {
 | |
| 			b = 1;
 | |
| 			strcpy (sz, "btn3 down");
 | |
| 		      }
 | |
| 
 | |
| 		    if (con.ext_mouse_mode6 /* distinguish release */
 | |
| 			&& mouse_event.dwButtonState < con.dwLastButtonState)
 | |
| 			mode6_term = 'm';
 | |
| 
 | |
| 		    con.last_button_code = b;
 | |
| 
 | |
| 		    if (mouse_event.dwEventFlags == MOUSE_MOVED)
 | |
| 		      {
 | |
| 			b += 32;
 | |
| 			strcpy (sz, "move");
 | |
| 		      }
 | |
| 		    else
 | |
| 		      {
 | |
| 			/* Remember the modified button state */
 | |
| 			con.dwLastButtonState = mouse_event.dwButtonState;
 | |
| 		      }
 | |
| 		  }
 | |
| 
 | |
| 		/* Remember mouse position */
 | |
| 		con.dwLastMousePosition.X = con.dwMousePosition.X;
 | |
| 		con.dwLastMousePosition.Y = con.dwMousePosition.Y;
 | |
| 
 | |
| 		/* Remember the modifiers */
 | |
| 		con.nModifiers = 0;
 | |
| 		if (mouse_event.dwControlKeyState & SHIFT_PRESSED)
 | |
| 		    con.nModifiers |= 0x4;
 | |
| 		if (mouse_event.dwControlKeyState & ALT_PRESSED)
 | |
| 		    con.nModifiers |= 0x8;
 | |
| 		if (mouse_event.dwControlKeyState & CTRL_PRESSED)
 | |
| 		    con.nModifiers |= 0x10;
 | |
| 
 | |
| 		/* Indicate the modifiers */
 | |
| 		b |= con.nModifiers;
 | |
| 
 | |
| 		/* We can now create the code. */
 | |
| 		if (con.ext_mouse_mode6)
 | |
| 		  {
 | |
| 		    __small_sprintf (tmp, "\033[<%d;%d;%d%c", b,
 | |
| 				     con.dwMousePosition.X + 1,
 | |
| 				     con.dwMousePosition.Y + 1,
 | |
| 				     mode6_term);
 | |
| 		    nread = strlen (tmp);
 | |
| 		  }
 | |
| 		else if (con.ext_mouse_mode15)
 | |
| 		  {
 | |
| 		    __small_sprintf (tmp, "\033[%d;%d;%dM", b + 32,
 | |
| 				     con.dwMousePosition.X + 1,
 | |
| 				     con.dwMousePosition.Y + 1);
 | |
| 		    nread = strlen (tmp);
 | |
| 		  }
 | |
| 		else if (con.ext_mouse_mode5)
 | |
| 		  {
 | |
| 		    unsigned int xcode = con.dwMousePosition.X + ' ' + 1;
 | |
| 		    unsigned int ycode = con.dwMousePosition.Y + ' ' + 1;
 | |
| 
 | |
| 		    __small_sprintf (tmp, "\033[M%c", b + ' ');
 | |
| 		    nread = 4;
 | |
| 		    /* the neat nested encoding function of mintty
 | |
| 		       does not compile in g++, so let's unfold it: */
 | |
| 		    if (xcode < 0x80)
 | |
| 		      tmp [nread++] = xcode;
 | |
| 		    else if (xcode < 0x800)
 | |
| 		      {
 | |
| 			tmp [nread++] = 0xC0 + (xcode >> 6);
 | |
| 			tmp [nread++] = 0x80 + (xcode & 0x3F);
 | |
| 		      }
 | |
| 		    else
 | |
| 		      tmp [nread++] = 0;
 | |
| 		    if (ycode < 0x80)
 | |
| 		      tmp [nread++] = ycode;
 | |
| 		    else if (ycode < 0x800)
 | |
| 		      {
 | |
| 			tmp [nread++] = 0xC0 + (ycode >> 6);
 | |
| 			tmp [nread++] = 0x80 + (ycode & 0x3F);
 | |
| 		      }
 | |
| 		    else
 | |
| 		      tmp [nread++] = 0;
 | |
| 		  }
 | |
| 		else
 | |
| 		  {
 | |
| 		    unsigned int xcode = con.dwMousePosition.X + ' ' + 1;
 | |
| 		    unsigned int ycode = con.dwMousePosition.Y + ' ' + 1;
 | |
| 		    if (xcode >= 256)
 | |
| 		      xcode = 0;
 | |
| 		    if (ycode >= 256)
 | |
| 		      ycode = 0;
 | |
| 		    __small_sprintf (tmp, "\033[M%c%c%c", b + ' ',
 | |
| 				     xcode, ycode);
 | |
| 		    nread = 6;	/* tmp may contain NUL bytes */
 | |
| 		  }
 | |
| 		syscall_printf ("mouse: %s at (%d,%d)", sz,
 | |
| 				con.dwMousePosition.X,
 | |
| 				con.dwMousePosition.Y);
 | |
| 
 | |
| 		toadd = tmp;
 | |
| 	      }
 | |
| 	  }
 | |
| 	  break;
 | |
| 
 | |
| 	case FOCUS_EVENT:
 | |
| 	  if (con.use_focus)
 | |
| 	    {
 | |
| 	      if (input_rec.Event.FocusEvent.bSetFocus)
 | |
| 		__small_sprintf (tmp, "\033[I");
 | |
| 	      else
 | |
| 		__small_sprintf (tmp, "\033[O");
 | |
| 
 | |
| 	      toadd = tmp;
 | |
| 	      nread = 3;
 | |
| 	    }
 | |
| 	  break;
 | |
| 
 | |
| 	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;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   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;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::set_input_state ()
 | |
| {
 | |
|   if (get_ttyp ()->rstcons ())
 | |
|     input_tcsetattr (0, &get_ttyp ()->ti);
 | |
| }
 | |
| 
 | |
| bool
 | |
| dev_console::fillin (HANDLE h)
 | |
| {
 | |
|   bool ret;
 | |
| 
 | |
|   if ((ret = GetConsoleScreenBufferInfo (h, &b)))
 | |
|     {
 | |
|       dwWinSize.Y = 1 + b.srWindow.Bottom - b.srWindow.Top;
 | |
|       dwWinSize.X = 1 + b.srWindow.Right - b.srWindow.Left;
 | |
|       if (b.dwSize.Y != b.dwSize.Y || b.dwSize.X != b.dwSize.X)
 | |
| 	dwEnd.X = dwEnd.Y = 0;
 | |
|       if (b.dwCursorPosition.Y > dwEnd.Y
 | |
| 	  || (b.dwCursorPosition.Y >= dwEnd.Y && b.dwCursorPosition.X > dwEnd.X))
 | |
| 	dwEnd = b.dwCursorPosition;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       memset (&b, 0, sizeof (b));
 | |
|       dwWinSize.Y = 25;
 | |
|       dwWinSize.X = 80;
 | |
|       b.srWindow.Bottom = 24;
 | |
|       b.srWindow.Right = 79;
 | |
|     }
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| void __reg3
 | |
| dev_console::scroll_buffer (HANDLE h, int x1, int y1, int x2, int y2, int xn, int yn)
 | |
| {
 | |
| /* Scroll the screen context.
 | |
|    x1, y1 - ul corner
 | |
|    x2, y2 - dr corner
 | |
|    xn, yn - new ul corner
 | |
|    Negative values represents current screen dimensions
 | |
| */
 | |
|   SMALL_RECT sr1, sr2;
 | |
|   CHAR_INFO fill;
 | |
|   COORD dest;
 | |
|   fill.Char.AsciiChar = ' ';
 | |
|   fill.Attributes = current_win32_attr;
 | |
| 
 | |
|   fillin (h);
 | |
|   sr1.Left = x1 >= 0 ? x1 : dwWinSize.X - 1;
 | |
|   sr1.Top = y1 >= 0 ? y1 : b.srWindow.Bottom;
 | |
|   sr1.Right = x2 >= 0 ? x2 : dwWinSize.X - 1;
 | |
|   sr1.Bottom = y2 >= 0 ? y2 : b.srWindow.Bottom;
 | |
|   sr2.Top = b.srWindow.Top + scroll_region.Top;
 | |
|   sr2.Left = 0;
 | |
|   sr2.Bottom = (scroll_region.Bottom < 0) ? b.srWindow.Bottom : b.srWindow.Top + scroll_region.Bottom;
 | |
|   sr2.Right = dwWinSize.X - 1;
 | |
|   if (sr1.Bottom > sr2.Bottom && sr1.Top <= sr2.Bottom)
 | |
|     sr1.Bottom = sr2.Bottom;
 | |
|   dest.X = xn >= 0 ? xn : dwWinSize.X - 1;
 | |
|   dest.Y = yn >= 0 ? yn : b.srWindow.Bottom;
 | |
|   ScrollConsoleScreenBuffer (h, &sr1, &sr2, dest, &fill);
 | |
| }
 | |
| 
 | |
| inline void
 | |
| fhandler_console::scroll_buffer (int x1, int y1, int x2, int y2, int xn, int yn)
 | |
| {
 | |
|   con.scroll_buffer (get_output_handle (), x1, y1, x2, y2, xn, yn);
 | |
| }
 | |
| 
 | |
| inline void
 | |
| fhandler_console::scroll_buffer_screen (int x1, int y1, int x2, int y2, int xn, int yn)
 | |
| {
 | |
|   if (y1 >= 0)
 | |
|     y1 += con.b.srWindow.Top;
 | |
|   if (y2 >= 0)
 | |
|     y1 += con.b.srWindow.Top;
 | |
|   if (yn >= 0)
 | |
|     yn += con.b.srWindow.Top;
 | |
|   con.scroll_buffer (get_output_handle (), x1, y1, x2, y2, xn, yn);
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::dup (fhandler_base *child, int flags)
 | |
| {
 | |
|   /* See comments in fhandler_pty_slave::dup */
 | |
|   if (myself->ctty != -2)
 | |
|     myself->set_ctty (this, flags);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::open (int flags, mode_t)
 | |
| {
 | |
|   HANDLE h;
 | |
| 
 | |
|   if (dev () == FH_ERROR)
 | |
|     {
 | |
|       set_errno (EPERM);	/* constructor found an error */
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   tcinit (false);
 | |
| 
 | |
|   set_io_handle (NULL);
 | |
|   set_output_handle (NULL);
 | |
| 
 | |
|   /* 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);
 | |
| 
 | |
|   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 (con.fillin (get_output_handle ()))
 | |
|     {
 | |
|       con.current_win32_attr = con.b.wAttributes;
 | |
|       if (!con.default_color)
 | |
| 	con.default_color = con.b.wAttributes;
 | |
|       con.set_default_attr ();
 | |
|     }
 | |
| 
 | |
|   get_ttyp ()->rstcons (false);
 | |
|   set_open_status ();
 | |
| 
 | |
|   DWORD cflags;
 | |
|   if (GetConsoleMode (get_io_handle (), &cflags))
 | |
|     SetConsoleMode (get_io_handle (),
 | |
| 		    ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | cflags);
 | |
| 
 | |
|   debug_printf ("opened conin$ %p, conout$ %p", get_io_handle (),
 | |
| 		get_output_handle ());
 | |
| 
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::open_setup (int flags)
 | |
| {
 | |
|   set_flags ((flags & ~O_TEXT) | O_BINARY);
 | |
|   if (myself->set_ctty (this, flags) && !myself->cygstarted)
 | |
|     init_console_handler (true);
 | |
|   fhandler_base::open_setup (flags);
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::close ()
 | |
| {
 | |
|   CloseHandle (get_io_handle ());
 | |
|   CloseHandle (get_output_handle ());
 | |
|   if (!have_execed)
 | |
|     free_console ();
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::ioctl (unsigned int cmd, void *arg)
 | |
| {
 | |
|   int res = fhandler_termios::ioctl (cmd, arg);
 | |
|   if (res <= 0)
 | |
|     return res;
 | |
|   switch (cmd)
 | |
|     {
 | |
|       case TIOCGWINSZ:
 | |
| 	int st;
 | |
| 
 | |
| 	st = con.fillin (get_output_handle ());
 | |
| 	if (st)
 | |
| 	  {
 | |
| 	    /* *not* the buffer size, the actual screen size... */
 | |
| 	    /* based on Left Top Right Bottom of srWindow */
 | |
| 	    ((struct winsize *) arg)->ws_row = con.dwWinSize.Y;
 | |
| 	    ((struct winsize *) arg)->ws_col = con.dwWinSize.X;
 | |
| 	    syscall_printf ("WINSZ: (row=%d,col=%d)",
 | |
| 			   ((struct winsize *) arg)->ws_row,
 | |
| 			   ((struct winsize *) arg)->ws_col);
 | |
| 	    return 0;
 | |
| 	  }
 | |
| 	else
 | |
| 	  {
 | |
| 	    syscall_printf ("WINSZ failed");
 | |
| 	    __seterrno ();
 | |
| 	    return -1;
 | |
| 	  }
 | |
| 	return 0;
 | |
|       case TIOCSWINSZ:
 | |
| 	bg_check (SIGTTOU);
 | |
| 	return 0;
 | |
|       case KDGKBMETA:
 | |
| 	*(int *) arg = (con.metabit) ? K_METABIT : K_ESCPREFIX;
 | |
| 	return 0;
 | |
|       case KDSKBMETA:
 | |
| 	if ((intptr_t) arg == K_METABIT)
 | |
| 	  con.metabit = TRUE;
 | |
| 	else if ((intptr_t) arg == K_ESCPREFIX)
 | |
| 	  con.metabit = FALSE;
 | |
| 	else
 | |
| 	  {
 | |
| 	    set_errno (EINVAL);
 | |
| 	    return -1;
 | |
| 	  }
 | |
| 	return 0;
 | |
|       case TIOCLINUX:
 | |
| 	if (*(unsigned char *) arg == 6)
 | |
| 	  {
 | |
| 	    *(unsigned char *) arg = (unsigned char) con.nModifiers;
 | |
| 	    return 0;
 | |
| 	  }
 | |
| 	set_errno (EINVAL);
 | |
| 	return -1;
 | |
|       case FIONREAD:
 | |
| 	{
 | |
| 	  /* Per MSDN, max size of buffer required is below 64K. */
 | |
| #define	  INREC_SIZE	(65536 / sizeof (INPUT_RECORD))
 | |
| 
 | |
| 	  DWORD n;
 | |
| 	  int ret = 0;
 | |
| 	  INPUT_RECORD inp[INREC_SIZE];
 | |
| 	  if (!PeekConsoleInputW (get_io_handle (), inp, INREC_SIZE, &n))
 | |
| 	    {
 | |
| 	      set_errno (EINVAL);
 | |
| 	      return -1;
 | |
| 	    }
 | |
| 	  while (n-- > 0)
 | |
| 	    if (inp[n].EventType == KEY_EVENT && inp[n].Event.KeyEvent.bKeyDown)
 | |
| 	      ++ret;
 | |
| 	  *(int *) arg = ret;
 | |
| 	  return 0;
 | |
| 	}
 | |
| 	break;
 | |
|     }
 | |
| 
 | |
|   return fhandler_base::ioctl (cmd, arg);
 | |
| }
 | |
| 
 | |
| 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;
 | |
|   if (res)
 | |
|     __seterrno_from_win_error (GetLastError ());
 | |
|   syscall_printf ("%d = tcsetattr(,%p) (ENABLE FLAGS %y) (lflag %y oflag %y)",
 | |
| 		  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. */
 | |
| 
 | |
|   termios_printf ("this %p, get_ttyp () %p, t %p", this, get_ttyp (), t);
 | |
|   get_ttyp ()->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) && !(t->c_iflag & IGNBRK))
 | |
|     {
 | |
|       flags |= ENABLE_PROCESSED_INPUT;
 | |
|     }
 | |
| 
 | |
|   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(,%p) enable flags %y, c_lflag %y iflag %y",
 | |
| 		      res, t, flags, t->c_lflag, t->c_iflag);
 | |
|     }
 | |
| 
 | |
|   get_ttyp ()->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 = get_ttyp ()->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;
 | |
|       else
 | |
| 	t->c_iflag |= IGNBRK;
 | |
| 
 | |
|       /* 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 %y, t->lflag %y, t->iflag %y",
 | |
| 		 res, t, flags, t->c_lflag, t->c_iflag);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| fhandler_console::fhandler_console (fh_devices unit) :
 | |
|   fhandler_termios ()
 | |
| {
 | |
|   if (unit > 0)
 | |
|     dev ().parse (unit);
 | |
|   setup ();
 | |
|   trunc_buf.len = 0;
 | |
|   _tc = &(shared_console_info->tty_min_state);
 | |
| }
 | |
| 
 | |
| void
 | |
| dev_console::set_color (HANDLE h)
 | |
| {
 | |
|   WORD win_fg = fg;
 | |
|   WORD win_bg = bg;
 | |
|   if (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);
 | |
|     }
 | |
| 
 | |
|   /* apply attributes */
 | |
|   if (underline)
 | |
|     win_fg = underline_color;
 | |
|   /* emulate blink with bright background */
 | |
|   if (blink)
 | |
|     win_bg |= BACKGROUND_INTENSITY;
 | |
|   if (intensity == INTENSITY_INVISIBLE)
 | |
|     win_fg = win_bg;
 | |
|   else if (intensity != INTENSITY_BOLD)
 | |
|     /* nothing to do */;
 | |
|     /* apply foreground intensity only in non-reverse mode! */
 | |
|   else if (reverse)
 | |
|     win_bg |= BACKGROUND_INTENSITY;
 | |
|   else
 | |
|     win_fg |= FOREGROUND_INTENSITY;
 | |
| 
 | |
|   current_win32_attr = win_fg | win_bg;
 | |
|   if (h)
 | |
|     SetConsoleTextAttribute (h, current_win32_attr);
 | |
| }
 | |
| 
 | |
| #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
 | |
| dev_console::set_default_attr ()
 | |
| {
 | |
|   blink = underline = reverse = false;
 | |
|   intensity = INTENSITY_NORMAL;
 | |
|   fg = default_color & FOREGROUND_ATTR_MASK;
 | |
|   bg = default_color & BACKGROUND_ATTR_MASK;
 | |
|   set_color (NULL);
 | |
| }
 | |
| 
 | |
| int
 | |
| dev_console::set_cl_x (cltype x)
 | |
| {
 | |
|   if (x == cl_disp_beg || x == cl_buf_beg)
 | |
|     return 0;
 | |
|   if (x == cl_disp_end)
 | |
|     return dwWinSize.X - 1;
 | |
|   if (x == cl_buf_end)
 | |
|     return b.dwSize.X - 1;
 | |
|   return b.dwCursorPosition.X;
 | |
| }
 | |
| 
 | |
| int
 | |
| dev_console::set_cl_y (cltype y)
 | |
| {
 | |
|   if (y == cl_buf_beg)
 | |
|     return 0;
 | |
|   if (y == cl_disp_beg)
 | |
|     return b.srWindow.Top;
 | |
|   if (y == cl_disp_end)
 | |
|     return b.srWindow.Bottom;
 | |
|   if (y == cl_buf_end)
 | |
|     return b.dwSize.Y - 1;
 | |
|   return b.dwCursorPosition.Y;
 | |
| }
 | |
| 
 | |
| bool
 | |
| dev_console::scroll_window (HANDLE h, int x1, int y1, int x2, int y2)
 | |
| {
 | |
|   if (save_buf || x1 != 0 || x2 != dwWinSize.X - 1 || y1 != b.srWindow.Top
 | |
|       || y2 != b.srWindow.Bottom || b.dwSize.Y <= dwWinSize.Y)
 | |
|     return false;
 | |
| 
 | |
|   SMALL_RECT sr;
 | |
|   int toscroll = 2 + dwEnd.Y - b.srWindow.Top;
 | |
|   int shrink = 1 + toscroll + b.srWindow.Bottom - b.dwSize.Y;
 | |
|   sr.Left = sr.Right = dwEnd.X = 0;
 | |
|   /* Can't increment dwEnd yet since we may not have space in
 | |
|      the buffer.  */
 | |
|   SetConsoleCursorPosition (h, dwEnd);
 | |
|   if (shrink > 0)
 | |
|     {
 | |
|       COORD c = b.dwSize;
 | |
|       c.Y = dwEnd.Y - shrink;
 | |
|       SetConsoleScreenBufferSize (h, c);
 | |
|       SetConsoleScreenBufferSize (h, b.dwSize);
 | |
|       dwEnd.Y = 0;
 | |
|       fillin (h);
 | |
|       toscroll = 2 + dwEnd.Y - b.srWindow.Top;
 | |
|     }
 | |
| 
 | |
|   sr.Top = sr.Bottom = toscroll;
 | |
| 
 | |
|   SetConsoleWindowInfo (h, FALSE, &sr);
 | |
| 
 | |
|   dwEnd.Y++;
 | |
|   SetConsoleCursorPosition (h, dwEnd);
 | |
| 
 | |
|   fillin (h);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Clear the screen context from x1/y1 to x2/y2 cell.
 | |
|  * Negative values represents current screen dimensions
 | |
|  */
 | |
| void __reg3
 | |
| fhandler_console::clear_screen (cltype xc1, cltype yc1, cltype xc2, cltype yc2)
 | |
| {
 | |
|   HANDLE h = get_output_handle ();
 | |
|   con.fillin (h);
 | |
| 
 | |
|   int x1 = con.set_cl_x (xc1);
 | |
|   int y1 = con.set_cl_y (yc1);
 | |
|   int x2 = con.set_cl_x (xc2);
 | |
|   int y2 = con.set_cl_y (yc2);
 | |
| 
 | |
|   /* Detect special case - scroll the screen if we have a buffer in order to
 | |
|      preserve the buffer. */
 | |
|   if (!con.scroll_window (h, x1, y1, x2, y2))
 | |
|     con.clear_screen (h, x1, y1, x2, y2);
 | |
| }
 | |
| 
 | |
| void __reg3
 | |
| dev_console::clear_screen (HANDLE h, int x1, int y1, int x2, int y2)
 | |
| {
 | |
|   COORD tlc;
 | |
|   DWORD done;
 | |
|   int num;
 | |
| 
 | |
|   num = abs (y1 - y2) * b.dwSize.X + abs (x1 - x2) + 1;
 | |
| 
 | |
|   if ((y2 * b.dwSize.X + x2) > (y1 * b.dwSize.X + x1))
 | |
|     {
 | |
|       tlc.X = x1;
 | |
|       tlc.Y = y1;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       tlc.X = x2;
 | |
|       tlc.Y = y2;
 | |
|     }
 | |
|   FillConsoleOutputCharacterA (h, ' ', num, tlc, &done);
 | |
|   FillConsoleOutputAttribute (h, current_win32_attr, num, tlc, &done);
 | |
| }
 | |
| 
 | |
| void __reg3
 | |
| fhandler_console::cursor_set (bool rel_to_top, int x, int y)
 | |
| {
 | |
|   COORD pos;
 | |
| 
 | |
|   con.fillin (get_output_handle ());
 | |
| #if 0
 | |
|   /* Setting y to the current b.srWindow.Bottom here is the reason that the window
 | |
|      isn't scrolled back to the current cursor position like it's done in
 | |
|      any other terminal.  Rather, the curser is forced to the bottom of the
 | |
|      currently scrolled region.  This breaks the console buffer content if
 | |
|      output is generated while the user had the window scrolled back.  This
 | |
|      behaviour is very old, it has no matching ChangeLog entry.
 | |
|      Just disable for now but keep the code in for future reference. */
 | |
|   if (y > con.b.srWindow.Bottom)
 | |
|     y = con.b.srWindow.Bottom;
 | |
|   else
 | |
| #endif
 | |
|   if (y < 0)
 | |
|     y = 0;
 | |
|   else if (rel_to_top)
 | |
|     y += con.b.srWindow.Top;
 | |
| 
 | |
|   if (x > con.dwWinSize.X)
 | |
|     x = con.dwWinSize.X - 1;
 | |
|   else if (x < 0)
 | |
|     x = 0;
 | |
| 
 | |
|   pos.X = x;
 | |
|   pos.Y = y;
 | |
|   SetConsoleCursorPosition (get_output_handle (), pos);
 | |
| }
 | |
| 
 | |
| void __reg3
 | |
| fhandler_console::cursor_rel (int x, int y)
 | |
| {
 | |
|   con.fillin (get_output_handle ());
 | |
|   x += con.b.dwCursorPosition.X;
 | |
|   y += con.b.dwCursorPosition.Y;
 | |
|   cursor_set (false, x, y);
 | |
| }
 | |
| 
 | |
| void __reg3
 | |
| fhandler_console::cursor_get (int *x, int *y)
 | |
| {
 | |
|   con.fillin (get_output_handle ());
 | |
|   *y = con.b.dwCursorPosition.Y;
 | |
|   *x = con.b.dwCursorPosition.X;
 | |
| }
 | |
| 
 | |
| /* VT100 line drawing graphics mode maps `abcdefghijklmnopqrstuvwxyz{|}~ to
 | |
|    graphical characters */
 | |
| static const wchar_t __vt100_conv[31] = {
 | |
| 	0x25C6, /* Black Diamond */
 | |
| 	0x2592, /* Medium Shade */
 | |
| 	0x2409, /* Symbol for Horizontal Tabulation */
 | |
| 	0x240C, /* Symbol for Form Feed */
 | |
| 	0x240D, /* Symbol for Carriage Return */
 | |
| 	0x240A, /* Symbol for Line Feed */
 | |
| 	0x00B0, /* Degree Sign */
 | |
| 	0x00B1, /* Plus-Minus Sign */
 | |
| 	0x2424, /* Symbol for Newline */
 | |
| 	0x240B, /* Symbol for Vertical Tabulation */
 | |
| 	0x2518, /* Box Drawings Light Up And Left */
 | |
| 	0x2510, /* Box Drawings Light Down And Left */
 | |
| 	0x250C, /* Box Drawings Light Down And Right */
 | |
| 	0x2514, /* Box Drawings Light Up And Right */
 | |
| 	0x253C, /* Box Drawings Light Vertical And Horizontal */
 | |
| 	0x23BA, /* Horizontal Scan Line-1 */
 | |
| 	0x23BB, /* Horizontal Scan Line-3 */
 | |
| 	0x2500, /* Box Drawings Light Horizontal */
 | |
| 	0x23BC, /* Horizontal Scan Line-7 */
 | |
| 	0x23BD, /* Horizontal Scan Line-9 */
 | |
| 	0x251C, /* Box Drawings Light Vertical And Right */
 | |
| 	0x2524, /* Box Drawings Light Vertical And Left */
 | |
| 	0x2534, /* Box Drawings Light Up And Horizontal */
 | |
| 	0x252C, /* Box Drawings Light Down And Horizontal */
 | |
| 	0x2502, /* Box Drawings Light Vertical */
 | |
| 	0x2264, /* Less-Than Or Equal To */
 | |
| 	0x2265, /* Greater-Than Or Equal To */
 | |
| 	0x03C0, /* Greek Small Letter Pi */
 | |
| 	0x2260, /* Not Equal To */
 | |
| 	0x00A3, /* Pound Sign */
 | |
| 	0x00B7, /* Middle Dot */
 | |
| };
 | |
| 
 | |
| inline
 | |
| bool fhandler_console::write_console (PWCHAR buf, DWORD len, DWORD& done)
 | |
| {
 | |
|   if (con.iso_2022_G1
 | |
| 	? con.vt100_graphics_mode_G1
 | |
| 	: con.vt100_graphics_mode_G0)
 | |
|     for (DWORD i = 0; i < len; i ++)
 | |
|       if (buf[i] >= (unsigned char) '`' && buf[i] <= (unsigned char) '~')
 | |
| 	buf[i] = __vt100_conv[buf[i] - (unsigned char) '`'];
 | |
| 
 | |
|   while (len > 0)
 | |
|     {
 | |
|       DWORD nbytes = len > MAX_WRITE_CHARS ? MAX_WRITE_CHARS : len;
 | |
|       if (!WriteConsoleW (get_output_handle (), buf, nbytes, &done, 0))
 | |
| 	{
 | |
| 	  __seterrno ();
 | |
| 	  return false;
 | |
| 	}
 | |
|       len -= done;
 | |
|       buf += done;
 | |
|     }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /* The following three functions were adapted (i.e., mildly modified) from
 | |
|    http://stackoverflow.com/questions/14699043/replacement-to-systemcolor */
 | |
| 
 | |
| /* Split a rectangular region into two smaller rectangles based on the
 | |
|    largest dimension. */
 | |
| static void
 | |
| region_split (PCHAR_INFO& buf, COORD& bufsiz, SMALL_RECT& region,
 | |
| 	      PCHAR_INFO& buf_b, COORD& bufsiz_b, SMALL_RECT& region_b)
 | |
| {
 | |
|   region_b = region;
 | |
|   bufsiz_b = bufsiz;
 | |
| 
 | |
|   SHORT half = (1 + region.Bottom - region.Top) / 2;
 | |
|   region_b.Top += half;
 | |
|   region.Bottom = (bufsiz.Y = region_b.Top) - 1;
 | |
|   buf_b = buf + (half * (1 + region.Right));
 | |
|   bufsiz_b.Y = region_b.Bottom - region_b.Top;
 | |
| }
 | |
| 
 | |
| /* Utility function to figure out the distance between two points. */
 | |
| static SHORT
 | |
| delta (SHORT first, SHORT second)
 | |
| {
 | |
|   return (second >= first) ? (second - first + 1) : 0;
 | |
| }
 | |
| 
 | |
| /* Subdivide the ReadConsoleInput operation into smaller and smaller chunks as
 | |
|    needed until it succeeds in reading the entire screen buffer. */
 | |
| static BOOL
 | |
| ReadConsoleOutputWrapper (HANDLE h, PCHAR_INFO buf, COORD bufsiz,
 | |
| 			  SMALL_RECT region)
 | |
| {
 | |
|   COORD coord = {};
 | |
|   SHORT width = delta (region.Left, region.Right);
 | |
|   SHORT height = delta (region.Top, region.Bottom);
 | |
| 
 | |
|   if ((width == 0) || (height == 0))
 | |
|     return TRUE;
 | |
| 
 | |
|   BOOL success = ReadConsoleOutputW (h, buf, bufsiz, coord, ®ion);
 | |
|   if (success)
 | |
|     /* it worked */;
 | |
|   else if (GetLastError () == ERROR_NOT_ENOUGH_MEMORY && (width * height) > 1)
 | |
|     {
 | |
|       PCHAR_INFO buf_b;
 | |
|       COORD bufsiz_b;
 | |
|       SMALL_RECT region_b;
 | |
|       region_split (buf, bufsiz, region, buf_b, bufsiz_b, region_b);
 | |
|       success = ReadConsoleOutputWrapper (h, buf, bufsiz, region)
 | |
| 		&& ReadConsoleOutputWrapper (h, buf_b, bufsiz_b, region_b);
 | |
|     }
 | |
|   return success;
 | |
| }
 | |
| 
 | |
| void
 | |
| dev_console::save_restore (HANDLE h, char c)
 | |
| {
 | |
|   if (c == 'h') /* save */
 | |
|     {
 | |
|       fillin (h);
 | |
|       save_bufsize.X = b.dwSize.X;
 | |
|       if ((save_bufsize.Y = dwEnd.Y + 1) > b.dwSize.Y)
 | |
| 	save_bufsize.X = b.dwSize.Y;
 | |
| 
 | |
|       if (save_buf)
 | |
| 	cfree (save_buf);
 | |
|       size_t screen_size = sizeof (CHAR_INFO) * save_bufsize.X * save_bufsize.Y;
 | |
|       save_buf = (PCHAR_INFO) cmalloc_abort (HEAP_1_BUF, screen_size);
 | |
| 
 | |
|       save_cursor = b.dwCursorPosition;	/* Remember where we were. */
 | |
|       save_top = b.srWindow.Top;
 | |
| 
 | |
|       SMALL_RECT now = {};		/* Read the whole buffer */
 | |
|       now.Bottom = save_bufsize.Y - 1;
 | |
|       now.Right = save_bufsize.X - 1;
 | |
|       if (!ReadConsoleOutputWrapper (h, save_buf, save_bufsize, now))
 | |
| 	debug_printf ("ReadConsoleOutputWrapper(h, ...) failed during save, %E");
 | |
| 
 | |
|       /* Position at top of buffer */
 | |
|       COORD cob = {};
 | |
|       if (!SetConsoleCursorPosition (h, cob))
 | |
| 	debug_printf ("SetConsoleCursorInfo(%p, ...) failed during save, %E", h);
 | |
| 
 | |
|       /* Clear entire buffer */
 | |
|       clear_screen (h, 0, 0, now.Right, now.Bottom);
 | |
|       b.dwCursorPosition.X = b.dwCursorPosition.Y = dwEnd.X = dwEnd.Y = 0;
 | |
|     }
 | |
|   else if (save_buf)
 | |
|     {
 | |
|       COORD cob = {};
 | |
|       SMALL_RECT now = {};
 | |
|       now.Bottom = save_bufsize.Y - 1;
 | |
|       now.Right = save_bufsize.X - 1;
 | |
|       /* Restore whole buffer */
 | |
|       clear_screen (h, 0, 0, b.dwSize.X - 1, b.dwSize.Y - 1);
 | |
|       BOOL res = WriteConsoleOutputW (h, save_buf, save_bufsize, cob, &now);
 | |
|       if (!res)
 | |
| 	debug_printf ("WriteConsoleOutputW failed, %E");
 | |
| 
 | |
|       cfree (save_buf);
 | |
|       save_buf = NULL;
 | |
| 
 | |
|       cob.X = 0;
 | |
|       cob.Y = save_top;
 | |
|       /* CGF: NOOP?  Doesn't seem to position screen as expected */
 | |
|       /* Temporarily position at top of screen */
 | |
|       if (!SetConsoleCursorPosition (h, cob))
 | |
| 	debug_printf ("SetConsoleCursorInfo(%p, cob) failed during restore, %E", h);
 | |
|       /* Position where we were previously */
 | |
|       if (!SetConsoleCursorPosition (h, save_cursor))
 | |
| 	debug_printf ("SetConsoleCursorInfo(%p, save_cursor) failed during restore, %E", h);
 | |
|       /* Get back correct version of buffer information */
 | |
|       dwEnd.X = dwEnd.Y = 0;
 | |
|       fillin (h);
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define BAK 1
 | |
| #define ESC 2
 | |
| #define NOR 0
 | |
| #define IGN 4
 | |
| #if 1
 | |
| #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
 | |
| #define SO 14
 | |
| #define SI 15
 | |
| 
 | |
| 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,  SO,  SI,
 | |
| /*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, n;
 | |
|   char buf[40];
 | |
| 
 | |
|   switch (c)
 | |
|     {
 | |
|     case 'm':   /* Set Graphics Rendition */
 | |
|        for (int i = 0; i <= con.nargs; i++)
 | |
| 	 switch (con.args[i])
 | |
| 	   {
 | |
| 	     case 0:    /* normal color */
 | |
| 	       con.set_default_attr ();
 | |
| 	       break;
 | |
| 	     case 1:    /* bold */
 | |
| 	       con.intensity = INTENSITY_BOLD;
 | |
| 	       break;
 | |
| 	     case 2:	/* dim */
 | |
| 	       con.intensity = INTENSITY_DIM;
 | |
| 	       break;
 | |
| 	     case 4:	/* underlined */
 | |
| 	       con.underline = 1;
 | |
| 	       break;
 | |
| 	     case 5:    /* blink mode */
 | |
| 	       con.blink = true;
 | |
| 	       break;
 | |
| 	     case 7:    /* reverse */
 | |
| 	       con.reverse = true;
 | |
| 	       break;
 | |
| 	     case 8:    /* invisible */
 | |
| 	       con.intensity = INTENSITY_INVISIBLE;
 | |
| 	       break;
 | |
| 	     case 10:   /* end alternate charset */
 | |
| 	       con.alternate_charset_active = false;
 | |
| 	       break;
 | |
| 	     case 11:   /* start alternate charset */
 | |
| 	       con.alternate_charset_active = true;
 | |
| 	       break;
 | |
| 	     case 22:
 | |
| 	     case 28:
 | |
| 	       con.intensity = INTENSITY_NORMAL;
 | |
| 	       break;
 | |
| 	     case 24:
 | |
| 	       con.underline = false;
 | |
| 	       break;
 | |
| 	     case 25:
 | |
| 	       con.blink = false;
 | |
| 	       break;
 | |
| 	     case 27:
 | |
| 	       con.reverse = false;
 | |
| 	       break;
 | |
| 	     case 30:		/* BLACK foreground */
 | |
| 	       con.fg = 0;
 | |
| 	       break;
 | |
| 	     case 31:		/* RED foreground */
 | |
| 	       con.fg = FOREGROUND_RED;
 | |
| 	       break;
 | |
| 	     case 32:		/* GREEN foreground */
 | |
| 	       con.fg = FOREGROUND_GREEN;
 | |
| 	       break;
 | |
| 	     case 33:		/* YELLOW foreground */
 | |
| 	       con.fg = FOREGROUND_RED | FOREGROUND_GREEN;
 | |
| 	       break;
 | |
| 	     case 34:		/* BLUE foreground */
 | |
| 	       con.fg = FOREGROUND_BLUE;
 | |
| 	       break;
 | |
| 	     case 35:		/* MAGENTA foreground */
 | |
| 	       con.fg = FOREGROUND_RED | FOREGROUND_BLUE;
 | |
| 	       break;
 | |
| 	     case 36:		/* CYAN foreground */
 | |
| 	       con.fg = FOREGROUND_BLUE | FOREGROUND_GREEN;
 | |
| 	       break;
 | |
| 	     case 37:		/* WHITE foreg */
 | |
| 	       con.fg = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
 | |
| 	       break;
 | |
| 	     case 39:
 | |
| 	       con.fg = con.default_color & FOREGROUND_ATTR_MASK;
 | |
| 	       break;
 | |
| 	     case 40:		/* BLACK background */
 | |
| 	       con.bg = 0;
 | |
| 	       break;
 | |
| 	     case 41:		/* RED background */
 | |
| 	       con.bg = BACKGROUND_RED;
 | |
| 	       break;
 | |
| 	     case 42:		/* GREEN background */
 | |
| 	       con.bg = BACKGROUND_GREEN;
 | |
| 	       break;
 | |
| 	     case 43:		/* YELLOW background */
 | |
| 	       con.bg = BACKGROUND_RED | BACKGROUND_GREEN;
 | |
| 	       break;
 | |
| 	     case 44:		/* BLUE background */
 | |
| 	       con.bg = BACKGROUND_BLUE;
 | |
| 	       break;
 | |
| 	     case 45:		/* MAGENTA background */
 | |
| 	       con.bg = BACKGROUND_RED | BACKGROUND_BLUE;
 | |
| 	       break;
 | |
| 	     case 46:		/* CYAN background */
 | |
| 	       con.bg = BACKGROUND_BLUE | BACKGROUND_GREEN;
 | |
| 	       break;
 | |
| 	     case 47:    /* WHITE background */
 | |
| 	       con.bg = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
 | |
| 	       break;
 | |
| 	     case 49:
 | |
| 	       con.bg = con.default_color & BACKGROUND_ATTR_MASK;
 | |
| 	       break;
 | |
| 	   }
 | |
|        con.set_color (get_output_handle ());
 | |
|       break;
 | |
|     case 'q': /* Set cursor style (DECSCUSR) */
 | |
|       if (con.saw_space)
 | |
| 	{
 | |
| 	    CONSOLE_CURSOR_INFO console_cursor_info;
 | |
| 	    GetConsoleCursorInfo (get_output_handle (), &console_cursor_info);
 | |
| 	    switch (con.args[0])
 | |
| 	      {
 | |
| 		case 0: /* blinking block */
 | |
| 		case 1: /* blinking block (default) */
 | |
| 		case 2: /* steady block */
 | |
| 		  console_cursor_info.dwSize = 100;
 | |
| 		  SetConsoleCursorInfo (get_output_handle (), &console_cursor_info);
 | |
| 		  break;
 | |
| 		case 3: /* blinking underline */
 | |
| 		case 4: /* steady underline */
 | |
| 		  console_cursor_info.dwSize = 10;	/* or Windows default 25? */
 | |
| 		  SetConsoleCursorInfo (get_output_handle (), &console_cursor_info);
 | |
| 		  break;
 | |
| 		default: /* use value as percentage */
 | |
| 		  console_cursor_info.dwSize = con.args[0];
 | |
| 		  SetConsoleCursorInfo (get_output_handle (), &console_cursor_info);
 | |
| 		  break;
 | |
| 	      }
 | |
| 	}
 | |
|       break;
 | |
|     case 'h':
 | |
|     case 'l':
 | |
|       if (!con.saw_question_mark)
 | |
| 	{
 | |
| 	  switch (con.args[0])
 | |
| 	    {
 | |
| 	    case 4:    /* Insert mode */
 | |
| 	      con.insert_mode = (c == 'h') ? true : false;
 | |
| 	      syscall_printf ("insert mode %sabled", con.insert_mode ? "en" : "dis");
 | |
| 	      break;
 | |
| 	    }
 | |
| 	  break;
 | |
| 	}
 | |
|       switch (con.args[0])
 | |
| 	{
 | |
| 	case 25: /* Show/Hide Cursor (DECTCEM) */
 | |
| 	  {
 | |
| 	    CONSOLE_CURSOR_INFO console_cursor_info;
 | |
| 	    GetConsoleCursorInfo (get_output_handle (), & console_cursor_info);
 | |
| 	    if (c == 'h')
 | |
| 	      console_cursor_info.bVisible = TRUE;
 | |
| 	    else
 | |
| 	      console_cursor_info.bVisible = FALSE;
 | |
| 	    SetConsoleCursorInfo (get_output_handle (), & console_cursor_info);
 | |
| 	    break;
 | |
| 	  }
 | |
| 	case 47:   /* Save/Restore screen */
 | |
| 	  con.save_restore (get_output_handle (), c);
 | |
| 	  break;
 | |
| 
 | |
| 	case 67: /* DECBKM ("DEC Backarrow Key Mode") */
 | |
| 	  con.backspace_keycode = (c == 'h' ? CTRL('H') : CERASE);
 | |
| 	  break;
 | |
| 
 | |
| 	case 1000: /* Mouse tracking */
 | |
| 	  con.use_mouse = (c == 'h') ? 1 : 0;
 | |
| 	  break;
 | |
| 
 | |
| 	case 1002: /* Mouse button event tracking */
 | |
| 	  con.use_mouse = (c == 'h') ? 2 : 0;
 | |
| 	  break;
 | |
| 
 | |
| 	case 1003: /* Mouse any event tracking */
 | |
| 	  con.use_mouse = (c == 'h') ? 3 : 0;
 | |
| 	  break;
 | |
| 
 | |
| 	case 1004: /* Focus in/out event reporting */
 | |
| 	  con.use_focus = (c == 'h') ? true : false;
 | |
| 	  break;
 | |
| 
 | |
| 	case 1005: /* Extended mouse mode */
 | |
| 	  con.ext_mouse_mode5 = c == 'h';
 | |
| 	  break;
 | |
| 
 | |
| 	case 1006: /* SGR extended mouse mode */
 | |
| 	  con.ext_mouse_mode6 = c == 'h';
 | |
| 	  break;
 | |
| 
 | |
| 	case 1015: /* Urxvt extended mouse mode */
 | |
| 	  con.ext_mouse_mode15 = c == 'h';
 | |
| 	  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", con.args[0]);
 | |
| 	  break;
 | |
| 	}
 | |
|       break;
 | |
|     case 'J':
 | |
|       switch (con.args[0])
 | |
| 	{
 | |
| 	case 0:			/* Clear to end of screen */
 | |
| 	  clear_screen (cl_curr_pos, cl_curr_pos, cl_disp_end, cl_disp_end);
 | |
| 	  break;
 | |
| 	case 1:			/* Clear from beginning of screen to cursor */
 | |
| 	  clear_screen (cl_disp_beg, cl_disp_beg, cl_curr_pos, cl_curr_pos);
 | |
| 	  break;
 | |
| 	case 2:			/* Clear screen */
 | |
| 	  cursor_get (&x, &y);
 | |
| 	  clear_screen (cl_disp_beg, cl_disp_beg, cl_disp_end, cl_disp_end);
 | |
| 	  cursor_set (false, x, y);
 | |
| 	  break;
 | |
| 	default:
 | |
| 	  goto bad_escape;
 | |
| 	}
 | |
|       break;
 | |
| 
 | |
|     case 'A':
 | |
|       cursor_rel (0, -(con.args[0] ?: 1));
 | |
|       break;
 | |
|     case 'B':
 | |
|       cursor_rel (0, con.args[0] ?: 1);
 | |
|       break;
 | |
|     case 'C':
 | |
|       cursor_rel (con.args[0] ?: 1, 0);
 | |
|       break;
 | |
|     case 'D':
 | |
|       cursor_rel (-(con.args[0] ?: 1),0);
 | |
|       break;
 | |
|     case 'K':
 | |
|       switch (con.args[0])
 | |
| 	{
 | |
| 	  case 0:		/* Clear to end of line */
 | |
| 	    clear_screen (cl_curr_pos, cl_curr_pos, cl_disp_end, cl_curr_pos);
 | |
| 	    break;
 | |
| 	  case 2:		/* Clear line */
 | |
| 	    clear_screen (cl_disp_beg, cl_curr_pos, cl_disp_end, cl_curr_pos);
 | |
| 	    break;
 | |
| 	  case 1:		/* Clear from bol to cursor */
 | |
| 	    clear_screen (cl_disp_beg, cl_curr_pos, cl_curr_pos, cl_curr_pos);
 | |
| 	    break;
 | |
| 	  default:
 | |
| 	    goto bad_escape;
 | |
| 	}
 | |
|       break;
 | |
|     case 'H':
 | |
|     case 'f':
 | |
|       cursor_set (true, (con.args[1] ?: 1) - 1,
 | |
| 			(con.args[0] ?: 1) - 1);
 | |
|       break;
 | |
|     case 'G':   /* hpa - position cursor at column n - 1 */
 | |
|       cursor_get (&x, &y);
 | |
|       cursor_set (false, (con.args[0] ? con.args[0] - 1 : 0), y);
 | |
|       break;
 | |
|     case 'd':   /* vpa - position cursor at line n */
 | |
|       cursor_get (&x, &y);
 | |
|       cursor_set (true, x, (con.args[0] ? con.args[0] - 1 : 0));
 | |
|       break;
 | |
|     case 's':   /* Save cursor position */
 | |
|       cursor_get (&con.savex, &con.savey);
 | |
|       con.savey -= con.b.srWindow.Top;
 | |
|       break;
 | |
|     case 'u':   /* Restore cursor position */
 | |
|       cursor_set (true, con.savex, con.savey);
 | |
|       break;
 | |
|     case 'I':	/* TAB */
 | |
|       cursor_get (&x, &y);
 | |
|       cursor_set (false, 8 * (x / 8 + 1), y);
 | |
|       break;
 | |
|     case 'L':				/* AL - insert blank lines */
 | |
|       n = con.args[0] ?: 1;
 | |
|       cursor_get (&x, &y);
 | |
|       scroll_buffer (0, y, -1, -1, 0, y + n);
 | |
|       break;
 | |
|     case 'M':				/* DL - delete lines */
 | |
|       n = con.args[0] ?: 1;
 | |
|       cursor_get (&x, &y);
 | |
|       scroll_buffer (0, y + n, -1, -1, 0, y);
 | |
|       break;
 | |
|     case '@':				/* IC - insert chars */
 | |
|       n = con.args[0] ?: 1;
 | |
|       cursor_get (&x, &y);
 | |
|       scroll_buffer (x, y, -1, y, x + n, y);
 | |
|       break;
 | |
|     case 'P':				/* DC - delete chars */
 | |
|       n = con.args[0] ?: 1;
 | |
|       cursor_get (&x, &y);
 | |
|       scroll_buffer (x + n, y, -1, y, x, y);
 | |
|       break;
 | |
|     case 'S':				/* SF - Scroll forward */
 | |
|       n = con.args[0] ?: 1;
 | |
|       scroll_buffer_screen (0, n, -1, -1, 0, 0);
 | |
|       break;
 | |
|     case 'T':				/* SR - Scroll down */
 | |
|       con.fillin (get_output_handle ());
 | |
|       n = con.b.srWindow.Top + con.args[0] ?: 1;
 | |
|       scroll_buffer_screen (0, 0, -1, -1, 0, n);
 | |
|       break;
 | |
|     case 'X':				/* ec - erase chars */
 | |
|       n = con.args[0] ?: 1;
 | |
|       cursor_get (&x, &y);
 | |
|       scroll_buffer (x + n, y, -1, y, x, y);
 | |
|       scroll_buffer (x, y, -1, y, x + n, 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 (con.insert_mode)
 | |
| 	{
 | |
| 	  cursor_get (&x, &y);
 | |
| 	  scroll_buffer (x, y, -1, y, x + con.args[1], y);
 | |
| 	}
 | |
|       while (con.args[1]--)
 | |
| 	WriteFile (get_output_handle (), &con.args[0], 1, (DWORD *) &x, 0);
 | |
|       break;
 | |
|     case 'c':				/* u9 - Terminal enquire string */
 | |
|       if (con.saw_greater_than_sign)
 | |
| 	/* Generate Secondary Device Attribute report, using 67 = ASCII 'C'
 | |
| 	   to indicate Cygwin (convention used by Rxvt, Urxvt, Screen, Mintty),
 | |
| 	   and cygwin version for terminal version. */
 | |
| 	__small_sprintf (buf, "\033[>67;%d%02d;0c", CYGWIN_VERSION_DLL_MAJOR, CYGWIN_VERSION_DLL_MINOR);
 | |
|       else
 | |
| 	strcpy (buf, "\033[?6c");
 | |
|       /* The generated report needs to be injected for read-ahead into the
 | |
| 	 fhandler_console object associated with standard input.
 | |
| 	 The current call does not work. */
 | |
|       puts_readahead (buf);
 | |
|       break;
 | |
|     case 'n':
 | |
|       switch (con.args[0])
 | |
| 	{
 | |
| 	case 6:				/* u7 - Cursor position request */
 | |
| 	  cursor_get (&x, &y);
 | |
| 	  y -= con.b.srWindow.Top;
 | |
| 	  /* x -= con.b.srWindow.Left;		// 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 */
 | |
|       con.scroll_region.Top = con.args[0] ? con.args[0] - 1 : 0;
 | |
|       con.scroll_region.Bottom = con.args[1] ? con.args[1] - 1 : -1;
 | |
|       cursor_set (true, 0, 0);
 | |
|       break;
 | |
|     case 'g':				/* TAB set/clear */
 | |
|       break;
 | |
|     default:
 | |
| bad_escape:
 | |
|       break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* This gets called when we found an invalid input character.  We just
 | |
|    print a half filled square (UTF 0x2592).  We have no chance to figure
 | |
|    out the "meaning" of the input char anyway. */
 | |
| inline void
 | |
| fhandler_console::write_replacement_char ()
 | |
| {
 | |
|   static const wchar_t replacement_char = 0x2592; /* Half filled square */
 | |
|   DWORD done;
 | |
|   WriteConsoleW (get_output_handle (), &replacement_char, 1, &done, 0);
 | |
| }
 | |
| 
 | |
| 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;
 | |
|   DWORD buf_len;
 | |
|   const unsigned char *found = src;
 | |
|   size_t ret;
 | |
|   mbstate_t ps;
 | |
|   UINT cp = con.get_console_cp ();
 | |
|   const char *charset;
 | |
|   mbtowc_p f_mbtowc;
 | |
| 
 | |
|   if (cp)
 | |
|     {
 | |
|       /* The alternate charset is always 437, just as in the Linux console. */
 | |
|       f_mbtowc = __cp_mbtowc;
 | |
|       charset = "CP437";
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       f_mbtowc = cygheap->locale.mbtowc;
 | |
|       charset = cygheap->locale.charset;
 | |
|     }
 | |
| 
 | |
|   /* First check if we have cached lead bytes of a former try to write
 | |
|      a truncated multibyte sequence.  If so, process it. */
 | |
|   if (trunc_buf.len)
 | |
|     {
 | |
|       const unsigned char *nfound;
 | |
|       int cp_len = MIN (end - src, 4 - trunc_buf.len);
 | |
|       memcpy (trunc_buf.buf + trunc_buf.len, src, cp_len);
 | |
|       memset (&ps, 0, sizeof ps);
 | |
|       switch (ret = f_mbtowc (_REENT, NULL, (const char *) trunc_buf.buf,
 | |
| 			       trunc_buf.len + cp_len, charset, &ps))
 | |
| 	{
 | |
| 	case -2:
 | |
| 	  /* Still truncated multibyte sequence?  Keep in trunc_buf. */
 | |
| 	  trunc_buf.len += cp_len;
 | |
| 	  return end;
 | |
| 	case -1:
 | |
| 	  /* Give up, print replacement chars for trunc_buf... */
 | |
| 	  for (int i = 0; i < trunc_buf.len; ++i)
 | |
| 	    write_replacement_char ();
 | |
| 	  /* ... mark trunc_buf as unused... */
 | |
| 	  trunc_buf.len = 0;
 | |
| 	  /* ... and proceed. */
 | |
| 	  nfound = NULL;
 | |
| 	  break;
 | |
| 	case 0:
 | |
| 	  nfound = trunc_buf.buf + 1;
 | |
| 	  break;
 | |
| 	default:
 | |
| 	  nfound = trunc_buf.buf + ret;
 | |
| 	  break;
 | |
| 	}
 | |
|       /* Valid multibyte sequence?  Process. */
 | |
|       if (nfound)
 | |
| 	{
 | |
| 	  buf_len = con.str_to_con (f_mbtowc, charset, write_buf,
 | |
| 					   (const char *) trunc_buf.buf,
 | |
| 					   nfound - trunc_buf.buf);
 | |
| 	  if (!write_console (write_buf, buf_len, done))
 | |
| 	    {
 | |
| 	      debug_printf ("multibyte sequence write failed, handle %p", get_output_handle ());
 | |
| 	      return 0;
 | |
| 	    }
 | |
| 	  found = src + (nfound - trunc_buf.buf - trunc_buf.len);
 | |
| 	  trunc_buf.len = 0;
 | |
| 	  return found;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /* Loop over src buffer as long as we have just simple characters.  Stop
 | |
|      as soon as we reach the conversion limit, or if we encounter a control
 | |
|      character or a truncated or invalid mutibyte sequence. */
 | |
|   memset (&ps, 0, sizeof ps);
 | |
|   while (found < end
 | |
| 	 && found - src < CONVERT_LIMIT
 | |
| 	 && base_chars[*found] == NOR)
 | |
|     {
 | |
|       switch (ret = f_mbtowc (_REENT, NULL, (const char *) found,
 | |
| 			       end - found, charset, &ps))
 | |
| 	{
 | |
| 	case -2: /* Truncated multibyte sequence.  Store for next write. */
 | |
| 	  trunc_buf.len = end - found;
 | |
| 	  memcpy (trunc_buf.buf, found, trunc_buf.len);
 | |
| 	  goto do_print;
 | |
| 	case -1: /* Invalid multibyte sequence. Handled below. */
 | |
| 	  goto do_print;
 | |
| 	case 0:
 | |
| 	  found++;
 | |
| 	  break;
 | |
| 	default:
 | |
| 	  found += ret;
 | |
| 	  break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
| do_print:
 | |
| 
 | |
|   /* Print all the base characters out */
 | |
|   if (found != src)
 | |
|     {
 | |
|       DWORD len = found - src;
 | |
|       buf_len = con.str_to_con (f_mbtowc, charset, write_buf,
 | |
| 				       (const char *) src, len);
 | |
|       if (!buf_len)
 | |
| 	{
 | |
| 	  debug_printf ("conversion error, handle %p",
 | |
| 			get_output_handle ());
 | |
| 	  __seterrno ();
 | |
| 	  return 0;
 | |
| 	}
 | |
| 
 | |
|       if (con.insert_mode)
 | |
| 	{
 | |
| 	  int x, y;
 | |
| 	  cursor_get (&x, &y);
 | |
| 	  scroll_buffer (x, y, -1, y, x + buf_len, y);
 | |
| 	}
 | |
| 
 | |
|       if (!write_console (write_buf, buf_len, done))
 | |
| 	{
 | |
| 	  debug_printf ("write failed, handle %p", get_output_handle ());
 | |
| 	  return 0;
 | |
| 	}
 | |
|       /* Stop here if we reached the conversion limit. */
 | |
|       if (len >= CONVERT_LIMIT)
 | |
| 	return found + trunc_buf.len;
 | |
|     }
 | |
|   /* If there's still something in the src buffer, but it's not a truncated
 | |
|      multibyte sequence, then we stumbled over a control character or an
 | |
|      invalid multibyte sequence.  Print it. */
 | |
|   if (found < end && trunc_buf.len == 0)
 | |
|     {
 | |
|       int x, y;
 | |
|       switch (base_chars[*found])
 | |
| 	{
 | |
| 	case SO:	/* Shift Out: Invoke G1 character set (ISO 2022) */
 | |
| 	  con.iso_2022_G1 = true;
 | |
| 	  break;
 | |
| 	case SI:	/* Shift In: Invoke G0 character set (ISO 2022) */
 | |
| 	  con.iso_2022_G1 = false;
 | |
| 	  break;
 | |
| 	case BEL:
 | |
| 	  beep ();
 | |
| 	  break;
 | |
| 	case ESC:
 | |
| 	  con.state = gotesc;
 | |
| 	  break;
 | |
| 	case DWN:
 | |
| 	  cursor_get (&x, &y);
 | |
| 	  if (y >= srBottom)
 | |
| 	    {
 | |
| 	      if (y >= con.b.srWindow.Bottom && !con.scroll_region.Top)
 | |
| 		WriteConsoleW (get_output_handle (), L"\n", 1, &done, 0);
 | |
| 	      else
 | |
| 		{
 | |
| 		  scroll_buffer (0, srTop + 1, -1, srBottom, 0, srTop);
 | |
| 		  y--;
 | |
| 		}
 | |
| 	    }
 | |
| 	  cursor_set (false, ((get_ttyp ()->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:
 | |
| 	  /* Don't print chars marked as ERR chars, except for a ASCII CAN
 | |
| 	     sequence which is printed as singlebyte chars from the UTF
 | |
| 	     Basic Latin and Latin 1 Supplement plains. */
 | |
| 	  if (*found == 0x18)
 | |
| 	    {
 | |
| 	      write_replacement_char ();
 | |
| 	      if (found + 1 < end)
 | |
| 		{
 | |
| 		  ret = __utf8_mbtowc (_REENT, NULL, (const char *) found + 1,
 | |
| 				       end - found - 1, NULL, &ps);
 | |
| 		  if (ret != (size_t) -1)
 | |
| 		    while (ret-- > 0)
 | |
| 		      {
 | |
| 			WCHAR w = *(found + 1);
 | |
| 			WriteConsoleW (get_output_handle (), &w, 1, &done, 0);
 | |
| 			found++;
 | |
| 		      }
 | |
| 		}
 | |
| 	    }
 | |
| 	  break;
 | |
| 	case TAB:
 | |
| 	  cursor_get (&x, &y);
 | |
| 	  cursor_set (false, 8 * (x / 8 + 1), y);
 | |
| 	  break;
 | |
| 	case NOR:
 | |
| 	  write_replacement_char ();
 | |
| 	  break;
 | |
| 	}
 | |
|       found++;
 | |
|     }
 | |
|   return found + trunc_buf.len;
 | |
| }
 | |
| 
 | |
| ssize_t __stdcall
 | |
| fhandler_console::write (const void *vsrc, size_t len)
 | |
| {
 | |
|   bg_check_types bg = bg_check (SIGTTOU);
 | |
|   if (bg <= bg_eof)
 | |
|     return (ssize_t) bg;
 | |
| 
 | |
|   push_process_state process_state (PID_TTYOU);
 | |
| 
 | |
|   /* Run and check for ansi sequences */
 | |
|   unsigned const char *src = (unsigned char *) vsrc;
 | |
|   unsigned const char *end = src + len;
 | |
|   /* This might look a bit far fetched, but using the TLS path buffer allows
 | |
|      to allocate a big buffer without using the stack too much.  Doing it here
 | |
|      in write instead of in write_normal should be faster, too. */
 | |
|   tmp_pathbuf tp;
 | |
|   write_buf = tp.w_get ();
 | |
| 
 | |
|   debug_printf ("%p, %ld", vsrc, len);
 | |
| 
 | |
|   while (src < end)
 | |
|     {
 | |
|       paranoid_printf ("char %0c state is %d", *src, con.state);
 | |
|       switch (con.state)
 | |
| 	{
 | |
| 	case normal:
 | |
| 	  src = write_normal (src, end);
 | |
| 	  if (!src) /* write_normal failed */
 | |
| 	    return -1;
 | |
| 	  break;
 | |
| 	case gotesc:
 | |
| 	  if (*src == '[')		/* CSI Control Sequence Introducer */
 | |
| 	    {
 | |
| 	      con.state = gotsquare;
 | |
| 	      con.saw_question_mark = false;
 | |
| 	      con.saw_greater_than_sign = false;
 | |
| 	      con.saw_space = false;
 | |
| 	      for (con.nargs = 0; con.nargs < MAXARGS; con.nargs++)
 | |
| 		con.args[con.nargs] = 0;
 | |
| 	      con.nargs = 0;
 | |
| 	    }
 | |
| 	  else if (*src == ']')		/* OSC Operating System Command */
 | |
| 	    {
 | |
| 	      con.rarg = 0;
 | |
| 	      con.my_title_buf[0] = '\0';
 | |
| 	      con.state = gotrsquare;
 | |
| 	    }
 | |
| 	  else if (*src == '(')		/* Designate G0 character set */
 | |
| 	    {
 | |
| 	      con.state = gotparen;
 | |
| 	    }
 | |
| 	  else if (*src == ')')		/* Designate G1 character set */
 | |
| 	    {
 | |
| 	      con.state = gotrparen;
 | |
| 	    }
 | |
| 	  else if (*src == 'M')		/* Reverse Index (scroll down) */
 | |
| 	    {
 | |
| 	      con.fillin (get_output_handle ());
 | |
| 	      scroll_buffer_screen (0, 0, -1, -1, 0, 1);
 | |
| 	      con.state = normal;
 | |
| 	    }
 | |
| 	  else if (*src == 'c')		/* RIS Full Reset */
 | |
| 	    {
 | |
| 	      con.set_default_attr ();
 | |
| 	      con.vt100_graphics_mode_G0 = false;
 | |
| 	      con.vt100_graphics_mode_G1 = false;
 | |
| 	      con.iso_2022_G1 = false;
 | |
| 	      cursor_set (false, 0, 0);
 | |
| 	      clear_screen (cl_buf_beg, cl_buf_beg, cl_buf_end, cl_buf_end);
 | |
| 	      con.state = normal;
 | |
| 	    }
 | |
| 	  else if (*src == '8')		/* DECRC Restore cursor position */
 | |
| 	    {
 | |
| 	      cursor_set (false, con.savex, con.savey);
 | |
| 	      con.state = normal;
 | |
| 	    }
 | |
| 	  else if (*src == '7')		/* DECSC Save cursor position */
 | |
| 	    {
 | |
| 	      cursor_get (&con.savex, &con.savey);
 | |
| 	      con.state = normal;
 | |
| 	    }
 | |
| 	  else if (*src == 'R')		/* ? */
 | |
| 	      con.state = normal;
 | |
| 	  else
 | |
| 	    {
 | |
| 	      con.state = normal;
 | |
| 	    }
 | |
| 	  src++;
 | |
| 	  break;
 | |
| 	case gotarg1:
 | |
| 	  if (isdigit (*src))
 | |
| 	    {
 | |
| 	      con.args[con.nargs] = con.args[con.nargs] * 10 + *src - '0';
 | |
| 	      src++;
 | |
| 	    }
 | |
| 	  else if (*src == ';')
 | |
| 	    {
 | |
| 	      src++;
 | |
| 	      con.nargs++;
 | |
| 	      if (con.nargs >= MAXARGS)
 | |
| 		con.nargs--;
 | |
| 	    }
 | |
| 	  else if (*src == ' ')
 | |
| 	    {
 | |
| 	      src++;
 | |
| 	      con.saw_space = true;
 | |
| 	      con.state = gotcommand;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    con.state = gotcommand;
 | |
| 	  break;
 | |
| 	case gotcommand:
 | |
| 	  char_command (*src++);
 | |
| 	  con.state = normal;
 | |
| 	  break;
 | |
| 	case gotrsquare:
 | |
| 	  if (isdigit (*src))
 | |
| 	    con.rarg = con.rarg * 10 + (*src - '0');
 | |
| 	  else if (*src == ';' && (con.rarg == 2 || con.rarg == 0))
 | |
| 	    con.state = gettitle;
 | |
| 	  else
 | |
| 	    con.state = eattitle;
 | |
| 	  src++;
 | |
| 	  break;
 | |
| 	case eattitle:
 | |
| 	case gettitle:
 | |
| 	  {
 | |
| 	    int n = strlen (con.my_title_buf);
 | |
| 	    if (*src < ' ')
 | |
| 	      {
 | |
| 		if (*src == '\007' && con.state == gettitle)
 | |
| 		  set_console_title (con.my_title_buf);
 | |
| 		con.state = normal;
 | |
| 	      }
 | |
| 	    else if (n < TITLESIZE)
 | |
| 	      {
 | |
| 		con.my_title_buf[n++] = *src;
 | |
| 		con.my_title_buf[n] = '\0';
 | |
| 	      }
 | |
| 	    src++;
 | |
| 	    break;
 | |
| 	  }
 | |
| 	case gotsquare:
 | |
| 	  if (*src == ';')
 | |
| 	    {
 | |
| 	      con.state = gotarg1;
 | |
| 	      con.nargs++;
 | |
| 	      src++;
 | |
| 	    }
 | |
| 	  else if (isalpha (*src))
 | |
| 	    con.state = gotcommand;
 | |
| 	  else if (*src != '@' && !isalpha (*src) && !isdigit (*src))
 | |
| 	    {
 | |
| 	      if (*src == '?')
 | |
| 		con.saw_question_mark = true;
 | |
| 	      else if (*src == '>')
 | |
| 		con.saw_greater_than_sign = true;
 | |
| 	      /* ignore any extra chars between [ and first arg or command */
 | |
| 	      src++;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    con.state = gotarg1;
 | |
| 	  break;
 | |
| 	case gotparen:	/* Designate G0 Character Set (ISO 2022) */
 | |
| 	  if (*src == '0')
 | |
| 	    con.vt100_graphics_mode_G0 = true;
 | |
| 	  else
 | |
| 	    con.vt100_graphics_mode_G0 = false;
 | |
| 	  con.state = normal;
 | |
| 	  src++;
 | |
| 	  break;
 | |
| 	case gotrparen:	/* Designate G1 Character Set (ISO 2022) */
 | |
| 	  if (*src == '0')
 | |
| 	    con.vt100_graphics_mode_G1 = true;
 | |
| 	  else
 | |
| 	    con.vt100_graphics_mode_G1 = false;
 | |
| 	  con.state = normal;
 | |
| 	  src++;
 | |
| 	  break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   syscall_printf ("%ld = fhandler_console::write(...)", len);
 | |
| 
 | |
|   return len;
 | |
| }
 | |
| 
 | |
| static const struct {
 | |
|   int vk;
 | |
|   const char *val[4];
 | |
| } keytable[] = {
 | |
| 	       /* NORMAL */    /* SHIFT */     /* CTRL */     /* CTRL-SHIFT */
 | |
|   /* Unmodified and Alt-modified keypad keys comply with linux console
 | |
|      SHIFT, CTRL, CTRL-SHIFT modifiers comply with xterm modifier usage */
 | |
|   {VK_NUMPAD5,	{"\033[G",	"\033[1;2G",	"\033[1;5G",	"\033[1;6G"}},
 | |
|   {VK_CLEAR,	{"\033[G",	"\033[1;2G",	"\033[1;5G",	"\033[1;6G"}},
 | |
|   {VK_LEFT,	{"\033[D",	"\033[1;2D",	"\033[1;5D",	"\033[1;6D"}},
 | |
|   {VK_RIGHT,	{"\033[C",	"\033[1;2C",	"\033[1;5C",	"\033[1;6C"}},
 | |
|   {VK_UP,	{"\033[A",	"\033[1;2A",	"\033[1;5A",	"\033[1;6A"}},
 | |
|   {VK_DOWN,	{"\033[B",	"\033[1;2B",	"\033[1;5B",	"\033[1;6B"}},
 | |
|   {VK_PRIOR,	{"\033[5~",	"\033[5;2~",	"\033[5;5~",	"\033[5;6~"}},
 | |
|   {VK_NEXT,	{"\033[6~",	"\033[6;2~",	"\033[6;5~",	"\033[6;6~"}},
 | |
|   {VK_HOME,	{"\033[1~",	"\033[1;2~",	"\033[1;5~",	"\033[1;6~"}},
 | |
|   {VK_END,	{"\033[4~",	"\033[4;2~",	"\033[4;5~",	"\033[4;6~"}},
 | |
|   {VK_INSERT,	{"\033[2~",	"\033[2;2~",	"\033[2;5~",	"\033[2;6~"}},
 | |
|   {VK_DELETE,	{"\033[3~",	"\033[3;2~",	"\033[3;5~",	"\033[3;6~"}},
 | |
|   /* F1...F12, SHIFT-F1...SHIFT-F10 comply with linux console
 | |
|      F6...F12, and all modified F-keys comply with rxvt (compatible extension) */
 | |
|   {VK_F1,	{"\033[[A",	"\033[23~",	"\033[11^",	"\033[23^"}},
 | |
|   {VK_F2,	{"\033[[B",	"\033[24~",	"\033[12^",	"\033[24^"}},
 | |
|   {VK_F3,	{"\033[[C",	"\033[25~",	"\033[13^",	"\033[25^"}},
 | |
|   {VK_F4,	{"\033[[D",	"\033[26~",	"\033[14^",	"\033[26^"}},
 | |
|   {VK_F5,	{"\033[[E",	"\033[28~",	"\033[15^",	"\033[28^"}},
 | |
|   {VK_F6,	{"\033[17~",	"\033[29~",	"\033[17^",	"\033[29^"}},
 | |
|   {VK_F7,	{"\033[18~",	"\033[31~",	"\033[18^",	"\033[31^"}},
 | |
|   {VK_F8,	{"\033[19~",	"\033[32~",	"\033[19^",	"\033[32^"}},
 | |
|   {VK_F9,	{"\033[20~",	"\033[33~",	"\033[20^",	"\033[33^"}},
 | |
|   {VK_F10,	{"\033[21~",	"\033[34~",	"\033[21^",	"\033[34^"}},
 | |
|   {VK_F11,	{"\033[23~",	"\033[23$",	"\033[23^",	"\033[23@"}},
 | |
|   {VK_F12,	{"\033[24~",	"\033[24$",	"\033[24^",	"\033[24@"}},
 | |
|   /* CTRL-6 complies with Windows cmd console but should be fixed */
 | |
|   {'6',		{NULL,		NULL,		"\036",		NULL}},
 | |
|   /* Table end marker */
 | |
|   {0}
 | |
| };
 | |
| 
 | |
| const char *
 | |
| get_nonascii_key (INPUT_RECORD& input_rec, char *tmp)
 | |
| {
 | |
| #define NORMAL  0
 | |
| #define SHIFT	1
 | |
| #define CONTROL	2
 | |
| /*#define CONTROLSHIFT	3*/
 | |
| 
 | |
|   int modifier_index = NORMAL;
 | |
|   if (input_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)
 | |
|     modifier_index = SHIFT;
 | |
|   if (input_rec.Event.KeyEvent.dwControlKeyState & CTRL_PRESSED)
 | |
|     modifier_index += CONTROL;
 | |
| 
 | |
|   for (int i = 0; keytable[i].vk; i++)
 | |
|     if (input_rec.Event.KeyEvent.wVirtualKeyCode == keytable[i].vk)
 | |
|       {
 | |
| 	if ((input_rec.Event.KeyEvent.dwControlKeyState & ALT_PRESSED)
 | |
| 	    && keytable[i].val[modifier_index] != NULL)
 | |
| 	  { /* Generic ESC prefixing if Alt is pressed */
 | |
| 	    tmp[0] = '\033';
 | |
| 	    strcpy (tmp + 1, keytable[i].val[modifier_index]);
 | |
| 	    return tmp;
 | |
| 	  }
 | |
| 	else
 | |
| 	  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;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::init (HANDLE h, DWORD a, mode_t bin)
 | |
| {
 | |
|   // this->fhandler_termios::init (h, 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_with_arch (flags | O_BINARY | (h ? 0 : O_NOCTTY));
 | |
| 
 | |
|   return !tcsetattr (0, &get_ttyp ()->ti);
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::igncr_enabled ()
 | |
| {
 | |
|   return get_ttyp ()->ti.c_iflag & IGNCR;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::set_close_on_exec (bool val)
 | |
| {
 | |
|   close_on_exec (val);
 | |
| }
 | |
| 
 | |
| void __stdcall
 | |
| set_console_title (char *title)
 | |
| {
 | |
|   wchar_t buf[TITLESIZE + 1];
 | |
|   sys_mbstowcs (buf, TITLESIZE + 1, title);
 | |
|   lock_ttys here (15000);
 | |
|   SetConsoleTitleW (buf);
 | |
|   debug_printf ("title '%W'", buf);
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::fixup_after_fork_exec (bool execing)
 | |
| {
 | |
|   set_unit ();
 | |
| }
 | |
| 
 | |
| // #define WINSTA_ACCESS (WINSTA_READATTRIBUTES | STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | WINSTA_CREATEDESKTOP | WINSTA_EXITWINDOWS)
 | |
| #define WINSTA_ACCESS WINSTA_ALL_ACCESS
 | |
| 
 | |
| /* Create a console in an invisible window station.  This should work
 | |
|    in all versions of Windows NT except Windows 7 (so far). */
 | |
| bool
 | |
| fhandler_console::create_invisible_console (HWINSTA horig)
 | |
| {
 | |
|   HWINSTA h = CreateWindowStationW (NULL, 0, WINSTA_ACCESS, NULL);
 | |
|   termios_printf ("%p = CreateWindowStation(NULL), %E", h);
 | |
|   BOOL b;
 | |
|   if (h)
 | |
|     {
 | |
|       b = SetProcessWindowStation (h);
 | |
|       termios_printf ("SetProcessWindowStation %d, %E", b);
 | |
|     }
 | |
|   b = AllocConsole ();	/* will cause flashing if CreateWindowStation
 | |
| 			   failed */
 | |
|   if (!h)
 | |
|     SetParent (GetConsoleWindow (), HWND_MESSAGE);
 | |
|   if (horig && h && h != horig && SetProcessWindowStation (horig))
 | |
|     CloseWindowStation (h);
 | |
|   termios_printf ("%d = AllocConsole (), %E", b);
 | |
|   invisible_console = true;
 | |
|   return b;
 | |
| }
 | |
| 
 | |
| /* Ugly workaround for Windows 7 and later.
 | |
| 
 | |
|    First try to just attach to any console which may have started this
 | |
|    app.  If that works use this as our "invisible console".
 | |
| 
 | |
|    This will fail if not started from the command prompt.  In that case, start
 | |
|    a dummy console application in a hidden state so that we can use its console
 | |
|    as our invisible console.  This probably works everywhere but process
 | |
|    creation is slow and to be avoided if possible so the window station method
 | |
|    is vastly preferred.
 | |
| 
 | |
|    FIXME: This is not completely thread-safe since it creates two inheritable
 | |
|    handles which are known only to this function.  If another thread starts
 | |
|    a process the new process will inherit these handles.  However, since this
 | |
|    function is currently only called at startup and during exec, it shouldn't
 | |
|    be a big deal.  */
 | |
| bool
 | |
| fhandler_console::create_invisible_console_workaround ()
 | |
| {
 | |
|   if (!AttachConsole (-1))
 | |
|     {
 | |
|       bool taskbar;
 | |
|       DWORD err = GetLastError ();
 | |
|       path_conv helper ("/bin/cygwin-console-helper.exe");
 | |
|       HANDLE hello = NULL;
 | |
|       HANDLE goodbye = NULL;
 | |
|       /* If err == ERROR_PROC_FOUND then this method won't work.  But that's
 | |
| 	 ok.  The window station method should work ok when AttachConsole doesn't
 | |
| 	 work.
 | |
| 
 | |
| 	 If the helper doesn't exist or we can't create event handles then we
 | |
| 	 can't use this method. */
 | |
|       if (err == ERROR_PROC_NOT_FOUND || !helper.exists ()
 | |
| 	  || !(hello = CreateEvent (&sec_none, true, false, NULL))
 | |
| 	  || !(goodbye = CreateEvent (&sec_none, true, false, NULL)))
 | |
| 	{
 | |
| 	  AllocConsole ();	/* This is just sanity check code.  We should
 | |
| 				   never actually hit here unless we're running
 | |
| 				   in an environment which lacks the helper
 | |
| 				   app. */
 | |
| 	  taskbar = true;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  STARTUPINFOW si = {};
 | |
| 	  PROCESS_INFORMATION pi;
 | |
| 	  size_t len = helper.get_wide_win32_path_len ();
 | |
| 	  WCHAR cmd[len + (2 * strlen (" 0xffffffff")) + 1];
 | |
| 	  WCHAR title[] = L"invisible cygwin console";
 | |
| 
 | |
| 	  helper.get_wide_win32_path (cmd);
 | |
| 	  __small_swprintf (cmd + len, L" %p %p", hello, goodbye);
 | |
| 
 | |
| 	  si.cb = sizeof (si);
 | |
| 	  si.dwFlags = STARTF_USESHOWWINDOW;
 | |
| 	  si.wShowWindow = SW_HIDE;
 | |
| 	  si.lpTitle = title;
 | |
| 
 | |
| 	  /* Create a new hidden process.  Use the two event handles as
 | |
| 	     argv[1] and argv[2]. */
 | |
| 	  BOOL x = CreateProcessW (NULL, cmd, &sec_none_nih, &sec_none_nih, true,
 | |
| 				   CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
 | |
| 	  if (x)
 | |
| 	    {
 | |
| 	      CloseHandle (pi.hProcess);	/* Don't need */
 | |
| 	      CloseHandle (pi.hThread);		/*  these.    */
 | |
| 	    }
 | |
| 	  taskbar = false;
 | |
| 	  /* Wait for subprocess to indicate that it is live.  This may not
 | |
| 	     actually be needed but it's hard to say since it is possible that
 | |
| 	     there will be no console for a brief time after the process
 | |
| 	     returns and there is no easy way to determine if/when this happens
 | |
| 	     in Windows.  So play it safe. */
 | |
| 	  if (!x || (WaitForSingleObject (hello, 10000) != WAIT_OBJECT_0)
 | |
| 	      || !AttachConsole (pi.dwProcessId))
 | |
| 	    AllocConsole ();	/* Oh well.  Watch the flash. */
 | |
| 	}
 | |
| 
 | |
|       if (!taskbar)
 | |
| 	/* Setting the owner of the console window to HWND_MESSAGE seems to
 | |
| 	   hide it from the taskbar.  Don't know if this method is faster than
 | |
| 	   calling ShowWindowAsync but it should guarantee no taskbar presence
 | |
| 	   for the hidden console. */
 | |
| 	SetParent (GetConsoleWindow (), HWND_MESSAGE);
 | |
|       if (hello)
 | |
| 	CloseHandle (hello);
 | |
|       if (goodbye)
 | |
| 	{
 | |
| 	  SetEvent (goodbye);	/* Tell helper process it's ok to exit. */
 | |
| 	  CloseHandle (goodbye);
 | |
| 	}
 | |
|     }
 | |
|   return invisible_console = true;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::free_console ()
 | |
| {
 | |
|   BOOL res = FreeConsole ();
 | |
|   debug_printf ("freed console, res %d", res);
 | |
|   init_console_handler (false);
 | |
| }
 | |
| 
 | |
| bool
 | |
| fhandler_console::need_invisible ()
 | |
| {
 | |
|   BOOL b = false;
 | |
|   if (exists ())
 | |
|     invisible_console = false;
 | |
|   else
 | |
|     {
 | |
|       HWINSTA h;
 | |
|       /* The intent here is to allocate an "invisible" console if we have no
 | |
| 	 controlling tty or to reuse the existing console if we already have
 | |
| 	 a tty.  So, first get the old window station.  If there is no controlling
 | |
| 	 terminal, create a new window station and then set it as the current
 | |
| 	 window station.  The subsequent AllocConsole will then be allocated
 | |
| 	 invisibly.  But, after doing that we have to restore any existing windows
 | |
| 	 station or, strangely, characters will not be displayed in any windows
 | |
| 	 drawn on the current screen.  We only do this if we have changed to
 | |
| 	 a new window station and if we had an existing windows station previously.
 | |
| 	 We also close the previously opened window station even though AllocConsole
 | |
| 	 is now "using" it.  This doesn't seem to cause any problems.
 | |
| 
 | |
| 	 Things to watch out for if you make changes in this code:
 | |
| 
 | |
| 	 - Flashing, black consoles showing up when you start, e.g., ssh in
 | |
| 	   an xterm.
 | |
| 	 - Non-displaying of characters in rxvt or xemacs if you start a
 | |
| 	   process using setsid: bash -lc "setsid rxvt".  */
 | |
| 
 | |
|       h = GetProcessWindowStation ();
 | |
| 
 | |
|       USEROBJECTFLAGS oi;
 | |
|       DWORD len;
 | |
|       if (!h
 | |
| 	  || !GetUserObjectInformationW (h, UOI_FLAGS, &oi, sizeof (oi), &len)
 | |
| 	  || !(oi.dwFlags & WSF_VISIBLE))
 | |
| 	{
 | |
| 	  b = true;
 | |
| 	  debug_printf ("window station is not visible");
 | |
| 	  AllocConsole ();
 | |
| 	  invisible_console = true;
 | |
| 	}
 | |
|       else if (wincap.has_broken_alloc_console ())
 | |
| 	b = create_invisible_console_workaround ();
 | |
|       else
 | |
| 	b = create_invisible_console (h);
 | |
|     }
 | |
| 
 | |
|   debug_printf ("invisible_console %d", invisible_console);
 | |
|   return b;
 | |
| }
 |