1558 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1558 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* fhandler_console.cc
 | |
| 
 | |
|    Copyright 1996, 1997, 1998, 1999, 2000, 2001 Red Hat, Inc.
 | |
| 
 | |
| This file is part of Cygwin.
 | |
| 
 | |
| This software is a copyrighted work licensed under the terms of the
 | |
| Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | |
| details. */
 | |
| 
 | |
| #include "winsup.h"
 | |
| #include <sys/termios.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <fcntl.h>
 | |
| #include <errno.h>
 | |
| #include <unistd.h>
 | |
| #include <wingdi.h>
 | |
| #include <winuser.h>
 | |
| #include <wincon.h>
 | |
| #include <ctype.h>
 | |
| #include <sys/cygwin.h>
 | |
| #include "cygerrno.h"
 | |
| #include "fhandler.h"
 | |
| #include "sync.h"
 | |
| #include "sigproc.h"
 | |
| #include "pinfo.h"
 | |
| #include "shared_info.h"
 | |
| #include "security.h"
 | |
| 
 | |
| /*
 | |
|  * Scroll the screen context.
 | |
|  * x1, y1 - ul corner
 | |
|  * x2, y2 - dr corner
 | |
|  * xn, yn - new ul corner
 | |
|  * Negative values represents current screen dimensions
 | |
|  */
 | |
| static struct
 | |
|     {
 | |
|       short Top, Bottom;
 | |
|     } scroll_region = {0, -1};
 | |
| 
 | |
| #define srTop (info.winTop + scroll_region.Top)
 | |
| #define srBottom ((scroll_region.Bottom < 0) ? info.winBottom : info.winTop + scroll_region.Bottom)
 | |
| 
 | |
| #define use_tty ISSTATE (myself, PID_USETTY)
 | |
| 
 | |
| const char * get_nonascii_key (INPUT_RECORD&, char *);
 | |
| 
 | |
| static BOOL use_mouse = FALSE;
 | |
| 
 | |
| HANDLE console_shared_h;
 | |
| 
 | |
| static tty_min NO_COPY *shared_console_info = NULL;
 | |
| 
 | |
| /* Allocate and initialize the shared record for the current console.
 | |
|    Returns a pointer to shared_console_info. */
 | |
| static __inline tty_min *
 | |
| get_tty_stuff (int force = 0)
 | |
| {
 | |
|   if (shared_console_info && !force)
 | |
|     return shared_console_info;
 | |
| 
 | |
|   shared_console_info = (tty_min *) open_shared (NULL, console_shared_h,
 | |
| 						 sizeof (*shared_console_info),
 | |
| 						 NULL);
 | |
|   ProtectHandle (console_shared_h);
 | |
|   shared_console_info->setntty (TTY_CONSOLE);
 | |
|   shared_console_info->setsid (myself->sid);
 | |
|   return shared_console_info;
 | |
| }
 | |
| 
 | |
| /* Return the tty structure associated with a given tty number.  If the
 | |
|    tty number is < 0, just return a dummy record. */
 | |
| tty_min *
 | |
| tty_list::get_tty (int n)
 | |
| {
 | |
|   static tty_min nada;
 | |
|   if (n == TTY_CONSOLE)
 | |
|     return get_tty_stuff ();
 | |
|   else if (n >= 0)
 | |
|     return &cygwin_shared->tty.ttys[n];
 | |
|   else
 | |
|     return &nada;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Determine if a console is associated with this process prior to a spawn.
 | |
|    If it is, then we'll return 1.  If the console has been initialized, then
 | |
|    set it into a more friendly state for non-cygwin apps. */
 | |
| int __stdcall
 | |
| set_console_state_for_spawn ()
 | |
| {
 | |
|   HANDLE h = CreateFileA ("CONIN$", GENERIC_READ, FILE_SHARE_WRITE,
 | |
| 			  &sec_none_nih, OPEN_EXISTING,
 | |
| 			  FILE_ATTRIBUTE_NORMAL, NULL);
 | |
| 
 | |
|   if (h == INVALID_HANDLE_VALUE || h == NULL)
 | |
|     return 0;
 | |
| 
 | |
|   if (shared_console_info != NULL)
 | |
|     {
 | |
| #     define tc shared_console_info	/* ACK.  Temporarily define for use in TTYSETF macro */
 | |
|       SetConsoleMode (h, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT);
 | |
|       TTYSETF (RSTCONS);
 | |
| #if 0
 | |
|       char ch;
 | |
|       DWORD n;
 | |
|       /* NOTE -- This ReadFile is apparently necessary for correct functioning on
 | |
| 	 Windows NT 4.0.  Without this, the next ReadFile returns garbage.  */
 | |
|       (void) ReadFile (h, &ch, 0, &n, NULL);
 | |
| #endif
 | |
| #     undef tc
 | |
|     }
 | |
| 
 | |
|   CloseHandle (h);
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::set_cursor_maybe ()
 | |
| {
 | |
|   CONSOLE_SCREEN_BUFFER_INFO now;
 | |
|   static CONSOLE_SCREEN_BUFFER_INFO last = {{0, 0}, {-1, -1}, 0, {0, 0}, {0, 0}};
 | |
| 
 | |
|   if (!GetConsoleScreenBufferInfo (get_output_handle(), &now))
 | |
|     return;
 | |
| 
 | |
|   if (last.dwCursorPosition.X != now.dwCursorPosition.X ||
 | |
|       last.dwCursorPosition.Y != now.dwCursorPosition.Y)
 | |
|     {
 | |
|       SetConsoleCursorPosition (get_output_handle (), now.dwCursorPosition);
 | |
|       last.dwCursorPosition = now.dwCursorPosition;
 | |
|     }
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::read (void *pv, size_t buflen)
 | |
| {
 | |
|   if (!buflen)
 | |
|     return 0;
 | |
| 
 | |
|   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)
 | |
|     return copied_chars;
 | |
| 
 | |
|   HANDLE w4[2];
 | |
|   DWORD nwait;
 | |
|   char tmp[17];
 | |
| 
 | |
|   w4[0] = h;
 | |
|   if (iscygthread ())
 | |
|     nwait = 1;
 | |
|   else
 | |
|     {
 | |
|       w4[1] = signal_arrived;
 | |
|       nwait = 2;
 | |
|     }
 | |
| 
 | |
|   for (;;)
 | |
|     {
 | |
|       int bgres;
 | |
|       if ((bgres = bg_check (SIGTTIN)) <= bg_eof)
 | |
| 	return bgres;
 | |
| 
 | |
|       set_cursor_maybe ();	/* to make cursor appear on the screen immediately */
 | |
|       switch (WaitForMultipleObjects (nwait, w4, FALSE, INFINITE))
 | |
| 	{
 | |
| 	case WAIT_OBJECT_0:
 | |
| 	  break;
 | |
| 	case WAIT_OBJECT_0 + 1:
 | |
| 	  set_sig_errno (EINTR);
 | |
| 	  return -1;
 | |
| 	default:
 | |
| 	  __seterrno ();
 | |
| 	  return -1;
 | |
| 	}
 | |
| 
 | |
|       DWORD nread;
 | |
|       INPUT_RECORD input_rec;
 | |
|       const char *toadd = NULL;
 | |
| 
 | |
|       if (!ReadConsoleInput (h, &input_rec, 1, &nread))
 | |
| 	{
 | |
| 	  __seterrno ();
 | |
| 	  syscall_printf ("ReadConsoleInput failed, %E");
 | |
| 	  return -1;		/* seems to be failure */
 | |
| 	}
 | |
| 
 | |
|       /* check the event that occurred */
 | |
|       switch (input_rec.EventType)
 | |
| 	{
 | |
| 	case KEY_EVENT:
 | |
| 	  if (!input_rec.Event.KeyEvent.bKeyDown)
 | |
| 	    continue;
 | |
| 
 | |
| #define ich (input_rec.Event.KeyEvent.uChar.AsciiChar)
 | |
| #define wch (input_rec.Event.KeyEvent.uChar.UnicodeChar)
 | |
| 
 | |
| 	  if (wch == 0 ||
 | |
| 	      /* arrow/function keys */
 | |
| 	      (input_rec.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
 | |
| 	    {
 | |
| 	      toadd = get_nonascii_key (input_rec, tmp);
 | |
| 	      if (!toadd)
 | |
| 		continue;
 | |
| 	      nread = strlen (toadd);
 | |
| 	    }
 | |
|           else
 | |
|             {
 | |
| 	      tmp[1] = ich;
 | |
| 	      /* Need this check since US code page seems to have a bug when
 | |
| 		 converting a CTRL-U. */
 | |
| 	      if ((unsigned char)ich > 0x7f && current_codepage == ansi_cp)
 | |
| 		OemToCharBuff (tmp + 1, tmp + 1, 1);
 | |
| 	      if (!(input_rec.Event.KeyEvent.dwControlKeyState & LEFT_ALT_PRESSED))
 | |
| 		toadd = tmp + 1;
 | |
| 	      else
 | |
| 		{
 | |
| 		  tmp[0] = '\033';
 | |
| 		  tmp[1] = cyg_tolower (tmp[1]);
 | |
| 		  toadd = tmp;
 | |
| 		  nread++;
 | |
| 		}
 | |
|             }
 | |
| #undef ich
 | |
| #undef wch
 | |
| 	  break;
 | |
| 
 | |
| 	case MOUSE_EVENT:
 | |
| 	  if (use_mouse)
 | |
| 	    {
 | |
| 	      MOUSE_EVENT_RECORD & mouse_event = input_rec.Event.MouseEvent;
 | |
| 
 | |
| 	      /* Treat the double-click event like a regular button press */
 | |
| 	      if (mouse_event.dwEventFlags == DOUBLE_CLICK)
 | |
| 		{
 | |
| 		  syscall_printf("mouse: double-click -> click");
 | |
| 		  mouse_event.dwEventFlags = 0;
 | |
| 		}
 | |
| 
 | |
| 	      /* Did something other than a click occur? */
 | |
| 	      if (mouse_event.dwEventFlags)
 | |
| 		continue;
 | |
| 	      
 | |
| 	      /* If the mouse event occurred out of the area we can handle,
 | |
| 		 ignore it. */
 | |
| 	      int x = mouse_event.dwMousePosition.X;
 | |
| 	      int y = mouse_event.dwMousePosition.Y;
 | |
| 	      if ((x + ' ' + 1 > 0xFF) || (y + ' ' + 1 > 0xFF))
 | |
| 		{
 | |
| 		  syscall_printf("mouse: position out of range");
 | |
| 		  continue;
 | |
| 		}
 | |
| 	      
 | |
| 	      /* Ignore unimportant mouse buttons */
 | |
| 	      mouse_event.dwButtonState &= 0x7;
 | |
| 	      
 | |
| 	      /* This code assumes Windows never reports multiple button
 | |
| 		 events at the same time. */
 | |
| 	      static DWORD dwLastButtonState = 0;
 | |
| 	      int b = 0;
 | |
| 	      char sz[32];
 | |
| 	      if (mouse_event.dwButtonState == dwLastButtonState)
 | |
| 		{
 | |
| 		  syscall_printf("mouse: button state unchanged");
 | |
| 		  continue;
 | |
| 		}
 | |
| 	      else if (mouse_event.dwButtonState < dwLastButtonState)
 | |
| 		{
 | |
| 		  b = 3;
 | |
| 		  strcpy(sz, "btn up");
 | |
| 		}
 | |
| 	      else if ((mouse_event.dwButtonState & 1) != (dwLastButtonState & 1))
 | |
| 		{
 | |
| 		  b = 0;
 | |
| 		  strcpy(sz, "btn1 down");
 | |
| 		}
 | |
| 	      else if ((mouse_event.dwButtonState & 2) != (dwLastButtonState & 2))
 | |
| 		{
 | |
| 		  b = 1;
 | |
| 		  strcpy(sz, "btn2 down");
 | |
| 		}
 | |
| 	      else if ((mouse_event.dwButtonState & 4) != (dwLastButtonState & 4))
 | |
| 		{
 | |
| 		  b = 2;
 | |
| 		  strcpy(sz, "btn3 down");
 | |
| 		}
 | |
| 	      
 | |
| 	      /* Remember the current button state */
 | |
| 	      dwLastButtonState = mouse_event.dwButtonState;
 | |
| 	      
 | |
| 	      static int nModifiers = 0;
 | |
| 	      /* If a button was pressed, remember the modifiers */
 | |
| 	      if (b != 3)
 | |
| 		{
 | |
| 		  nModifiers = 0;
 | |
| 		  if (mouse_event.dwControlKeyState & SHIFT_PRESSED)
 | |
| 		    nModifiers |= 0x4;
 | |
| 		  if (mouse_event.dwControlKeyState & (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED))
 | |
| 		    nModifiers |= 0x8;
 | |
| 		  if (mouse_event.dwControlKeyState & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED))
 | |
| 		    nModifiers |= 0x10;
 | |
| 		}
 | |
| 	      
 | |
| 	      b |= nModifiers;
 | |
| 	      
 | |
| 	      /* We can now create the code. */
 | |
| 	      sprintf(tmp, "\033[M%c%c%c", b + ' ', x + ' ' + 1, y + ' ' + 1);
 | |
| 	      syscall_printf("mouse: %s at (%d,%d)", sz, x, y);
 | |
| 
 | |
| 	      toadd = tmp;
 | |
| 	      nread = 6;
 | |
| 	    }
 | |
| 	  break;
 | |
| 
 | |
| 	case WINDOW_BUFFER_SIZE_EVENT:
 | |
| 	  kill_pgrp (tc->getpgid (), SIGWINCH);
 | |
| 	  continue;
 | |
| 
 | |
| 	default:
 | |
| 	  continue;
 | |
| 	}
 | |
| 
 | |
|       if (toadd && line_edit (toadd, nread))
 | |
| 	break;
 | |
| #undef ich
 | |
|     }
 | |
| 
 | |
|   while (buflen)
 | |
|     if ((ch = get_readahead ()) < 0)
 | |
|       break;
 | |
|     else
 | |
|       {
 | |
| 	buf[copied_chars++] = (unsigned char)(ch & 0xff);
 | |
| 	buflen--;
 | |
|       }
 | |
| #undef buf
 | |
| 
 | |
|   return copied_chars;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::set_input_state ()
 | |
| {
 | |
|   if (TTYISSETF (RSTCONS))
 | |
|     input_tcsetattr (0, &tc->ti);
 | |
| }
 | |
| 
 | |
| static struct
 | |
|   {
 | |
|     SHORT winTop;
 | |
|     SHORT winBottom;
 | |
|     COORD dwWinSize;
 | |
|     COORD dwBufferSize;
 | |
|     COORD dwCursorPosition;
 | |
|     WORD wAttributes;
 | |
|   } info;
 | |
| 
 | |
| BOOL
 | |
| fhandler_console::fillin_info (void)
 | |
| {
 | |
|   BOOL ret;
 | |
|   CONSOLE_SCREEN_BUFFER_INFO linfo;
 | |
| 
 | |
|   if ((ret = GetConsoleScreenBufferInfo (get_output_handle(), &linfo)))
 | |
|     {
 | |
|       info.winTop = linfo.srWindow.Top;
 | |
|       info.winBottom = linfo.srWindow.Bottom;
 | |
|       info.dwWinSize.Y = 1 + linfo.srWindow.Bottom - linfo.srWindow.Top;
 | |
|       info.dwWinSize.X = 1 + linfo.srWindow.Right - linfo.srWindow.Left;
 | |
|       info.dwBufferSize = linfo.dwSize;
 | |
|       info.dwCursorPosition = linfo.dwCursorPosition;
 | |
|       info.wAttributes = linfo.wAttributes;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       memset (&info, 0, sizeof info);
 | |
|       info.dwWinSize.Y = 25;
 | |
|       info.dwWinSize.X = 80;
 | |
|       info.winBottom = 24;
 | |
|     }
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::scroll_screen (int x1, int y1, int x2, int y2, int xn, int yn)
 | |
| {
 | |
|   SMALL_RECT sr1, sr2;
 | |
|   CHAR_INFO fill;
 | |
|   COORD dest;
 | |
| 
 | |
|   (void)fillin_info ();
 | |
|   sr1.Left = x1 >= 0 ? x1 : info.dwWinSize.X - 1;
 | |
|   if (y1 == 0)
 | |
|     sr1.Top = info.winTop;
 | |
|   else
 | |
|     sr1.Top = y1 > 0 ? y1 : info.winBottom;
 | |
|   sr1.Right = x2 >= 0 ? x2 : info.dwWinSize.X - 1;
 | |
|   if (y2 == 0)
 | |
|     sr1.Bottom = info.winTop;
 | |
|   else
 | |
|     sr1.Bottom = y2 > 0 ? y2 : info.winBottom;
 | |
|   sr2.Top = srTop;
 | |
|   sr2.Left = 0;
 | |
|   sr2.Bottom = srBottom;
 | |
|   sr2.Right = info.dwWinSize.X - 1;
 | |
|   if (sr1.Bottom > sr2.Bottom && sr1.Top <= sr2.Bottom)
 | |
|     sr1.Bottom = sr2.Bottom;
 | |
|   dest.X = xn >= 0 ? xn : info.dwWinSize.X - 1;
 | |
|   if (yn == 0)
 | |
|     dest.Y = info.winTop;
 | |
|   else
 | |
|     dest.Y = yn > 0 ? yn : info.winBottom;
 | |
|   fill.Char.AsciiChar = ' ';
 | |
|   fill.Attributes = default_color;
 | |
|   ScrollConsoleScreenBuffer (get_output_handle (), &sr1, &sr2, dest, &fill);
 | |
| 
 | |
|   /* ScrollConsoleScreenBuffer on Windows 95 is buggy - when scroll distance
 | |
|    * is more than half of screen, filling doesn't work as expected */
 | |
| 
 | |
|   if (sr1.Top != sr1.Bottom)
 | |
|     if (dest.Y <= sr1.Top)	/* forward scroll */
 | |
|       clear_screen (0, 1 + dest.Y + sr1.Bottom - sr1.Top, sr2.Right, sr2.Bottom);
 | |
|     else			/* reverse scroll */
 | |
|       clear_screen (0, sr1.Top, sr2.Right, dest.Y - 1);
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::open (const char *, int flags, mode_t)
 | |
| {
 | |
|   HANDLE h;
 | |
| 
 | |
|   tcinit (get_tty_stuff ());
 | |
| 
 | |
|   set_io_handle (INVALID_HANDLE_VALUE);
 | |
|   set_output_handle (INVALID_HANDLE_VALUE);
 | |
| 
 | |
|   set_flags (flags);
 | |
| 
 | |
|   /* Open the input handle as handle_ */
 | |
|   h = CreateFileA ("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);
 | |
|   set_r_no_interrupt (1);	// Handled explicitly in read code
 | |
| 
 | |
|   h = CreateFileA ("CONOUT$", GENERIC_READ|GENERIC_WRITE,
 | |
| 		   FILE_SHARE_READ | FILE_SHARE_WRITE, &sec_none,
 | |
| 		   OPEN_EXISTING, 0, 0);
 | |
| 
 | |
|   if (h == INVALID_HANDLE_VALUE)
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return 0;
 | |
|     }
 | |
|   set_output_handle (h);
 | |
| 
 | |
|   if (fillin_info ())
 | |
|     default_color = info.wAttributes;
 | |
| 
 | |
|   DWORD cflags;
 | |
|   if (GetConsoleMode (get_io_handle (), &cflags))
 | |
|     {
 | |
|       cflags |= ENABLE_PROCESSED_INPUT;
 | |
|       SetConsoleMode (get_io_handle (), ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | cflags);
 | |
|     }
 | |
| 
 | |
|   TTYCLEARF (RSTCONS);
 | |
|   set_ctty (TTY_CONSOLE, flags);
 | |
|   debug_printf("opened conin$ %p, conout$ %p",
 | |
| 		get_io_handle (), get_output_handle ());
 | |
| 
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::close (void)
 | |
| {
 | |
|   CloseHandle (get_io_handle ());
 | |
|   CloseHandle (get_output_handle ());
 | |
|   set_io_handle (INVALID_HANDLE_VALUE);
 | |
|   set_output_handle (INVALID_HANDLE_VALUE);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Special console dup to duplicate input and output
 | |
|  * handles.
 | |
|  */
 | |
| 
 | |
| int
 | |
| fhandler_console::dup (fhandler_base *child)
 | |
| {
 | |
|   fhandler_console *fhc = (fhandler_console *) child;
 | |
| 
 | |
|   if (!fhc->open(get_name (), get_flags (), 0))
 | |
|     system_printf ("error opening console, %E");
 | |
| 
 | |
|   fhc->state_ = state_;
 | |
|   fhc->default_color = default_color;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::ioctl (unsigned int cmd, void *buf)
 | |
| {
 | |
|   switch (cmd)
 | |
|     {
 | |
|       case TIOCGWINSZ:
 | |
| 	int st;
 | |
| 
 | |
| 	st = fillin_info ();
 | |
| 	if (st)
 | |
| 	  {
 | |
| 	    /* *not* the buffer size, the actual screen size... */
 | |
| 	    /* based on Left Top Right Bottom of srWindow */
 | |
| 	    ((struct winsize *) buf)->ws_row = info.dwWinSize.Y;
 | |
| 	    ((struct winsize *) buf)->ws_col = info.dwWinSize.X;
 | |
| 	    syscall_printf ("WINSZ: (row=%d,col=%d)",
 | |
| 			   ((struct winsize *) buf)->ws_row,
 | |
| 			   ((struct winsize *) buf)->ws_col);
 | |
| 	    return 0;
 | |
| 	  }
 | |
| 	else
 | |
| 	  {
 | |
| 	    syscall_printf ("WINSZ failed");
 | |
| 	    __seterrno ();
 | |
| 	    return -1;
 | |
| 	  }
 | |
| 	return 0;
 | |
|       case TIOCSWINSZ:
 | |
| 	(void) bg_check (SIGTTOU);
 | |
| 	return 0;
 | |
|     }
 | |
| 
 | |
|   return fhandler_base::ioctl (cmd, buf);
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::tcflush (int queue)
 | |
| {
 | |
|   int res = 0;
 | |
|   if (queue == TCIFLUSH
 | |
|       || queue == TCIOFLUSH)
 | |
|     {
 | |
|       if (!FlushConsoleInputBuffer (get_io_handle ()))
 | |
| 	{
 | |
| 	  __seterrno ();
 | |
| 	  res = -1;
 | |
| 	}
 | |
|     }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::output_tcsetattr (int, struct termios const *t)
 | |
| {
 | |
|   /* Ignore the optional_actions stuff, since all output is emitted
 | |
|      instantly */
 | |
| 
 | |
|   /* Enable/disable LF -> CRLF conversions */
 | |
|   set_w_binary ((t->c_oflag & ONLCR) ? 0 : 1);
 | |
| 
 | |
|   /* All the output bits we can ignore */
 | |
| 
 | |
|   DWORD flags = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
 | |
| 
 | |
|   int res = SetConsoleMode (get_output_handle (), flags) ? 0 : -1;
 | |
|   syscall_printf ("%d = tcsetattr (,%x) (ENABLE FLAGS %x) (lflag %x oflag %x)",
 | |
| 		  res, t, flags, t->c_lflag, t->c_oflag);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::input_tcsetattr (int, struct termios const *t)
 | |
| {
 | |
|   /* Ignore the optional_actions stuff, since all output is emitted
 | |
|      instantly */
 | |
| 
 | |
|   DWORD oflags;
 | |
| 
 | |
|   if (!GetConsoleMode (get_io_handle (), &oflags))
 | |
|     oflags = 0;
 | |
|   DWORD flags = 0;
 | |
| 
 | |
|   /* Enable/disable LF -> CRLF conversions */
 | |
|   set_r_binary ((t->c_iflag & INLCR) ? 0 : 1);
 | |
| 
 | |
|   /* There's some disparity between what we need and what's
 | |
|      available.  We've got ECHO and ICANON, they've
 | |
|      got ENABLE_ECHO_INPUT and ENABLE_LINE_INPUT. */
 | |
| 
 | |
|   tc->ti = *t;
 | |
| 
 | |
|   if (t->c_lflag & ECHO)
 | |
|     {
 | |
|       flags |= ENABLE_ECHO_INPUT;
 | |
|     }
 | |
|   if (t->c_lflag & ICANON)
 | |
|     {
 | |
|       flags |= ENABLE_LINE_INPUT;
 | |
|     }
 | |
| 
 | |
|   if (flags & ENABLE_ECHO_INPUT
 | |
|       && !(flags & ENABLE_LINE_INPUT))
 | |
|     {
 | |
|       /* This is illegal, so turn off the echo here, and fake it
 | |
| 	 when we read the characters */
 | |
| 
 | |
|       flags &= ~ENABLE_ECHO_INPUT;
 | |
|     }
 | |
| 
 | |
|   if (t->c_lflag & ISIG)
 | |
|     {
 | |
|       flags |= ENABLE_PROCESSED_INPUT;
 | |
|     }
 | |
|   /* What about ENABLE_WINDOW_INPUT
 | |
|      and ENABLE_MOUSE_INPUT   ? */
 | |
| 
 | |
|   if (use_tty)
 | |
|     {
 | |
|       flags = 0; // ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
 | |
|       tc->ti.c_iflag = 0;
 | |
|       tc->ti.c_lflag = 0;
 | |
|     }
 | |
| 
 | |
|   flags |= ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT;
 | |
| 
 | |
|   int res;
 | |
|   if (flags == oflags)
 | |
|     res = 0;
 | |
|   else
 | |
|     {
 | |
|       res = SetConsoleMode (get_io_handle (), flags) ? 0 : -1;
 | |
|       if (res < 0)
 | |
| 	__seterrno ();
 | |
|       syscall_printf ("%d = tcsetattr (,%x) enable flags %p, c_lflag %p iflag %p",
 | |
| 		      res, t, flags, t->c_lflag, t->c_iflag);
 | |
|     }
 | |
| 
 | |
|   TTYCLEARF (RSTCONS);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::tcsetattr (int a, struct termios const *t)
 | |
| {
 | |
|   int res = output_tcsetattr (a, t);
 | |
|   if (res != 0)
 | |
|     return res;
 | |
|   return input_tcsetattr (a, t);
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::tcgetattr (struct termios *t)
 | |
| {
 | |
|   int res;
 | |
|   *t = tc->ti;
 | |
| 
 | |
|   t->c_cflag |= CS8;
 | |
| 
 | |
| #if 0
 | |
|   if (!get_r_binary ())
 | |
|     t->c_iflag |= IGNCR;
 | |
|   if (!get_w_binary ())
 | |
|     t->c_oflag |= ONLCR;
 | |
| #endif
 | |
| 
 | |
|   DWORD flags;
 | |
| 
 | |
|   if (!GetConsoleMode (get_io_handle (), &flags))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       res = -1;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       if (flags & ENABLE_ECHO_INPUT)
 | |
| 	t->c_lflag |= ECHO;
 | |
| 
 | |
|       if (flags & ENABLE_LINE_INPUT)
 | |
| 	t->c_lflag |= ICANON;
 | |
| 
 | |
|       if (flags & ENABLE_PROCESSED_INPUT)
 | |
| 	t->c_lflag |= ISIG;
 | |
| 
 | |
|       /* What about ENABLE_WINDOW_INPUT
 | |
| 	 and ENABLE_MOUSE_INPUT   ? */
 | |
| 
 | |
|       /* All the output bits we can ignore */
 | |
|       res = 0;
 | |
|     }
 | |
|   syscall_printf ("%d = tcgetattr (%p) enable flags %p, t->lflag %p, t->iflag %p",
 | |
| 		 res, t, flags, t->c_lflag, t->c_iflag);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Constructor.
 | |
|  */
 | |
| 
 | |
| fhandler_console::fhandler_console (const char *name) :
 | |
|   fhandler_termios (FH_CONSOLE, name, -1)
 | |
| {
 | |
|   set_cb (sizeof *this);
 | |
|   state_ = normal;
 | |
|   set_need_fork_fixup ();
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Clear the screen context from x1/y1 to x2/y2 cell.
 | |
|  * Negative values represents current screen dimensions
 | |
|  */
 | |
| void
 | |
| fhandler_console::clear_screen (int x1, int y1, int x2, int y2)
 | |
| {
 | |
|   COORD tlc;
 | |
|   DWORD done;
 | |
|   int num;
 | |
| 
 | |
|   (void)fillin_info ();
 | |
| 
 | |
|   if (x1 < 0)
 | |
|     x1 = info.dwWinSize.X-1;
 | |
|   if (y1 < 0)
 | |
|     y1 = info.winBottom;
 | |
|   if (x2 < 0)
 | |
|     x2 = info.dwWinSize.X-1;
 | |
|   if (y2 < 0)
 | |
|     y2 = info.winBottom;
 | |
| 
 | |
|   num = abs (y1 - y2) * info.dwBufferSize.X + abs (x1 - x2) + 1;
 | |
| 
 | |
|   if ((y2 * info.dwBufferSize.X + x2) > (y1 * info.dwBufferSize.X + x1))
 | |
|     {
 | |
|       tlc.X = x1;
 | |
|       tlc.Y = y1;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       tlc.X = x2;
 | |
|       tlc.Y = y2;
 | |
|     }
 | |
|   FillConsoleOutputCharacterA (get_output_handle (), ' ',
 | |
| 			       num,
 | |
| 			       tlc,
 | |
| 			       &done);
 | |
|   FillConsoleOutputAttribute (get_output_handle (),
 | |
| 			       default_color,
 | |
| 			       num,
 | |
| 			       tlc,
 | |
| 			       &done);
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::cursor_set (BOOL rel_to_top, int x, int y)
 | |
| {
 | |
|   COORD pos;
 | |
| 
 | |
|   (void)fillin_info ();
 | |
|   if (y > info.winBottom)
 | |
|     y = info.winBottom;
 | |
|   else if (y < 0)
 | |
|     y = 0;
 | |
|   else if (rel_to_top)
 | |
|     y += info.winTop;
 | |
| 
 | |
|   if (x > info.dwWinSize.X)
 | |
|     x = info.dwWinSize.X - 1;
 | |
|   else if (x < 0)
 | |
|     x = 0;
 | |
| 
 | |
|   pos.X = x;
 | |
|   pos.Y = y;
 | |
|   SetConsoleCursorPosition (get_output_handle (), pos);
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::cursor_rel (int x, int y)
 | |
| {
 | |
|   fillin_info ();
 | |
|   x += info.dwCursorPosition.X;
 | |
|   y += info.dwCursorPosition.Y;
 | |
|   cursor_set (FALSE, x, y);
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::cursor_get (int *x, int *y)
 | |
| {
 | |
|   fillin_info ();
 | |
|   *y = info.dwCursorPosition.Y;
 | |
|   *x = info.dwCursorPosition.X;
 | |
| }
 | |
| 
 | |
| #define BAK 1
 | |
| #define ESC 2
 | |
| #define NOR 0
 | |
| #define IGN 4
 | |
| #if 0
 | |
| #define ERR 5
 | |
| #else
 | |
| #define ERR NOR
 | |
| #endif
 | |
| #define DWN 6
 | |
| #define BEL 7
 | |
| #define TAB 8 /* We should't let the console deal with these */
 | |
| #define CR 13
 | |
| #define LF 10
 | |
| 
 | |
| static const char base_chars[256] =
 | |
| {
 | |
| /*00 01 02 03 04 05 06 07 */ IGN, ERR, ERR, NOR, NOR, NOR, NOR, BEL,
 | |
| /*08 09 0A 0B 0C 0D 0E 0F */ BAK, TAB, DWN, ERR, ERR, CR,  ERR, IGN,
 | |
| /*10 11 12 13 14 15 16 17 */ NOR, NOR, ERR, ERR, ERR, ERR, ERR, ERR,
 | |
| /*18 19 1A 1B 1C 1D 1E 1F */ NOR, NOR, ERR, ESC, ERR, ERR, ERR, ERR,
 | |
| /*   !  "  #  $  %  &  '  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*(  )  *  +  ,  -  .  /  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*0  1  2  3  4  5  6  7  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*8  9  :  ;  <  =  >  ?  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*@  A  B  C  D  E  F  G  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*H  I  J  K  L  M  N  O  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*P  Q  R  S  T  U  V  W  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*X  Y  Z  [  \  ]  ^  _  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*`  a  b  c  d  e  f  g  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*h  i  j  k  l  m  n  o  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*p  q  r  s  t  u  v  w  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*x  y  z  {  |  }  ~  7F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*80 81 82 83 84 85 86 87 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*88 89 8A 8B 8C 8D 8E 8F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*90 91 92 93 94 95 96 97 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*98 99 9A 9B 9C 9D 9E 9F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*A0 A1 A2 A3 A4 A5 A6 A7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*A8 A9 AA AB AC AD AE AF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*B0 B1 B2 B3 B4 B5 B6 B7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*B8 B9 BA BB BC BD BE BF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*C0 C1 C2 C3 C4 C5 C6 C7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*C8 C9 CA CB CC CD CE CF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*D0 D1 D2 D3 D4 D5 D6 D7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*D8 D9 DA DB DC DD DE DF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*E0 E1 E2 E3 E4 E5 E6 E7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*E8 E9 EA EB EC ED EE EF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*F0 F1 F2 F3 F4 F5 F6 F7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 | |
| /*F8 F9 FA FB FC FD FE FF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR };
 | |
| 
 | |
| /*#define syscall_printf small_printf*/
 | |
| 
 | |
| static int savex, savey; /* for CSI s, CSI u */
 | |
| 
 | |
| void
 | |
| fhandler_console::char_command (char c, bool saw_question_mark)
 | |
| {
 | |
|   // Keep the background intensity with the colr since there doesn't seem
 | |
|   // to be a way to set this with termcap/terminfo.
 | |
|   static int fg = default_color & (FOREGROUND_BLUE | FOREGROUND_GREEN |
 | |
| 				   FOREGROUND_RED),
 | |
| 	     bg = default_color & (BACKGROUND_BLUE | BACKGROUND_GREEN |
 | |
| 				   BACKGROUND_RED | BACKGROUND_INTENSITY),
 | |
| 			 bold = default_color & FOREGROUND_INTENSITY;
 | |
|   int x, y;
 | |
|   char buf[40];
 | |
| 
 | |
|   switch (c)
 | |
|     {
 | |
|     case 'm':   /* Set Graphics Rendition */
 | |
|        int i;
 | |
| 
 | |
|        for (i = 0; i <= nargs_; i++)
 | |
| 	 switch (args_[i])
 | |
| 	   {
 | |
| 	     case 0:    /* normal color */
 | |
| 	       fg = default_color & (FOREGROUND_BLUE | FOREGROUND_GREEN |
 | |
| 				     FOREGROUND_RED);
 | |
| 	       bg = default_color & (BACKGROUND_BLUE | BACKGROUND_GREEN |
 | |
| 				     BACKGROUND_RED | BACKGROUND_INTENSITY);
 | |
| 	       bold = default_color & FOREGROUND_INTENSITY;
 | |
| 	       break;
 | |
| 	     case 1:    /* bold */
 | |
| 	       fg = default_color & (FOREGROUND_BLUE | FOREGROUND_GREEN |
 | |
| 				     FOREGROUND_RED);
 | |
| 	       bg = default_color & (BACKGROUND_BLUE | BACKGROUND_GREEN |
 | |
| 				     BACKGROUND_RED | BACKGROUND_INTENSITY);
 | |
| 	       bold = FOREGROUND_INTENSITY;
 | |
| 	       break;
 | |
| 	     case 4:    /* underline - simulate with cyan */
 | |
| 	       fg = FOREGROUND_BLUE | FOREGROUND_GREEN;
 | |
| 	       bg = default_color & (BACKGROUND_BLUE | BACKGROUND_GREEN |
 | |
| 				     BACKGROUND_RED | BACKGROUND_INTENSITY);
 | |
| 	       bold = default_color & FOREGROUND_INTENSITY;
 | |
| 	       break;
 | |
| 	     case 5:    /* blink mode */
 | |
| 	       fg = default_color & (FOREGROUND_BLUE | FOREGROUND_GREEN |
 | |
| 				     FOREGROUND_RED);
 | |
| 	       bg = default_color & (BACKGROUND_BLUE | BACKGROUND_GREEN |
 | |
| 				     BACKGROUND_RED | BACKGROUND_INTENSITY);
 | |
| 	       bold = default_color & FOREGROUND_INTENSITY;
 | |
| 	       break;
 | |
| 	     case 7:    /* reverse */
 | |
| 	       fg = (default_color & BACKGROUND_BLUE) ? FOREGROUND_BLUE : 0;
 | |
| 	       fg |= (default_color & BACKGROUND_GREEN) ? FOREGROUND_GREEN : 0;
 | |
| 	       fg |= (default_color & BACKGROUND_RED) ? FOREGROUND_RED : 0;
 | |
| 	       fg |= (default_color & BACKGROUND_INTENSITY) ?
 | |
| 					     FOREGROUND_INTENSITY : 0;
 | |
| 	       bg = (default_color & FOREGROUND_BLUE) ? BACKGROUND_BLUE : 0;
 | |
| 	       bg |= (default_color & FOREGROUND_GREEN) ? BACKGROUND_GREEN : 0;
 | |
| 	       bg |= (default_color & FOREGROUND_RED) ? BACKGROUND_RED : 0;
 | |
| 	       bg |= (default_color & FOREGROUND_INTENSITY) ?
 | |
| 					     BACKGROUND_INTENSITY : 0;
 | |
| 	       break;
 | |
| 	     case 8:    /* invisible */
 | |
| 	       fg = (default_color & BACKGROUND_BLUE) ? FOREGROUND_BLUE : 0;
 | |
| 	       fg |= (default_color & BACKGROUND_GREEN) ? FOREGROUND_GREEN : 0;
 | |
| 	       fg |= (default_color & BACKGROUND_RED) ? FOREGROUND_RED : 0;
 | |
| 	       bg = default_color & (BACKGROUND_BLUE | BACKGROUND_GREEN |
 | |
| 				     BACKGROUND_RED | BACKGROUND_INTENSITY);
 | |
| 	       bold = (default_color & BACKGROUND_INTENSITY) ?
 | |
| 						   FOREGROUND_INTENSITY : 0;
 | |
| 	       break;
 | |
| 	     case 9:    /* dim */
 | |
| 	       fg = default_color & (FOREGROUND_BLUE | FOREGROUND_GREEN |
 | |
| 				     FOREGROUND_RED);
 | |
| 	       bg = default_color & (BACKGROUND_BLUE | BACKGROUND_GREEN |
 | |
| 				     BACKGROUND_RED | BACKGROUND_INTENSITY);
 | |
| 	       bold = (fg == 0) ? FOREGROUND_INTENSITY : 0;
 | |
| 	       break;
 | |
| 	     case 30:		/* BLACK foreground */
 | |
| 	       fg = 0;
 | |
| 	       break;
 | |
| 	     case 31:		/* RED foreground */
 | |
| 	       fg = FOREGROUND_RED;
 | |
| 	       break;
 | |
| 	     case 32:		/* GREEN foreground */
 | |
| 	       fg = FOREGROUND_GREEN;
 | |
| 	       break;
 | |
| 	     case 33:		/* YELLOW foreground */
 | |
| 	       fg = FOREGROUND_RED | FOREGROUND_GREEN;
 | |
| 	       break;
 | |
| 	     case 34:		/* BLUE foreground */
 | |
| 	       fg = FOREGROUND_BLUE;
 | |
| 	       break;
 | |
| 	     case 35:		/* MAGENTA foreground */
 | |
| 	       fg = FOREGROUND_RED | FOREGROUND_BLUE;
 | |
| 	       break;
 | |
| 	     case 36:		/* CYAN foreground */
 | |
| 	       fg = FOREGROUND_BLUE | FOREGROUND_GREEN;
 | |
| 	       break;
 | |
| 	     case 37:		/* WHITE foreg */
 | |
| 	       fg = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
 | |
| 	       break;
 | |
| 	     case 40:		/* BLACK background */
 | |
| 	       bg = 0;
 | |
| 	       break;
 | |
| 	     case 41:		/* RED background */
 | |
| 	       bg = BACKGROUND_RED;
 | |
| 	       break;
 | |
| 	     case 42:		/* GREEN background */
 | |
| 	       bg = BACKGROUND_GREEN;
 | |
| 	       break;
 | |
| 	     case 43:		/* YELLOW background */
 | |
| 	       bg = BACKGROUND_RED | BACKGROUND_GREEN;
 | |
| 	       break;
 | |
| 	     case 44:		/* BLUE background */
 | |
| 	       bg = BACKGROUND_BLUE;
 | |
| 	       break;
 | |
| 	     case 45:		/* MAGENTA background */
 | |
| 	       bg = BACKGROUND_RED | BACKGROUND_BLUE;
 | |
| 	       break;
 | |
| 	     case 46:		/* CYAN background */
 | |
| 	       bg = BACKGROUND_BLUE | BACKGROUND_GREEN;
 | |
| 	       break;
 | |
| 	     case 47:    /* WHITE background */
 | |
| 	       bg = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
 | |
| 	       break;
 | |
| 	     default:
 | |
| 	       fg = default_color & (FOREGROUND_BLUE | FOREGROUND_GREEN |
 | |
| 				     FOREGROUND_RED);
 | |
| 	       bg = default_color & (BACKGROUND_BLUE | BACKGROUND_GREEN |
 | |
| 				     BACKGROUND_RED | BACKGROUND_INTENSITY);
 | |
| 	       bold = default_color & FOREGROUND_INTENSITY;
 | |
| 	       break;
 | |
| 	   }
 | |
| 	 SetConsoleTextAttribute (get_output_handle (), fg | bg | bold);
 | |
|       break;
 | |
|     case 'h':
 | |
|     case 'l':
 | |
|       if (!saw_question_mark)
 | |
| 	break;
 | |
|       switch (args_[0])
 | |
| 	{
 | |
| 	case 1000: /* Mouse support */
 | |
| 	  use_mouse = (c == 'h') ? TRUE : FALSE;
 | |
| 	  syscall_printf("mouse support %sabled", use_mouse ? "en" : "dis");
 | |
| 	  break;
 | |
| 
 | |
| 	default: /* Ignore */
 | |
| 	  syscall_printf("unknown h/l command: %d", args_[0]);
 | |
| 	  break;
 | |
| 	}
 | |
|       break;
 | |
|     case 'J':
 | |
|       switch (args_[0])
 | |
| 	{
 | |
| 	case 0:			/* Clear to end of screen */
 | |
| 	  cursor_get (&x, &y);
 | |
| 	  clear_screen (x, y, -1, -1);
 | |
| 	  break;
 | |
| 	case 1:			/* Clear from beginning of screen to cursor */
 | |
| 	  cursor_get (&x, &y);
 | |
| 	  clear_screen (0, 0, x, y);
 | |
| 	  break;
 | |
| 	case 2:			/* Clear screen */
 | |
| 	  clear_screen (0, 0, -1, -1);
 | |
| 	  cursor_set (TRUE, 0,0);
 | |
| 	  break;
 | |
| 	default:
 | |
| 	  goto bad_escape;
 | |
| 	}
 | |
|       break;
 | |
| 
 | |
|     case 'A':
 | |
|       cursor_rel (0, -(args_[0] ? args_[0] : 1));
 | |
|       break;
 | |
|     case 'B':
 | |
|       cursor_rel (0, args_[0] ? args_[0] : 1);
 | |
|       break;
 | |
|     case 'C':
 | |
|       cursor_rel (args_[0] ? args_[0] : 1, 0);
 | |
|       break;
 | |
|     case 'D':
 | |
|       cursor_rel (-(args_[0] ? args_[0] : 1),0);
 | |
|       break;
 | |
|     case 'K':
 | |
|       switch (args_[0])
 | |
| 	{
 | |
| 	  case 0:		/* Clear to end of line */
 | |
| 	    cursor_get (&x, &y);
 | |
| 	    clear_screen (x, y, -1, y);
 | |
| 	    break;
 | |
| 	  case 2:		/* Clear line */
 | |
| 	    cursor_get (&x, &y);
 | |
| 	    clear_screen (0, y, -1, y);
 | |
| 	    break;
 | |
| 	  case 1:		/* Clear from bol to cursor */
 | |
| 	    cursor_get (&x, &y);
 | |
| 	    clear_screen (0, y, x, y);
 | |
| 	    break;
 | |
| 	  default:
 | |
| 	    goto bad_escape;
 | |
| 	}
 | |
|       break;
 | |
|     case 'H':
 | |
|     case 'f':
 | |
|       cursor_set (TRUE, (args_[1] ? args_[1] : 1) - 1,
 | |
| 			(args_[0] ? args_[0] : 1) - 1);
 | |
|       break;
 | |
|     case 'G':   /* hpa - position cursor at column n - 1 */
 | |
|       cursor_get (&x, &y);
 | |
|       cursor_set (FALSE, (args_[0] ? args_[0] - 1 : 0), y);
 | |
|       break;
 | |
|     case 'd':   /* vpa - position cursor at line n */
 | |
|       cursor_get (&x, &y);
 | |
|       cursor_set (TRUE, x, (args_[0] ? args_[0] - 1 : 0));
 | |
|       break;
 | |
|     case 's':   /* Save cursor position */
 | |
|       cursor_get (&savex, &savey);
 | |
|       break;
 | |
|     case 'u':   /* Restore cursor position */
 | |
|       cursor_set (FALSE, savex, savey);
 | |
|       break;
 | |
|     case 'I':	/* TAB */
 | |
|       cursor_get (&x, &y);
 | |
|       cursor_set (FALSE, 8*(x/8+1), y);
 | |
|       break;
 | |
|     case 'L':				/* AL - insert blank lines */
 | |
|       args_[0] = args_[0] ? args_[0] : 1;
 | |
|       cursor_get (&x, &y);
 | |
|       scroll_screen (0, y, -1, -1, 0, y + args_[0]);
 | |
|       break;
 | |
|     case 'M':				/* DL - delete lines */
 | |
|       args_[0] = args_[0] ? args_[0] : 1;
 | |
|       cursor_get (&x, &y);
 | |
|       scroll_screen (0, y + args_[0], -1, -1, 0, y);
 | |
|       break;
 | |
|     case '@':				/* IC - insert chars */
 | |
|       args_[0] = args_[0] ? args_[0] : 1;
 | |
|       cursor_get (&x, &y);
 | |
|       scroll_screen (x, y, -1, y, x + args_[0], y);
 | |
|       break;
 | |
|     case 'P':				/* DC - delete chars */
 | |
|       args_[0] = args_[0] ? args_[0] : 1;
 | |
|       cursor_get (&x, &y);
 | |
|       scroll_screen (x + args_[0], y, -1, y, x, y);
 | |
|       break;
 | |
|     case 'S':				/* SF - Scroll forward */
 | |
|       args_[0] = args_[0] ? args_[0] : 1;
 | |
|       scroll_screen(0, args_[0], -1, -1, 0, 0);
 | |
|       break;
 | |
|     case 'T':				/* SR - Scroll down */
 | |
|       fillin_info ();
 | |
|       args_[0] = args_[0] ? args_[0] : 1;
 | |
|       scroll_screen (0, 0, -1, -1, 0, info.winTop + args_[0]);
 | |
|       break;
 | |
|     case 'X':				/* ec - erase chars */
 | |
|       args_[0] = args_[0] ? args_[0] : 1;
 | |
|       cursor_get (&x, &y);
 | |
|       scroll_screen (x + args_[0], y, -1, y, x, y);
 | |
|       scroll_screen (x, y, -1, y, x + args_[0], y);
 | |
|       break;
 | |
|     case 'Z':				/* Back tab */
 | |
|       cursor_get (&x, &y);
 | |
|       cursor_set (FALSE, ((8 * (x / 8 + 1)) - 8), y);
 | |
|       break;
 | |
|     case 'b':				/* Repeat char #1 #2 times */
 | |
|       while (args_[1]--)
 | |
| 	WriteFile (get_output_handle (), &args_[0], 1, (DWORD *) &x, 0);
 | |
|       break;
 | |
|     case 'c':				/* u9 - Terminal enquire string */
 | |
|       strcpy (buf, "\033[?6c");
 | |
|       puts_readahead (buf);
 | |
|       break;
 | |
|     case 'n':
 | |
|       switch (args_[0])
 | |
| 	{
 | |
| 	case 6:				/* u7 - Cursor position request */
 | |
| 	  cursor_get (&x, &y);
 | |
| 	  y -= info.winTop;
 | |
| 	  /* x -= info.winLeft;		// not available yet */
 | |
| 	  __small_sprintf (buf, "\033[%d;%dR", y + 1, x + 1);
 | |
| 	  puts_readahead (buf);
 | |
| 	  break;
 | |
|     default:
 | |
| 	  goto bad_escape;
 | |
| 	}
 | |
|       break;
 | |
|     case 'r':				/* Set Scroll region */
 | |
|       scroll_region.Top = args_[0] ? args_[0] - 1 : 0;
 | |
|       scroll_region.Bottom = args_[1] ? args_[1] - 1 : -1;
 | |
|       cursor_set (TRUE, 0, 0);
 | |
|       break;
 | |
|     case 'g':				/* TAB set/clear */
 | |
|       break;
 | |
|     default:
 | |
| bad_escape:
 | |
|       break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| const unsigned char *
 | |
| fhandler_console::write_normal (const unsigned char *src,
 | |
| 				const unsigned char *end)
 | |
| {
 | |
|   /* Scan forward to see what a char which needs special treatment */
 | |
|   DWORD done;
 | |
|   const unsigned char *found = src;
 | |
| 
 | |
|   while (found < end)
 | |
|     {
 | |
|       if (base_chars[*found] != NOR)
 | |
| 	break;
 | |
|       found++;
 | |
|     }
 | |
|   /* Print all the base ones out */
 | |
|   if (found != src)
 | |
|     {
 | |
|       char buf[4096];
 | |
|       size_t len = found - src;
 | |
|       do {
 | |
| 	size_t l2 = min (sizeof (buf), len);
 | |
| 	if (current_codepage == ansi_cp)
 | |
| 	  CharToOemBuff ((LPCSTR)src, buf, l2);
 | |
| 	else
 | |
| 	  strncpy (buf, (LPCSTR)src, l2);
 | |
| 	if (! WriteFile (get_output_handle (), buf, l2, &done, 0))
 | |
| 	{
 | |
| 	  debug_printf ("write failed, handle %p", get_output_handle ());
 | |
| 	  __seterrno ();
 | |
| 	  return 0;
 | |
| 	}
 | |
| 	len -= done;
 | |
| 	src += done;
 | |
|       } while (len > 0);
 | |
|     }
 | |
|   if (src < end)
 | |
|     {
 | |
|       int x, y;
 | |
|       switch (base_chars[*src])
 | |
| 	{
 | |
| 	case BEL:
 | |
| 	  Beep (412, 100);
 | |
| 	  break;
 | |
| 	case ESC:
 | |
| 	  state_ = gotesc;
 | |
| 	  break;
 | |
| 	case DWN:		/* WriteFile("\n") always adds CR... */
 | |
| 	  cursor_get (&x, &y);
 | |
| 	  if (y >= srBottom)
 | |
| 	    {
 | |
| 	      if (y < info.winBottom || scroll_region.Top)
 | |
| 		{
 | |
| 		  scroll_screen (0, srTop + 1, -1, srBottom, 0, srTop);
 | |
| 		  y--;
 | |
| 		}
 | |
| 	      else
 | |
| 		WriteFile (get_output_handle (), "\n", 1, &done, 0);
 | |
| 	    }
 | |
| 	  if (!get_w_binary ())
 | |
| 	    x = 0;
 | |
| 	  cursor_set (FALSE, x, y + 1);
 | |
| 	  break;
 | |
| 	case BAK:
 | |
| 	  cursor_rel (-1, 0);
 | |
| 	  break;
 | |
| 	case IGN:
 | |
| 	  cursor_rel (1, 0);
 | |
| 	  break;
 | |
| 	case CR:
 | |
| 	  cursor_get (&x, &y);
 | |
| 	  cursor_set (FALSE, 0, y);
 | |
| 	  break;
 | |
| 	case ERR:
 | |
| 	  WriteFile (get_output_handle (), src, 1, &done, 0);
 | |
| 	  break;
 | |
| 	case TAB:
 | |
| 	  cursor_get (&x, &y);
 | |
| 	  cursor_set (FALSE, 8 * (x / 8 + 1), y);
 | |
| 	  break;
 | |
| 	}
 | |
|       src ++;
 | |
|     }
 | |
|   return src;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::write (const void *vsrc, size_t len)
 | |
| {
 | |
|   /* Run and check for ansi sequences */
 | |
|   unsigned const char *src = (unsigned char *) vsrc;
 | |
|   unsigned const char *end = src + len;
 | |
|   static NO_COPY unsigned rarg;
 | |
|   static NO_COPY char my_title_buf[TITLESIZE + 1];
 | |
|   bool saw_question_mark = 0;
 | |
| 
 | |
|   debug_printf ("%x, %d", vsrc, len);
 | |
| 
 | |
|   while (src < end)
 | |
|     {
 | |
|       debug_printf ("at %d(%c) state is %d", *src, isprint (*src) ? *src : ' ',
 | |
| 		    state_);
 | |
|       switch (state_)
 | |
| 	{
 | |
| 	case normal:
 | |
| 	  src = write_normal (src, end);
 | |
| 	  if (!src) /* write_normal failed */
 | |
| 	    return -1;
 | |
| 	  break;
 | |
| 	case gotesc:
 | |
| 	  if (*src == '[')
 | |
| 	    {
 | |
| 	      state_ = gotsquare;
 | |
| 	      for (nargs_ = 0; nargs_ < MAXARGS; nargs_++)
 | |
| 		args_[nargs_] = 0;
 | |
| 	      nargs_ = 0;
 | |
| 	    }
 | |
| 	  else if (*src == ']')
 | |
| 	    {
 | |
| 	      rarg = 0;
 | |
| 	      my_title_buf[0] = '\0';
 | |
| 	      state_ = gotrsquare;
 | |
| 	    }
 | |
| 	  else if (*src == 'M')		/* Reverse Index */
 | |
| 	    {
 | |
| 	      fillin_info ();
 | |
| 	      scroll_screen (0, 0, -1, -1, 0, info.winTop + 1);
 | |
| 	      state_ = normal;
 | |
| 	    }
 | |
| 	  else if (*src == 'c')		/* Reset Linux terminal */
 | |
| 	    {
 | |
| 	      clear_screen (0, 0, -1, -1);
 | |
| 	      cursor_set (TRUE, 0, 0);
 | |
| 	      state_ = normal;
 | |
| 	    }
 | |
| 	  else if (*src == '8')		/* Restore cursor position */
 | |
| 	    {
 | |
| 	      cursor_set (FALSE, savex, savey);
 | |
| 	      state_ = normal;
 | |
| 	    }
 | |
| 	  else if (*src == '7')		/* Save cursor position */
 | |
| 	    {
 | |
| 	      cursor_get (&savex, &savey);
 | |
| 	      state_ = normal;
 | |
| 	    }
 | |
| 	  else if (*src == 'R')
 | |
| 	      state_ = normal;
 | |
| 	  else
 | |
| 	    {
 | |
| 	      state_ = normal;
 | |
| 	    }
 | |
| 	  src++;
 | |
| 	  break;
 | |
| 	case gotarg1:
 | |
| 	  if (isdigit (*src))
 | |
| 	    {
 | |
| 	      args_[nargs_] = args_[nargs_] * 10 + *src - '0';
 | |
| 	      src++;
 | |
| 	    }
 | |
| 	  else if (*src == ';')
 | |
| 	    {
 | |
| 	      src++;
 | |
| 	      nargs_++;
 | |
| 	      if (nargs_ >= MAXARGS)
 | |
| 		nargs_--;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      state_ = gotcommand;
 | |
| 	    }
 | |
| 	  break;
 | |
| 	case gotcommand:
 | |
| 	  char_command (*src++, saw_question_mark);
 | |
| 	  state_ = normal;
 | |
| 	  break;
 | |
| 	case gotrsquare:
 | |
| 	  if (isdigit(*src))
 | |
| 	    rarg = rarg * 10 + (*src - '0');
 | |
| 	  else if (*src == ';' && (rarg == 2 || rarg == 0))
 | |
| 	    state_ = gettitle;
 | |
| 	  else
 | |
| 	    state_ = eattitle;
 | |
| 	  src++;
 | |
| 	  break;
 | |
| 	case eattitle:
 | |
| 	case gettitle:
 | |
| 	  {
 | |
| 	    int n = strlen (my_title_buf);
 | |
| 	    if (*src < ' ' || *src >= '\177')
 | |
| 	      {
 | |
| 		if (*src == '\007' && state_ == gettitle)
 | |
| 		  {
 | |
| 		    if (old_title)
 | |
| 		      strcpy (old_title, my_title_buf);
 | |
| 		    set_console_title (my_title_buf);
 | |
| 		  }
 | |
| 		state_ = normal;
 | |
| 	      }
 | |
| 	    else if (n < TITLESIZE)
 | |
| 	      {
 | |
| 		my_title_buf[n++] = *src;
 | |
| 		my_title_buf[n] = '\0';
 | |
| 	      }
 | |
| 	    src++;
 | |
| 	    break;
 | |
| 	  }
 | |
| 	case gotsquare:
 | |
| 	  if (*src == ';')
 | |
| 	    {
 | |
| 	      state_ = gotarg1;
 | |
| 	      nargs_++;
 | |
| 	      src++;
 | |
| 	    }
 | |
| 	  else if (isalpha (*src))
 | |
| 	    {
 | |
| 	      state_ = gotcommand;
 | |
| 	    }
 | |
| 	  else if (*src != '@' && !isalpha (*src) && !isdigit (*src))
 | |
| 	    {
 | |
| 	      if (*src == '?')
 | |
| 		saw_question_mark = 1;
 | |
| 	      /* ignore any extra chars between [ and first arg or command */
 | |
| 	      src++;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    state_ = gotarg1;
 | |
| 	  break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   syscall_printf ("%d = write_console (,..%d)", len, len);
 | |
| 
 | |
|   return len;
 | |
| }
 | |
| 
 | |
| static struct {
 | |
|   int vk;
 | |
|   const char *val[4];
 | |
| } keytable[] = {
 | |
| 	       /* NORMAL */  /* SHIFT */    /* CTRL */       /* ALT */
 | |
|   {VK_LEFT,	{"\033[D",	NULL,		NULL,		NULL}},
 | |
|   {VK_RIGHT,	{"\033[C",	NULL,		NULL,		NULL}},
 | |
|   {VK_UP,	{"\033[A",	NULL,		NULL,		NULL}},
 | |
|   {VK_DOWN,	{"\033[B",	NULL,		NULL,		NULL}},
 | |
|   {VK_PRIOR,	{"\033[5~",	NULL,		NULL,		NULL}},
 | |
|   {VK_NEXT,	{"\033[6~",	NULL,		NULL,		NULL}},
 | |
|   {VK_HOME,	{"\033[1~",	NULL,		NULL,		NULL}},
 | |
|   {VK_END,	{"\033[4~",	NULL,		NULL,		NULL}},
 | |
|   {VK_INSERT,	{"\033[2~",	NULL,		NULL,		NULL}},
 | |
|   {VK_DELETE,	{"\033[3~",	NULL,		NULL,		NULL}},
 | |
|   {VK_F1,	{"\033[[A",	"\033[23~",	NULL,		NULL}},
 | |
|   {VK_F2,	{"\033[[B",	"\033[24~",	NULL,		NULL}},
 | |
|   {VK_F3,	{"\033[[C",	"\033[25~",	NULL,		NULL}},
 | |
|   {VK_F4,	{"\033[[D",	"\033[26~",	NULL,		NULL}},
 | |
|   {VK_F5,	{"\033[[E",	"\033[28~",	NULL,		NULL}},
 | |
|   {VK_F6,	{"\033[17~",	"\033[29~",	"\036",		NULL}},
 | |
|   {VK_F7,	{"\033[18~",	"\033[31~",	NULL,		NULL}},
 | |
|   {VK_F8,	{"\033[19~",	"\033[32~",	NULL,		NULL}},
 | |
|   {VK_F9,	{"\033[20~",	"\033[33~",	NULL,		NULL}},
 | |
|   {VK_F10,	{"\033[21~",	"\033[34~",	NULL,		NULL}},
 | |
|   {VK_F11,	{"\033[23~",	NULL,		NULL,		NULL}},
 | |
|   {VK_F12,	{"\033[24~",	NULL,		NULL,		NULL}},
 | |
|   {VK_NUMPAD5,	{"\033[G",	NULL,		NULL,		NULL}},
 | |
|   {VK_CLEAR,	{"\033[G",	NULL,		NULL,		NULL}},
 | |
|   {'6',		{NULL,		NULL,		"\036",		NULL}},
 | |
|   {0,		{"",		NULL,		NULL,		NULL}}
 | |
| };
 | |
| 
 | |
| const char *
 | |
| get_nonascii_key (INPUT_RECORD& input_rec, char *tmp)
 | |
| {
 | |
| #define NORMAL  0
 | |
| #define SHIFT	1
 | |
| #define CONTROL	2
 | |
| #define ALT	3
 | |
|   int modifier_index = NORMAL;
 | |
| 
 | |
|   if (input_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)
 | |
|     modifier_index = SHIFT;
 | |
|   else if (input_rec.Event.KeyEvent.dwControlKeyState &
 | |
| 		(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
 | |
|     modifier_index = CONTROL;
 | |
|   else if (input_rec.Event.KeyEvent.dwControlKeyState &
 | |
| 		(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
 | |
|     modifier_index = ALT;
 | |
| 
 | |
|   for (int i = 0; keytable[i].vk; i++)
 | |
|     if (input_rec.Event.KeyEvent.wVirtualKeyCode == keytable[i].vk)
 | |
|       return keytable[i].val[modifier_index];
 | |
| 
 | |
|   if (input_rec.Event.KeyEvent.uChar.AsciiChar)
 | |
|     {
 | |
|       tmp[0] = input_rec.Event.KeyEvent.uChar.AsciiChar;
 | |
|       tmp[1] = '\0';
 | |
|       return tmp;
 | |
|     }
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::init (HANDLE f, DWORD a, mode_t bin)
 | |
| {
 | |
|   this->fhandler_termios::init (f, bin, a);
 | |
| 
 | |
|   /* Ensure both input and output console handles are open */
 | |
|   int mode = 0;
 | |
| 
 | |
|   a &= GENERIC_READ | GENERIC_WRITE;
 | |
|   if (a == GENERIC_READ)
 | |
|     mode = O_RDONLY;
 | |
|   if (a == GENERIC_WRITE)
 | |
|     mode = O_WRONLY;
 | |
|   if (a == (GENERIC_READ | GENERIC_WRITE))
 | |
|     mode = O_RDWR;
 | |
|   open (0, mode);
 | |
|   if (f != INVALID_HANDLE_VALUE)
 | |
|     CloseHandle (f);	/* Reopened by open */
 | |
| 
 | |
|   output_tcsetattr (0, &tc->ti);
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_console::igncr_enabled (void)
 | |
| {
 | |
|   return tc->ti.c_iflag & IGNCR;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::set_close_on_exec (int val)
 | |
| {
 | |
|   this->fhandler_base::set_close_on_exec (val);
 | |
|   set_inheritance (output_handle, val);
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::fixup_after_fork (HANDLE)
 | |
| {
 | |
|   HANDLE h = get_handle ();
 | |
|   HANDLE oh = get_output_handle ();
 | |
| 
 | |
|   /* Windows does not allow duplication of console handles between processes
 | |
|      so open the console explicitly. */
 | |
| 
 | |
|   if (!open(get_name (), get_flags (), 0))
 | |
|     system_printf ("error opening console after fork, %E");
 | |
| 
 | |
|   if (!get_close_on_exec ())
 | |
|     {
 | |
|       CloseHandle (h);
 | |
|       CloseHandle (oh);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void __stdcall
 | |
| set_console_title (char *title)
 | |
| {
 | |
|   int rc;
 | |
|   char buf[257];
 | |
|   strncpy(buf, title, sizeof(buf) - 1);
 | |
|   buf[sizeof(buf) - 1] = '\0';
 | |
|   if ((rc = WaitForSingleObject (title_mutex, 15000)) != WAIT_OBJECT_0)
 | |
|     sigproc_printf ("wait for title mutex failed rc %d, %E", rc);
 | |
|   SetConsoleTitle (buf);
 | |
|   ReleaseMutex (title_mutex);
 | |
|   debug_printf ("title '%s'", buf);
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_console::fixup_after_exec (HANDLE)
 | |
| {
 | |
|   HANDLE h = get_handle ();
 | |
|   HANDLE oh = get_output_handle ();
 | |
| 
 | |
|   if (!open(get_name (), get_flags (), 0))
 | |
|     {
 | |
|       int sawerr = 0;
 | |
|       if (!get_io_handle ())
 | |
| 	{
 | |
| 	  system_printf ("error opening input console handle after exec, errno %d, %E", get_errno ());
 | |
| 	  sawerr = 1;
 | |
| 	}
 | |
|       if (!get_output_handle ())
 | |
| 	{
 | |
| 	  system_printf ("error opening input console handle after exec, errno %d, %E", get_errno ());
 | |
| 	  sawerr = 1;
 | |
| 	}
 | |
| 
 | |
|       if (!sawerr)
 | |
| 	system_printf ("error opening console after exec, errno %d, %E", get_errno ());
 | |
|     }
 | |
| 
 | |
|   CloseHandle (h);
 | |
|   CloseHandle (oh);
 | |
|   return;
 | |
| }
 |