OSDN Git Service

558e793e760836930376f9b56188aafb771248a4
[pf3gnuchains/sourceware.git] / winsup / cygwin / fhandler_console.cc
1 /* fhandler_console.cc
2
3    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
11 #include "winsup.h"
12 #include <sys/termios.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <wingdi.h>
17 #include <winuser.h>
18 #include <wincon.h>
19 #include <winnls.h>
20 #include <ctype.h>
21 #include <sys/cygwin.h>
22 #include "cygerrno.h"
23 #include "security.h"
24 #include "path.h"
25 #include "fhandler.h"
26 #include "dtable.h"
27 #include "cygheap.h"
28 #include "sigproc.h"
29 #include "pinfo.h"
30 #include "shared_info.h"
31 #include "cygtls.h"
32
33 #define CONVERT_LIMIT 16384
34
35 static bool
36 cp_convert (UINT destcp, char *dest, UINT srccp, const char *src, DWORD size)
37 {
38   if (!size)
39     /* no action */;
40   else if (destcp == srccp)
41     {
42       if (dest != src)
43         memcpy (dest, src, size);
44     }
45   else
46     {
47       WCHAR wbuffer[CONVERT_LIMIT]; /* same size as the maximum input, s.b. */
48       if (!MultiByteToWideChar (srccp, 0, src, size, wbuffer, sizeof (wbuffer)))
49         return false;
50       if (!WideCharToMultiByte (destcp, 0, wbuffer, size, dest, size,
51                                 NULL, NULL))
52         return false;
53     }
54   return true;
55 }
56
57 /*
58  * Scroll the screen context.
59  * x1, y1 - ul corner
60  * x2, y2 - dr corner
61  * xn, yn - new ul corner
62  * Negative values represents current screen dimensions
63  */
64
65 #define srTop (dev_state->info.winTop + dev_state->scroll_region.Top)
66 #define srBottom ((dev_state->scroll_region.Bottom < 0) ? dev_state->info.winBottom : dev_state->info.winTop + dev_state->scroll_region.Bottom)
67
68 #define use_tty ISSTATE (myself, PID_USETTY)
69
70 const char * get_nonascii_key (INPUT_RECORD&, char *);
71
72 static console_state NO_COPY *shared_console_info;
73
74 dev_console NO_COPY *fhandler_console::dev_state;
75
76 /* Allocate and initialize the shared record for the current console.
77    Returns a pointer to shared_console_info. */
78 tty_min *
79 fhandler_console::get_tty_stuff (int flags = 0)
80 {
81   if (dev_state)
82     return &shared_console_info->tty_min_state;
83
84   shared_locations sh_shared_console = SH_SHARED_CONSOLE;
85   shared_console_info =
86     (console_state *) open_shared (NULL, 0, cygheap->console_h,
87                                    sizeof (*shared_console_info),
88                                    sh_shared_console);
89   dev_state = &shared_console_info->dev_state;
90
91   ProtectHandleINH (cygheap->console_h);
92   if (!shared_console_info->tty_min_state.ntty)
93     {
94       shared_console_info->tty_min_state.setntty (TTY_CONSOLE);
95       shared_console_info->tty_min_state.setsid (myself->sid);
96       myself->set_ctty (&shared_console_info->tty_min_state, flags, NULL);
97
98       dev_state->scroll_region.Bottom = -1;
99       dev_state->dwLastCursorPosition.X = -1;
100       dev_state->dwLastCursorPosition.Y = -1;
101       dev_state->underline_color = FOREGROUND_GREEN | FOREGROUND_BLUE;
102       dev_state->dim_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
103       dev_state->meta_mask = LEFT_ALT_PRESSED;
104       /* Set the mask that determines if an input keystroke is modified by
105          META.  We set this based on the keyboard layout language loaded
106          for the current thread.  The left <ALT> key always generates
107          META, but the right <ALT> key only generates META if we are using
108          an English keyboard because many "international" keyboards
109          replace common shell symbols ('[', '{', etc.) with accented
110          language-specific characters (umlaut, accent grave, etc.).  On
111          these keyboards right <ALT> (called AltGr) is used to produce the
112          shell symbols and should not be interpreted as META. */
113       if (PRIMARYLANGID (LOWORD (GetKeyboardLayout (0))) == LANG_ENGLISH)
114         dev_state->meta_mask |= RIGHT_ALT_PRESSED;
115       dev_state->set_default_attr ();
116     }
117
118   return &shared_console_info->tty_min_state;
119 }
120
121 void
122 set_console_ctty ()
123 {
124   fhandler_console::get_tty_stuff ();
125 }
126
127 /* Return the tty structure associated with a given tty number.  If the
128    tty number is < 0, just return a dummy record. */
129 tty_min *
130 tty_list::get_tty (int n)
131 {
132   static tty_min nada;
133   if (n == TTY_CONSOLE)
134     return fhandler_console::get_tty_stuff ();
135   else if (n >= 0)
136     return &cygwin_shared->tty.ttys[n];
137   else
138     return &nada;
139 }
140
141
142 /* Determine if a console is associated with this process prior to a spawn.
143    If it is, then we'll return 1.  If the console has been initialized, then
144    set it into a more friendly state for non-cygwin apps. */
145 int __stdcall
146 set_console_state_for_spawn ()
147 {
148   HANDLE h = CreateFile ("CONIN$", GENERIC_READ, FILE_SHARE_WRITE,
149                          &sec_none_nih, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
150                          NULL);
151
152   if (h == INVALID_HANDLE_VALUE)
153     return 0;
154
155   if (shared_console_info != NULL)
156     {
157       /* ACK.  Temporarily define for use in TTYSETF macro */
158       SetConsoleMode (h, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT);
159       shared_console_info->tty_min_state.rstcons (true);
160     }
161
162   CloseHandle (h);
163   return 1;
164 }
165
166 /* The results of GetConsoleCP() and GetConsoleOutputCP() cannot be
167    cached, because a program or the user can change these values at
168    any time. */
169 inline bool
170 dev_console::con_to_str (char *d, const char *s, DWORD sz)
171 {
172   return cp_convert (get_cp (), d, GetConsoleCP (), s, sz);
173 }
174
175 inline bool
176 dev_console::str_to_con (char *d, const char *s, DWORD sz)
177 {
178   if (alternate_charset_active)
179     {
180       /* no translation when alternate charset is active */
181       memcpy(d, s, sz);
182       return true;
183     }
184   return cp_convert (GetConsoleOutputCP (), d, get_cp (), s, sz);
185 }
186
187 bool
188 fhandler_console::set_raw_win32_keyboard_mode (bool new_mode)
189 {
190   bool old_mode = dev_state->raw_win32_keyboard_mode;
191   dev_state->raw_win32_keyboard_mode = new_mode;
192   syscall_printf ("raw keyboard mode %sabled", dev_state->raw_win32_keyboard_mode ? "en" : "dis");
193   return old_mode;
194 };
195
196 void
197 fhandler_console::set_cursor_maybe ()
198 {
199   CONSOLE_SCREEN_BUFFER_INFO now;
200
201   if (!GetConsoleScreenBufferInfo (get_output_handle (), &now))
202     return;
203
204   if (dev_state->dwLastCursorPosition.X != now.dwCursorPosition.X ||
205       dev_state->dwLastCursorPosition.Y != now.dwCursorPosition.Y)
206     {
207       SetConsoleCursorPosition (get_output_handle (), now.dwCursorPosition);
208       dev_state->dwLastCursorPosition = now.dwCursorPosition;
209     }
210 }
211
212 void
213 fhandler_console::send_winch_maybe ()
214 {
215   SHORT y = dev_state->info.dwWinSize.Y;
216   SHORT x = dev_state->info.dwWinSize.X;
217   dev_state->fillin_info (get_output_handle ());
218
219   if (y != dev_state->info.dwWinSize.Y || x != dev_state->info.dwWinSize.X)
220     {
221       extern fhandler_tty_master *tty_master;
222       dev_state->scroll_region.Top = 0;
223       dev_state->scroll_region.Bottom = -1;
224       if (tty_master)
225         tty_master->set_winsize (true);
226       else
227         tc->kill_pgrp (SIGWINCH);
228     }
229 }
230
231 void __stdcall
232 fhandler_console::read (void *pv, size_t& buflen)
233 {
234   HANDLE h = get_io_handle ();
235
236 #define buf ((char *) pv)
237
238   int ch;
239   set_input_state ();
240
241   int copied_chars = get_readahead_into_buffer (buf, buflen);
242
243   if (copied_chars)
244     {
245       buflen = copied_chars;
246       return;
247     }
248
249   HANDLE w4[2];
250   DWORD nwait;
251   char tmp[60];
252
253   w4[0] = h;
254   if (&_my_tls != _main_tls)
255     nwait = 1;
256   else
257     {
258       w4[1] = signal_arrived;
259       nwait = 2;
260     }
261
262   termios ti = tc->ti;
263   for (;;)
264     {
265       int bgres;
266       if ((bgres = bg_check (SIGTTIN)) <= bg_eof)
267         {
268           buflen = bgres;
269           return;
270         }
271
272       set_cursor_maybe ();      /* to make cursor appear on the screen immediately */
273       switch (WaitForMultipleObjects (nwait, w4, FALSE, INFINITE))
274         {
275         case WAIT_OBJECT_0:
276           break;
277         case WAIT_OBJECT_0 + 1:
278           goto sig_exit;
279         default:
280           goto err;
281         }
282
283       DWORD nread;
284       INPUT_RECORD input_rec;
285       const char *toadd = NULL;
286
287       if (!ReadConsoleInput (h, &input_rec, 1, &nread))
288         {
289           syscall_printf ("ReadConsoleInput failed, %E");
290           goto err;             /* seems to be failure */
291         }
292
293       /* check the event that occurred */
294       switch (input_rec.EventType)
295         {
296         case KEY_EVENT:
297 #define virtual_key_code (input_rec.Event.KeyEvent.wVirtualKeyCode)
298 #define control_key_state (input_rec.Event.KeyEvent.dwControlKeyState)
299
300           dev_state->nModifiers = 0;
301
302 #ifdef DEBUGGING
303           /* allow manual switching to/from raw mode via ctrl-alt-scrolllock */
304           if (input_rec.Event.KeyEvent.bKeyDown &&
305               virtual_key_code == VK_SCROLL &&
306               control_key_state & (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED) == LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED
307             )
308             {
309               set_raw_win32_keyboard_mode (!dev_state->raw_win32_keyboard_mode);
310               continue;
311             }
312 #endif
313
314           if (dev_state->raw_win32_keyboard_mode)
315             {
316               __small_sprintf (tmp, "\033{%u;%u;%u;%u;%u;%luK",
317                                     input_rec.Event.KeyEvent.bKeyDown,
318                                     input_rec.Event.KeyEvent.wRepeatCount,
319                                     input_rec.Event.KeyEvent.wVirtualKeyCode,
320                                     input_rec.Event.KeyEvent.wVirtualScanCode,
321                                     input_rec.Event.KeyEvent.uChar.UnicodeChar,
322                                     input_rec.Event.KeyEvent.dwControlKeyState);
323               toadd = tmp;
324               nread = strlen (toadd);
325               break;
326             }
327
328 #define ich (input_rec.Event.KeyEvent.uChar.AsciiChar)
329 #define wch (input_rec.Event.KeyEvent.uChar.UnicodeChar)
330 #define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
331 #define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
332
333           /* Ignore key up events, except for left alt events with non-zero character
334            */
335           if (!input_rec.Event.KeyEvent.bKeyDown &&
336               /*
337                 Event for left alt, with a non-zero character, comes from
338                 "alt + numerics" key sequence.
339                 e.g. <left-alt> 0233 => &eacute;
340               */
341               !(wch != 0
342                 // ?? experimentally determined on an XP system
343                 && virtual_key_code == VK_MENU
344                 // left alt -- see http://www.microsoft.com/hwdev/tech/input/Scancode.asp
345                 && input_rec.Event.KeyEvent.wVirtualScanCode == 0x38))
346             continue;
347
348           if (control_key_state & SHIFT_PRESSED)
349             dev_state->nModifiers |= 1;
350           if (control_key_state & RIGHT_ALT_PRESSED)
351             dev_state->nModifiers |= 2;
352           if (control_key_state & CTRL_PRESSED)
353             dev_state->nModifiers |= 4;
354           if (control_key_state & LEFT_ALT_PRESSED)
355             dev_state->nModifiers |= 8;
356
357           if (wch == 0 ||
358               /* arrow/function keys */
359               (input_rec.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
360             {
361               toadd = get_nonascii_key (input_rec, tmp);
362               if (!toadd)
363                 {
364                   dev_state->nModifiers = 0;
365                   continue;
366                 }
367               nread = strlen (toadd);
368             }
369           else
370             {
371               tmp[1] = ich;
372               /* Need this check since US code page seems to have a bug when
373                  converting a CTRL-U. */
374               if ((unsigned char) ich > 0x7f)
375                 dev_state->con_to_str (tmp + 1, tmp + 1, 1);
376               /* Determine if the keystroke is modified by META.  The tricky
377                  part is to distinguish whether the right Alt key should be
378                  recognized as Alt, or as AltGr. */
379               bool meta;
380               if (wincap.altgr_is_ctrl_alt ())
381                 /* WinNT: AltGr is reported as Ctrl+Alt, and Ctrl+Alt is
382                    treated just like AltGr.  However, if Ctrl+Alt+key generates
383                    an ASCII control character, interpret is as META. */
384                 meta = (control_key_state & ALT_PRESSED) != 0
385                        && ((control_key_state & CTRL_PRESSED) == 0
386                            || ((signed char) ich >= 0 && ich <= 0x1f || ich == 0x7f));
387               else
388                 /* Win9x: there's no way to distinguish Alt from AltGr, so rely
389                    on dev_state->meta_mask heuristic (see fhandler_console constructor). */
390                 meta = (control_key_state & dev_state->meta_mask) != 0;
391               if (!meta)
392                 toadd = tmp + 1;
393               else
394                 {
395                   tmp[0] = '\033';
396                   tmp[1] = cyg_tolower (tmp[1]);
397                   toadd = tmp;
398                   nread++;
399                   dev_state->nModifiers &= ~4;
400                 }
401             }
402 #undef ich
403 #undef wch
404 #undef ALT_PRESSED
405 #undef CTRL_PRESSED
406           break;
407
408         case MOUSE_EVENT:
409           send_winch_maybe ();
410           if (dev_state->use_mouse)
411             {
412               MOUSE_EVENT_RECORD& mouse_event = input_rec.Event.MouseEvent;
413
414               /* Treat the double-click event like a regular button press */
415               if (mouse_event.dwEventFlags == DOUBLE_CLICK)
416                 {
417                   syscall_printf ("mouse: double-click -> click");
418                   mouse_event.dwEventFlags = 0;
419                 }
420
421               /* Did something other than a click occur? */
422               if (mouse_event.dwEventFlags)
423                 continue;
424
425               /* Retrieve reported mouse position */
426               int x = mouse_event.dwMousePosition.X;
427               int y = mouse_event.dwMousePosition.Y;
428
429               /* Adjust mouse position by scroll buffer offset */
430               CONSOLE_SCREEN_BUFFER_INFO now;
431               if (GetConsoleScreenBufferInfo (get_output_handle (), &now))
432                 {
433                   y -= now.srWindow.Top;
434                   x -= now.srWindow.Left;
435                 }
436               else
437                 {
438                   syscall_printf ("mouse: cannot adjust position by scroll buffer offset");
439                   continue;
440                 }
441
442               /* If the mouse event occurred out of the area we can handle,
443                  ignore it. */
444               if ((x + ' ' + 1 > 0xFF) || (y + ' ' + 1 > 0xFF))
445                 {
446                   syscall_printf ("mouse: position out of range");
447                   continue;
448                 }
449
450               /* Ignore unimportant mouse buttons */
451               mouse_event.dwButtonState &= 0x7;
452
453               /* This code assumes Windows never reports multiple button
454                  events at the same time. */
455               int b = 0;
456               char sz[32];
457               if (mouse_event.dwButtonState == dev_state->dwLastButtonState)
458                 {
459                   syscall_printf ("mouse: button state unchanged");
460                   continue;
461                 }
462               else if (mouse_event.dwButtonState < dev_state->dwLastButtonState)
463                 {
464                   b = 3;
465                   strcpy (sz, "btn up");
466                 }
467               else if ((mouse_event.dwButtonState & 1) != (dev_state->dwLastButtonState & 1))
468                 {
469                   b = 0;
470                   strcpy (sz, "btn1 down");
471                 }
472               else if ((mouse_event.dwButtonState & 2) != (dev_state->dwLastButtonState & 2))
473                 {
474                   b = 2;
475                   strcpy (sz, "btn2 down");
476                 }
477               else if ((mouse_event.dwButtonState & 4) != (dev_state->dwLastButtonState & 4))
478                 {
479                   b = 1;
480                   strcpy (sz, "btn3 down");
481                 }
482
483               /* Remember the current button state */
484               dev_state->dwLastButtonState = mouse_event.dwButtonState;
485
486               /* If a button was pressed, remember the modifiers */
487               if (b != 3)
488                 {
489                   dev_state->nModifiers = 0;
490                   if (mouse_event.dwControlKeyState & SHIFT_PRESSED)
491                     dev_state->nModifiers |= 0x4;
492                   if (mouse_event.dwControlKeyState & (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED))
493                     dev_state->nModifiers |= 0x8;
494                   if (mouse_event.dwControlKeyState & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED))
495                     dev_state->nModifiers |= 0x10;
496                 }
497
498               b |= dev_state->nModifiers;
499
500               /* We can now create the code. */
501               sprintf (tmp, "\033[M%c%c%c", b + ' ', x + ' ' + 1, y + ' ' + 1);
502               syscall_printf ("mouse: %s at (%d,%d)", sz, x, y);
503
504               toadd = tmp;
505               nread = 6;
506             }
507           break;
508
509         case FOCUS_EVENT:
510         case WINDOW_BUFFER_SIZE_EVENT:
511           send_winch_maybe ();
512           /* fall through */
513         default:
514           continue;
515         }
516
517       if (toadd)
518         {
519           line_edit_status res = line_edit (toadd, nread, ti);
520           if (res == line_edit_signalled)
521             goto sig_exit;
522           else if (res == line_edit_input_done)
523             break;
524         }
525 #undef ich
526     }
527
528   while (buflen)
529     if ((ch = get_readahead ()) < 0)
530       break;
531     else
532       {
533         buf[copied_chars++] = (unsigned char)(ch & 0xff);
534         buflen--;
535       }
536 #undef buf
537
538   buflen = copied_chars;
539   return;
540
541 err:
542   __seterrno ();
543   buflen = (size_t) -1;
544   return;
545
546 sig_exit:
547   set_sig_errno (EINTR);
548   buflen = (size_t) -1;
549 }
550
551 void
552 fhandler_console::set_input_state ()
553 {
554   if (tc->rstcons ())
555     input_tcsetattr (0, &tc->ti);
556 }
557
558 bool
559 dev_console::fillin_info (HANDLE h)
560 {
561   bool ret;
562   CONSOLE_SCREEN_BUFFER_INFO linfo;
563
564   if ((ret = GetConsoleScreenBufferInfo (h, &linfo)))
565     {
566       info.winTop = linfo.srWindow.Top;
567       info.winBottom = linfo.srWindow.Bottom;
568       info.dwWinSize.Y = 1 + linfo.srWindow.Bottom - linfo.srWindow.Top;
569       info.dwWinSize.X = 1 + linfo.srWindow.Right - linfo.srWindow.Left;
570       info.dwBufferSize = linfo.dwSize;
571       info.dwCursorPosition = linfo.dwCursorPosition;
572       info.wAttributes = linfo.wAttributes;
573     }
574   else
575     {
576       memset (&info, 0, sizeof info);
577       info.dwWinSize.Y = 25;
578       info.dwWinSize.X = 80;
579       info.winBottom = 24;
580     }
581
582   return ret;
583 }
584
585 void
586 fhandler_console::scroll_screen (int x1, int y1, int x2, int y2, int xn, int yn)
587 {
588   SMALL_RECT sr1, sr2;
589   CHAR_INFO fill;
590   COORD dest;
591
592   dev_state->fillin_info (get_output_handle ());
593   sr1.Left = x1 >= 0 ? x1 : dev_state->info.dwWinSize.X - 1;
594   if (y1 == 0)
595     sr1.Top = dev_state->info.winTop;
596   else
597     sr1.Top = y1 > 0 ? y1 : dev_state->info.winBottom;
598   sr1.Right = x2 >= 0 ? x2 : dev_state->info.dwWinSize.X - 1;
599   if (y2 == 0)
600     sr1.Bottom = dev_state->info.winTop;
601   else
602     sr1.Bottom = y2 > 0 ? y2 : dev_state->info.winBottom;
603   sr2.Top = srTop;
604   sr2.Left = 0;
605   sr2.Bottom = srBottom;
606   sr2.Right = dev_state->info.dwWinSize.X - 1;
607   if (sr1.Bottom > sr2.Bottom && sr1.Top <= sr2.Bottom)
608     sr1.Bottom = sr2.Bottom;
609   dest.X = xn >= 0 ? xn : dev_state->info.dwWinSize.X - 1;
610   if (yn == 0)
611     dest.Y = dev_state->info.winTop;
612   else
613     dest.Y = yn > 0 ? yn : dev_state->info.winBottom;
614   fill.Char.AsciiChar = ' ';
615   fill.Attributes = dev_state->current_win32_attr;
616   ScrollConsoleScreenBuffer (get_output_handle (), &sr1, &sr2, dest, &fill);
617
618   /* ScrollConsoleScreenBuffer on Windows 95 is buggy - when scroll distance
619    * is more than half of screen, filling doesn't work as expected */
620
621   if (sr1.Top != sr1.Bottom)
622     if (dest.Y <= sr1.Top)      /* forward scroll */
623       clear_screen (0, 1 + dest.Y + sr1.Bottom - sr1.Top, sr2.Right, sr2.Bottom);
624     else                        /* reverse scroll */
625       clear_screen (0, sr1.Top, sr2.Right, dest.Y - 1);
626 }
627
628 int
629 fhandler_console::open (int flags, mode_t)
630 {
631   HANDLE h;
632
633   tcinit (get_tty_stuff (flags));
634
635   set_io_handle (NULL);
636   set_output_handle (NULL);
637
638   set_flags ((flags & ~O_TEXT) | O_BINARY);
639
640   /* Open the input handle as handle_ */
641   h = CreateFile ("CONIN$", GENERIC_READ | GENERIC_WRITE,
642                   FILE_SHARE_READ | FILE_SHARE_WRITE, &sec_none,
643                   OPEN_EXISTING, 0, 0);
644
645   if (h == INVALID_HANDLE_VALUE)
646     {
647       __seterrno ();
648       return 0;
649     }
650   set_io_handle (h);
651   uninterruptible_io (true);    // Handled explicitly in read code
652
653   h = CreateFile ("CONOUT$", GENERIC_READ | GENERIC_WRITE,
654                   FILE_SHARE_READ | FILE_SHARE_WRITE, &sec_none,
655                   OPEN_EXISTING, 0, 0);
656
657   if (h == INVALID_HANDLE_VALUE)
658     {
659       __seterrno ();
660       return 0;
661     }
662   set_output_handle (h);
663
664   if (dev_state->fillin_info (get_output_handle ()))
665     {
666       dev_state->current_win32_attr = dev_state->info.wAttributes;
667       if (!dev_state->default_color)
668         dev_state->default_color = dev_state->info.wAttributes;
669       dev_state->set_default_attr ();
670     }
671
672   DWORD cflags;
673   if (GetConsoleMode (get_io_handle (), &cflags))
674     {
675       cflags |= ENABLE_PROCESSED_INPUT;
676       SetConsoleMode (get_io_handle (), ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | cflags);
677     }
678
679   tc->rstcons (false);
680   set_open_status ();
681   cygheap->open_fhs++;
682   debug_printf ("incremented open_fhs, now %d", cygheap->open_fhs);
683   debug_printf ("opened conin$ %p, conout$ %p", get_io_handle (),
684                 get_output_handle ());
685
686   return 1;
687 }
688
689 int
690 fhandler_console::close ()
691 {
692   CloseHandle (get_io_handle ());
693   CloseHandle (get_output_handle ());
694   if (!hExeced && --(cygheap->open_fhs) <= 0 && myself->ctty != TTY_CONSOLE)
695     {
696       syscall_printf ("open_fhs %d", cygheap->open_fhs);
697       FreeConsole ();
698     }
699   debug_printf ("decremented open_fhs, now %d", cygheap->open_fhs);
700   return 0;
701 }
702
703 /*
704  * Special console dup to duplicate input and output
705  * handles.
706  */
707
708 int
709 fhandler_console::dup (fhandler_base *child, HANDLE from_proc)
710 {
711   fhandler_console *fhc = (fhandler_console *) child;
712
713   if (!fhc->open (get_flags () & ~O_NOCTTY, 0))
714     system_printf ("error opening console, %E");
715
716   return 0;
717 }
718
719 int
720 fhandler_console::ioctl (unsigned int cmd, void *buf)
721 {
722   switch (cmd)
723     {
724       case TIOCGWINSZ:
725         int st;
726
727         st = dev_state->fillin_info (get_output_handle ());
728         if (st)
729           {
730             /* *not* the buffer size, the actual screen size... */
731             /* based on Left Top Right Bottom of srWindow */
732             ((struct winsize *) buf)->ws_row = dev_state->info.dwWinSize.Y;
733             ((struct winsize *) buf)->ws_col = dev_state->info.dwWinSize.X;
734             syscall_printf ("WINSZ: (row=%d,col=%d)",
735                            ((struct winsize *) buf)->ws_row,
736                            ((struct winsize *) buf)->ws_col);
737             return 0;
738           }
739         else
740           {
741             syscall_printf ("WINSZ failed");
742             __seterrno ();
743             return -1;
744           }
745         return 0;
746       case TIOCSWINSZ:
747         bg_check (SIGTTOU);
748         return 0;
749       case TIOCLINUX:
750         if (* (int *) buf == 6)
751           {
752             * (int *) buf = dev_state->nModifiers;
753             return 0;
754           }
755         else
756           {
757             set_errno (EINVAL);
758             return -1;
759           }
760     }
761
762   return fhandler_base::ioctl (cmd, buf);
763 }
764
765 int
766 fhandler_console::tcflush (int queue)
767 {
768   int res = 0;
769   if (queue == TCIFLUSH
770       || queue == TCIOFLUSH)
771     {
772       if (!FlushConsoleInputBuffer (get_io_handle ()))
773         {
774           __seterrno ();
775           res = -1;
776         }
777     }
778   return res;
779 }
780
781 int
782 fhandler_console::output_tcsetattr (int, struct termios const *t)
783 {
784   /* All the output bits we can ignore */
785
786   DWORD flags = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
787
788   int res = SetConsoleMode (get_output_handle (), flags) ? 0 : -1;
789   syscall_printf ("%d = tcsetattr (,%x) (ENABLE FLAGS %x) (lflag %x oflag %x)",
790                   res, t, flags, t->c_lflag, t->c_oflag);
791   return res;
792 }
793
794 int
795 fhandler_console::input_tcsetattr (int, struct termios const *t)
796 {
797   /* Ignore the optional_actions stuff, since all output is emitted
798      instantly */
799
800   DWORD oflags;
801
802   if (!GetConsoleMode (get_io_handle (), &oflags))
803     oflags = 0;
804   DWORD flags = 0;
805
806 #if 0
807   /* Enable/disable LF -> CRLF conversions */
808   rbinary ((t->c_iflag & INLCR) ? false : true);
809 #endif
810
811   /* There's some disparity between what we need and what's
812      available.  We've got ECHO and ICANON, they've
813      got ENABLE_ECHO_INPUT and ENABLE_LINE_INPUT. */
814
815   tc->ti = *t;
816
817   if (t->c_lflag & ECHO)
818     {
819       flags |= ENABLE_ECHO_INPUT;
820     }
821   if (t->c_lflag & ICANON)
822     {
823       flags |= ENABLE_LINE_INPUT;
824     }
825
826   if (flags & ENABLE_ECHO_INPUT
827       && !(flags & ENABLE_LINE_INPUT))
828     {
829       /* This is illegal, so turn off the echo here, and fake it
830          when we read the characters */
831
832       flags &= ~ENABLE_ECHO_INPUT;
833     }
834
835   if (t->c_lflag & ISIG)
836     {
837       flags |= ENABLE_PROCESSED_INPUT;
838     }
839
840   if (use_tty)
841     {
842       flags = 0; // ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
843       tc->ti.c_iflag = 0;
844       tc->ti.c_lflag = 0;
845     }
846
847   flags |= ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT;
848
849   int res;
850   if (flags == oflags)
851     res = 0;
852   else
853     {
854       res = SetConsoleMode (get_io_handle (), flags) ? 0 : -1;
855       if (res < 0)
856         __seterrno ();
857       syscall_printf ("%d = tcsetattr (,%x) enable flags %p, c_lflag %p iflag %p",
858                       res, t, flags, t->c_lflag, t->c_iflag);
859     }
860
861   tc->rstcons (false);
862   return res;
863 }
864
865 int
866 fhandler_console::tcsetattr (int a, struct termios const *t)
867 {
868   int res = output_tcsetattr (a, t);
869   if (res != 0)
870     return res;
871   return input_tcsetattr (a, t);
872 }
873
874 int
875 fhandler_console::tcgetattr (struct termios *t)
876 {
877   int res;
878   *t = tc->ti;
879
880   t->c_cflag |= CS8;
881
882   DWORD flags;
883
884   if (!GetConsoleMode (get_io_handle (), &flags))
885     {
886       __seterrno ();
887       res = -1;
888     }
889   else
890     {
891       if (flags & ENABLE_ECHO_INPUT)
892         t->c_lflag |= ECHO;
893
894       if (flags & ENABLE_LINE_INPUT)
895         t->c_lflag |= ICANON;
896
897       if (flags & ENABLE_PROCESSED_INPUT)
898         t->c_lflag |= ISIG;
899
900       /* What about ENABLE_WINDOW_INPUT
901          and ENABLE_MOUSE_INPUT   ? */
902
903       /* All the output bits we can ignore */
904       res = 0;
905     }
906   syscall_printf ("%d = tcgetattr (%p) enable flags %p, t->lflag %p, t->iflag %p",
907                  res, t, flags, t->c_lflag, t->c_iflag);
908   return res;
909 }
910
911 fhandler_console::fhandler_console () :
912   fhandler_termios ()
913 {
914 }
915 void
916 dev_console::set_color (HANDLE h)
917 {
918   WORD win_fg = fg;
919   WORD win_bg = bg;
920   if (reverse)
921     {
922       WORD save_fg = win_fg;
923       win_fg = (win_bg & BACKGROUND_RED   ? FOREGROUND_RED   : 0) |
924                (win_bg & BACKGROUND_GREEN ? FOREGROUND_GREEN : 0) |
925                (win_bg & BACKGROUND_BLUE  ? FOREGROUND_BLUE  : 0) |
926                (win_bg & BACKGROUND_INTENSITY ? FOREGROUND_INTENSITY : 0);
927       win_bg = (save_fg & FOREGROUND_RED   ? BACKGROUND_RED   : 0) |
928                (save_fg & FOREGROUND_GREEN ? BACKGROUND_GREEN : 0) |
929                (save_fg & FOREGROUND_BLUE  ? BACKGROUND_BLUE  : 0) |
930                (save_fg & FOREGROUND_INTENSITY ? BACKGROUND_INTENSITY : 0);
931     }
932   if (underline)
933     win_fg = underline_color;
934   /* emulate blink with bright background */
935   if (blink)
936     win_bg |= BACKGROUND_INTENSITY;
937   if (intensity == INTENSITY_INVISIBLE)
938     win_fg = win_bg;
939   else if (intensity == INTENSITY_BOLD)
940     win_fg |= FOREGROUND_INTENSITY;
941   current_win32_attr = win_fg | win_bg;
942   if (h)
943     SetConsoleTextAttribute (h, current_win32_attr);
944 }
945
946 #define FOREGROUND_ATTR_MASK (FOREGROUND_RED | FOREGROUND_GREEN | \
947                               FOREGROUND_BLUE | FOREGROUND_INTENSITY)
948 #define BACKGROUND_ATTR_MASK (BACKGROUND_RED | BACKGROUND_GREEN | \
949                               BACKGROUND_BLUE | BACKGROUND_INTENSITY)
950 void
951 dev_console::set_default_attr ()
952 {
953   blink = underline = reverse = false;
954   intensity = INTENSITY_NORMAL;
955   fg = default_color & FOREGROUND_ATTR_MASK;
956   bg = default_color & BACKGROUND_ATTR_MASK;
957   set_color (NULL);
958 }
959
960 /*
961  * Clear the screen context from x1/y1 to x2/y2 cell.
962  * Negative values represents current screen dimensions
963  */
964 void
965 fhandler_console::clear_screen (int x1, int y1, int x2, int y2)
966 {
967   COORD tlc;
968   DWORD done;
969   int num;
970
971   dev_state->fillin_info (get_output_handle ());
972
973   if (x1 < 0)
974     x1 = dev_state->info.dwWinSize.X - 1;
975   if (y1 < 0)
976     y1 = dev_state->info.winBottom;
977   if (x2 < 0)
978     x2 = dev_state->info.dwWinSize.X - 1;
979   if (y2 < 0)
980     y2 = dev_state->info.winBottom;
981
982   num = abs (y1 - y2) * dev_state->info.dwBufferSize.X + abs (x1 - x2) + 1;
983
984   if ((y2 * dev_state->info.dwBufferSize.X + x2) > (y1 * dev_state->info.dwBufferSize.X + x1))
985     {
986       tlc.X = x1;
987       tlc.Y = y1;
988     }
989   else
990     {
991       tlc.X = x2;
992       tlc.Y = y2;
993     }
994   FillConsoleOutputCharacterA (get_output_handle (), ' ',
995                                num,
996                                tlc,
997                                &done);
998   FillConsoleOutputAttribute (get_output_handle (),
999                                dev_state->current_win32_attr,
1000                                num,
1001                                tlc,
1002                                &done);
1003 }
1004
1005 void
1006 fhandler_console::cursor_set (bool rel_to_top, int x, int y)
1007 {
1008   COORD pos;
1009
1010   dev_state->fillin_info (get_output_handle ());
1011   if (y > dev_state->info.winBottom)
1012     y = dev_state->info.winBottom;
1013   else if (y < 0)
1014     y = 0;
1015   else if (rel_to_top)
1016     y += dev_state->info.winTop;
1017
1018   if (x > dev_state->info.dwWinSize.X)
1019     x = dev_state->info.dwWinSize.X - 1;
1020   else if (x < 0)
1021     x = 0;
1022
1023   pos.X = x;
1024   pos.Y = y;
1025   SetConsoleCursorPosition (get_output_handle (), pos);
1026 }
1027
1028 void
1029 fhandler_console::cursor_rel (int x, int y)
1030 {
1031   dev_state->fillin_info (get_output_handle ());
1032   x += dev_state->info.dwCursorPosition.X;
1033   y += dev_state->info.dwCursorPosition.Y;
1034   cursor_set (false, x, y);
1035 }
1036
1037 void
1038 fhandler_console::cursor_get (int *x, int *y)
1039 {
1040   dev_state->fillin_info (get_output_handle ());
1041   *y = dev_state->info.dwCursorPosition.Y;
1042   *x = dev_state->info.dwCursorPosition.X;
1043 }
1044
1045 #define BAK 1
1046 #define ESC 2
1047 #define NOR 0
1048 #define IGN 4
1049 #if 0
1050 #define ERR 5
1051 #else
1052 #define ERR NOR
1053 #endif
1054 #define DWN 6
1055 #define BEL 7
1056 #define TAB 8 /* We should't let the console deal with these */
1057 #define CR 13
1058 #define LF 10
1059
1060 static const char base_chars[256] =
1061 {
1062 /*00 01 02 03 04 05 06 07 */ IGN, ERR, ERR, NOR, NOR, NOR, NOR, BEL,
1063 /*08 09 0A 0B 0C 0D 0E 0F */ BAK, TAB, DWN, ERR, ERR, CR,  ERR, IGN,
1064 /*10 11 12 13 14 15 16 17 */ NOR, NOR, ERR, ERR, ERR, ERR, ERR, ERR,
1065 /*18 19 1A 1B 1C 1D 1E 1F */ NOR, NOR, ERR, ESC, ERR, ERR, ERR, ERR,
1066 /*   !  "  #  $  %  &  '  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1067 /*()  *  +  ,  -  .  /  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1068 /*0  1  2  3  4  5  6  7  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1069 /*8  9  :  ;  <  =  >  ?  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1070 /*@  A  B  C  D  E  F  G  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1071 /*H  I  J  K  L  M  N  O  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1072 /*P  Q  R  S  T  U  V  W  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1073 /*X  Y  Z  [  \  ]  ^  _  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1074 /*`  a  b  c  d  e  f  g  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1075 /*h  i  j  k  l  m  n  o  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1076 /*p  q  r  s  t  u  v  w  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1077 /*x  y  z  {  |  }  ~  7F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1078 /*80 81 82 83 84 85 86 87 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1079 /*88 89 8A 8B 8C 8D 8E 8F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1080 /*90 91 92 93 94 95 96 97 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1081 /*98 99 9A 9B 9C 9D 9E 9F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1082 /*A0 A1 A2 A3 A4 A5 A6 A7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1083 /*A8 A9 AA AB AC AD AE AF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1084 /*B0 B1 B2 B3 B4 B5 B6 B7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1085 /*B8 B9 BA BB BC BD BE BF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1086 /*C0 C1 C2 C3 C4 C5 C6 C7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1087 /*C8 C9 CA CB CC CD CE CF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1088 /*D0 D1 D2 D3 D4 D5 D6 D7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1089 /*D8 D9 DA DB DC DD DE DF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1090 /*E0 E1 E2 E3 E4 E5 E6 E7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1091 /*E8 E9 EA EB EC ED EE EF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1092 /*F0 F1 F2 F3 F4 F5 F6 F7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
1093 /*F8 F9 FA FB FC FD FE FF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR };
1094
1095 void
1096 fhandler_console::char_command (char c)
1097 {
1098   int x, y;
1099   char buf[40];
1100
1101   switch (c)
1102     {
1103     case 'm':   /* Set Graphics Rendition */
1104        for (int i = 0; i <= dev_state->nargs_; i++)
1105          switch (dev_state->args_[i])
1106            {
1107              case 0:    /* normal color */
1108                dev_state->set_default_attr ();
1109                break;
1110              case 1:    /* bold */
1111                dev_state->intensity = INTENSITY_BOLD;
1112                break;
1113              case 4:
1114                dev_state->underline = 1;
1115                break;
1116              case 5:    /* blink mode */
1117                dev_state->blink = true;
1118                break;
1119              case 7:    /* reverse */
1120                dev_state->reverse = true;
1121                break;
1122              case 8:    /* invisible */
1123                dev_state->intensity = INTENSITY_INVISIBLE;
1124                break;
1125              case 9:    /* dim */
1126                dev_state->intensity = INTENSITY_DIM;
1127                break;
1128              case 10:   /* end alternate charset */
1129                dev_state->alternate_charset_active = false;
1130                break;
1131              case 11:   /* start alternate charset */
1132                dev_state->alternate_charset_active = true;
1133                break;
1134              case 24:
1135                dev_state->underline = false;
1136                break;
1137              case 27:
1138                dev_state->reverse = false;
1139                break;
1140              case 30:           /* BLACK foreground */
1141                dev_state->fg = 0;
1142                break;
1143              case 31:           /* RED foreground */
1144                dev_state->fg = FOREGROUND_RED;
1145                break;
1146              case 32:           /* GREEN foreground */
1147                dev_state->fg = FOREGROUND_GREEN;
1148                break;
1149              case 33:           /* YELLOW foreground */
1150                dev_state->fg = FOREGROUND_RED | FOREGROUND_GREEN;
1151                break;
1152              case 34:           /* BLUE foreground */
1153                dev_state->fg = FOREGROUND_BLUE;
1154                break;
1155              case 35:           /* MAGENTA foreground */
1156                dev_state->fg = FOREGROUND_RED | FOREGROUND_BLUE;
1157                break;
1158              case 36:           /* CYAN foreground */
1159                dev_state->fg = FOREGROUND_BLUE | FOREGROUND_GREEN;
1160                break;
1161              case 37:           /* WHITE foreg */
1162                dev_state->fg = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
1163                break;
1164              case 39:
1165                dev_state->fg = dev_state->default_color & FOREGROUND_ATTR_MASK;
1166                break;
1167              case 40:           /* BLACK background */
1168                dev_state->bg = 0;
1169                break;
1170              case 41:           /* RED background */
1171                dev_state->bg = BACKGROUND_RED;
1172                break;
1173              case 42:           /* GREEN background */
1174                dev_state->bg = BACKGROUND_GREEN;
1175                break;
1176              case 43:           /* YELLOW background */
1177                dev_state->bg = BACKGROUND_RED | BACKGROUND_GREEN;
1178                break;
1179              case 44:           /* BLUE background */
1180                dev_state->bg = BACKGROUND_BLUE;
1181                break;
1182              case 45:           /* MAGENTA background */
1183                dev_state->bg = BACKGROUND_RED | BACKGROUND_BLUE;
1184                break;
1185              case 46:           /* CYAN background */
1186                dev_state->bg = BACKGROUND_BLUE | BACKGROUND_GREEN;
1187                break;
1188              case 47:    /* WHITE background */
1189                dev_state->bg = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
1190                break;
1191              case 49:
1192                dev_state->bg = dev_state->default_color & BACKGROUND_ATTR_MASK;
1193                break;
1194            }
1195        dev_state->set_color (get_output_handle ());
1196       break;
1197     case 'h':
1198     case 'l':
1199       if (!dev_state->saw_question_mark)
1200         {
1201           switch (dev_state->args_[0])
1202             {
1203             case 4:    /* Insert mode */
1204               dev_state->insert_mode = (c == 'h') ? true : false;
1205               syscall_printf ("insert mode %sabled", dev_state->insert_mode ? "en" : "dis");
1206               break;
1207             }
1208           break;
1209         }
1210       switch (dev_state->args_[0])
1211         {
1212         case 47:   /* Save/Restore screen */
1213           if (c == 'h') /* save */
1214             {
1215               CONSOLE_SCREEN_BUFFER_INFO now;
1216               COORD cob = { 0, 0 };
1217
1218               if (!GetConsoleScreenBufferInfo (get_output_handle (), &now))
1219                 break;
1220
1221               dev_state->savebufsiz.X = now.srWindow.Right - now.srWindow.Left + 1;
1222               dev_state->savebufsiz.Y = now.srWindow.Bottom - now.srWindow.Top + 1;
1223
1224               if (dev_state->savebuf)
1225                 cfree (dev_state->savebuf);
1226               dev_state->savebuf = (PCHAR_INFO) cmalloc (HEAP_1_BUF, sizeof (CHAR_INFO) *
1227                                              dev_state->savebufsiz.X * dev_state->savebufsiz.Y);
1228
1229               ReadConsoleOutputA (get_output_handle (), dev_state->savebuf,
1230                                   dev_state->savebufsiz, cob, &now.srWindow);
1231             }
1232           else          /* restore */
1233             {
1234               CONSOLE_SCREEN_BUFFER_INFO now;
1235               COORD cob = { 0, 0 };
1236
1237               if (!GetConsoleScreenBufferInfo (get_output_handle (), &now))
1238                 break;
1239
1240               if (!dev_state->savebuf)
1241                 break;
1242
1243               WriteConsoleOutputA (get_output_handle (), dev_state->savebuf,
1244                                    dev_state->savebufsiz, cob, &now.srWindow);
1245
1246               cfree (dev_state->savebuf);
1247               dev_state->savebuf = NULL;
1248               dev_state->savebufsiz.X = dev_state->savebufsiz.Y = 0;
1249             }
1250           break;
1251
1252         case 1000: /* Mouse support */
1253           dev_state->use_mouse = (c == 'h') ? true : false;
1254           syscall_printf ("mouse support %sabled", dev_state->use_mouse ? "en" : "dis");
1255           break;
1256
1257         case 2000: /* Raw keyboard mode */
1258           set_raw_win32_keyboard_mode ((c == 'h') ? true : false);
1259           break;
1260
1261         default: /* Ignore */
1262           syscall_printf ("unknown h/l command: %d", dev_state->args_[0]);
1263           break;
1264         }
1265       break;
1266     case 'J':
1267       switch (dev_state->args_[0])
1268         {
1269         case 0:                 /* Clear to end of screen */
1270           cursor_get (&x, &y);
1271           clear_screen (x, y, -1, -1);
1272           break;
1273         case 1:                 /* Clear from beginning of screen to cursor */
1274           cursor_get (&x, &y);
1275           clear_screen (0, 0, x, y);
1276           break;
1277         case 2:                 /* Clear screen */
1278           clear_screen (0, 0, -1, -1);
1279           cursor_set (true, 0,0);
1280           break;
1281         default:
1282           goto bad_escape;
1283         }
1284       break;
1285
1286     case 'A':
1287       cursor_rel (0, -(dev_state->args_[0] ? dev_state->args_[0] : 1));
1288       break;
1289     case 'B':
1290       cursor_rel (0, dev_state->args_[0] ? dev_state->args_[0] : 1);
1291       break;
1292     case 'C':
1293       cursor_rel (dev_state->args_[0] ? dev_state->args_[0] : 1, 0);
1294       break;
1295     case 'D':
1296       cursor_rel (-(dev_state->args_[0] ? dev_state->args_[0] : 1),0);
1297       break;
1298     case 'K':
1299       switch (dev_state->args_[0])
1300         {
1301           case 0:               /* Clear to end of line */
1302             cursor_get (&x, &y);
1303             clear_screen (x, y, -1, y);
1304             break;
1305           case 2:               /* Clear line */
1306             cursor_get (&x, &y);
1307             clear_screen (0, y, -1, y);
1308             break;
1309           case 1:               /* Clear from bol to cursor */
1310             cursor_get (&x, &y);
1311             clear_screen (0, y, x, y);
1312             break;
1313           default:
1314             goto bad_escape;
1315         }
1316       break;
1317     case 'H':
1318     case 'f':
1319       cursor_set (true, (dev_state->args_[1] ? dev_state->args_[1] : 1) - 1,
1320                         (dev_state->args_[0] ? dev_state->args_[0] : 1) - 1);
1321       break;
1322     case 'G':   /* hpa - position cursor at column n - 1 */
1323       cursor_get (&x, &y);
1324       cursor_set (false, (dev_state->args_[0] ? dev_state->args_[0] - 1 : 0), y);
1325       break;
1326     case 'd':   /* vpa - position cursor at line n */
1327       cursor_get (&x, &y);
1328       cursor_set (true, x, (dev_state->args_[0] ? dev_state->args_[0] - 1 : 0));
1329       break;
1330     case 's':   /* Save cursor position */
1331       cursor_get (&dev_state->savex, &dev_state->savey);
1332       dev_state->savey -= dev_state->info.winTop;
1333       break;
1334     case 'u':   /* Restore cursor position */
1335       cursor_set (true, dev_state->savex, dev_state->savey);
1336       break;
1337     case 'I':   /* TAB */
1338       cursor_get (&x, &y);
1339       cursor_set (false, 8 * (x / 8 + 1), y);
1340       break;
1341     case 'L':                           /* AL - insert blank lines */
1342       dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
1343       cursor_get (&x, &y);
1344       scroll_screen (0, y, -1, -1, 0, y + dev_state->args_[0]);
1345       break;
1346     case 'M':                           /* DL - delete lines */
1347       dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
1348       cursor_get (&x, &y);
1349       scroll_screen (0, y + dev_state->args_[0], -1, -1, 0, y);
1350       break;
1351     case '@':                           /* IC - insert chars */
1352       dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
1353       cursor_get (&x, &y);
1354       scroll_screen (x, y, -1, y, x + dev_state->args_[0], y);
1355       break;
1356     case 'P':                           /* DC - delete chars */
1357       dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
1358       cursor_get (&x, &y);
1359       scroll_screen (x + dev_state->args_[0], y, -1, y, x, y);
1360       break;
1361     case 'S':                           /* SF - Scroll forward */
1362       dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
1363       scroll_screen (0, dev_state->args_[0], -1, -1, 0, 0);
1364       break;
1365     case 'T':                           /* SR - Scroll down */
1366       dev_state->fillin_info (get_output_handle ());
1367       dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
1368       scroll_screen (0, 0, -1, -1, 0, dev_state->info.winTop + dev_state->args_[0]);
1369       break;
1370     case 'X':                           /* ec - erase chars */
1371       dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
1372       cursor_get (&x, &y);
1373       scroll_screen (x + dev_state->args_[0], y, -1, y, x, y);
1374       scroll_screen (x, y, -1, y, x + dev_state->args_[0], y);
1375       break;
1376     case 'Z':                           /* Back tab */
1377       cursor_get (&x, &y);
1378       cursor_set (false, ((8 * (x / 8 + 1)) - 8), y);
1379       break;
1380     case 'b':                           /* Repeat char #1 #2 times */
1381       if (dev_state->insert_mode)
1382         {
1383           cursor_get (&x, &y);
1384           scroll_screen (x, y, -1, y, x + dev_state->args_[1], y);
1385         }
1386       while (dev_state->args_[1]--)
1387         WriteFile (get_output_handle (), &dev_state->args_[0], 1, (DWORD *) &x, 0);
1388       break;
1389     case 'c':                           /* u9 - Terminal enquire string */
1390       strcpy (buf, "\033[?6c");
1391       puts_readahead (buf);
1392       break;
1393     case 'n':
1394       switch (dev_state->args_[0])
1395         {
1396         case 6:                         /* u7 - Cursor position request */
1397           cursor_get (&x, &y);
1398           y -= dev_state->info.winTop;
1399           /* x -= dev_state->info.winLeft;              // not available yet */
1400           __small_sprintf (buf, "\033[%d;%dR", y + 1, x + 1);
1401           puts_readahead (buf);
1402           break;
1403     default:
1404           goto bad_escape;
1405         }
1406       break;
1407     case 'r':                           /* Set Scroll region */
1408       dev_state->scroll_region.Top = dev_state->args_[0] ? dev_state->args_[0] - 1 : 0;
1409       dev_state->scroll_region.Bottom = dev_state->args_[1] ? dev_state->args_[1] - 1 : -1;
1410       cursor_set (true, 0, 0);
1411       break;
1412     case 'g':                           /* TAB set/clear */
1413       break;
1414     default:
1415 bad_escape:
1416       break;
1417     }
1418 }
1419
1420 const unsigned char *
1421 fhandler_console::write_normal (const unsigned char *src,
1422                                 const unsigned char *end)
1423 {
1424   /* Scan forward to see what a char which needs special treatment */
1425   DWORD done;
1426   const unsigned char *found = src;
1427
1428   while (found < end)
1429     {
1430       char ch = base_chars[*found];
1431       if (ch != NOR)
1432         break;
1433       found++;
1434     }
1435
1436   /* Print all the base ones out */
1437   if (found != src)
1438     {
1439       DWORD len = found - src;
1440       do
1441         {
1442           DWORD buf_len;
1443           char buf[CONVERT_LIMIT];
1444           done = buf_len = min (sizeof (buf), len);
1445           if (!dev_state->str_to_con (buf, (const char *) src, buf_len))
1446             {
1447               debug_printf ("conversion error, handle %p",
1448                             get_output_handle ());
1449               __seterrno ();
1450               return 0;
1451             }
1452
1453           if (dev_state->insert_mode)
1454             {
1455               int x, y;
1456               cursor_get (&x, &y);
1457               scroll_screen (x, y, -1, y, x + buf_len, y);
1458             }
1459
1460           if (!WriteFile (get_output_handle (), buf, buf_len, &done, 0))
1461             {
1462               debug_printf ("write failed, handle %p", get_output_handle ());
1463               __seterrno ();
1464               return 0;
1465             }
1466           len -= done;
1467           src += done;
1468         }
1469       while (len > 0);
1470     }
1471
1472   if (src < end)
1473     {
1474       int x, y;
1475       switch (base_chars[*src])
1476         {
1477         case BEL:
1478           MessageBeep (0xFFFFFFFF);
1479           break;
1480         case ESC:
1481           dev_state->state_ = gotesc;
1482           break;
1483         case DWN:
1484           cursor_get (&x, &y);
1485           if (y >= srBottom)
1486             {
1487               if (y >= dev_state->info.winBottom && !dev_state->scroll_region.Top)
1488                 WriteFile (get_output_handle (), "\n", 1, &done, 0);
1489               else
1490                 {
1491                   scroll_screen (0, srTop + 1, -1, srBottom, 0, srTop);
1492                   y--;
1493                 }
1494             }
1495           cursor_set (false, ((tc->ti.c_oflag & ONLCR) ? 0 : x), y + 1);
1496           break;
1497         case BAK:
1498           cursor_rel (-1, 0);
1499           break;
1500         case IGN:
1501           cursor_rel (1, 0);
1502           break;
1503         case CR:
1504           cursor_get (&x, &y);
1505           cursor_set (false, 0, y);
1506           break;
1507         case ERR:
1508           WriteFile (get_output_handle (), src, 1, &done, 0);
1509           break;
1510         case TAB:
1511           cursor_get (&x, &y);
1512           cursor_set (false, 8 * (x / 8 + 1), y);
1513           break;
1514         }
1515       src ++;
1516     }
1517   return src;
1518 }
1519
1520 int
1521 fhandler_console::write (const void *vsrc, size_t len)
1522 {
1523   /* Run and check for ansi sequences */
1524   unsigned const char *src = (unsigned char *) vsrc;
1525   unsigned const char *end = src + len;
1526
1527   debug_printf ("%x, %d", vsrc, len);
1528
1529   while (src < end)
1530     {
1531       debug_printf ("at %d(%c) state is %d", *src, isprint (*src) ? *src : ' ',
1532                     dev_state->state_);
1533       switch (dev_state->state_)
1534         {
1535         case normal:
1536           src = write_normal (src, end);
1537           if (!src) /* write_normal failed */
1538             return -1;
1539           break;
1540         case gotesc:
1541           if (*src == '[')
1542             {
1543               dev_state->state_ = gotsquare;
1544               dev_state->saw_question_mark = false;
1545               for (dev_state->nargs_ = 0; dev_state->nargs_ < MAXARGS; dev_state->nargs_++)
1546                 dev_state->args_[dev_state->nargs_] = 0;
1547               dev_state->nargs_ = 0;
1548             }
1549           else if (*src == ']')
1550             {
1551               dev_state->rarg = 0;
1552               dev_state->my_title_buf[0] = '\0';
1553               dev_state->state_ = gotrsquare;
1554             }
1555           else if (*src == 'M')         /* Reverse Index */
1556             {
1557               dev_state->fillin_info (get_output_handle ());
1558               scroll_screen (0, 0, -1, -1, 0, dev_state->info.winTop + 1);
1559               dev_state->state_ = normal;
1560             }
1561           else if (*src == 'c')         /* Reset Linux terminal */
1562             {
1563               dev_state->set_default_attr ();
1564               clear_screen (0, 0, -1, -1);
1565               cursor_set (true, 0, 0);
1566               dev_state->state_ = normal;
1567             }
1568           else if (*src == '8')         /* Restore cursor position */
1569             {
1570               cursor_set (true, dev_state->savex, dev_state->savey);
1571               dev_state->state_ = normal;
1572             }
1573           else if (*src == '7')         /* Save cursor position */
1574             {
1575               cursor_get (&dev_state->savex, &dev_state->savey);
1576               dev_state->savey -= dev_state->info.winTop;
1577               dev_state->state_ = normal;
1578             }
1579           else if (*src == 'R')
1580               dev_state->state_ = normal;
1581           else
1582             {
1583               dev_state->state_ = normal;
1584             }
1585           src++;
1586           break;
1587         case gotarg1:
1588           if (isdigit (*src))
1589             {
1590               dev_state->args_[dev_state->nargs_] = dev_state->args_[dev_state->nargs_] * 10 + *src - '0';
1591               src++;
1592             }
1593           else if (*src == ';')
1594             {
1595               src++;
1596               dev_state->nargs_++;
1597               if (dev_state->nargs_ >= MAXARGS)
1598                 dev_state->nargs_--;
1599             }
1600           else
1601             {
1602               dev_state->state_ = gotcommand;
1603             }
1604           break;
1605         case gotcommand:
1606           char_command (*src++);
1607           dev_state->state_ = normal;
1608           break;
1609         case gotrsquare:
1610           if (isdigit (*src))
1611             dev_state->rarg = dev_state->rarg * 10 + (*src - '0');
1612           else if (*src == ';' && (dev_state->rarg == 2 || dev_state->rarg == 0))
1613             dev_state->state_ = gettitle;
1614           else
1615             dev_state->state_ = eattitle;
1616           src++;
1617           break;
1618         case eattitle:
1619         case gettitle:
1620           {
1621             int n = strlen (dev_state->my_title_buf);
1622             if (*src < ' ')
1623               {
1624                 if (*src == '\007' && dev_state->state_ == gettitle)
1625                   {
1626                     if (old_title)
1627                       strcpy (old_title, dev_state->my_title_buf);
1628                     set_console_title (dev_state->my_title_buf);
1629                   }
1630                 dev_state->state_ = normal;
1631               }
1632             else if (n < TITLESIZE)
1633               {
1634                 dev_state->my_title_buf[n++] = *src;
1635                 dev_state->my_title_buf[n] = '\0';
1636               }
1637             src++;
1638             break;
1639           }
1640         case gotsquare:
1641           if (*src == ';')
1642             {
1643               dev_state->state_ = gotarg1;
1644               dev_state->nargs_++;
1645               src++;
1646             }
1647           else if (isalpha (*src))
1648             dev_state->state_ = gotcommand;
1649           else if (*src != '@' && !isalpha (*src) && !isdigit (*src))
1650             {
1651               if (*src == '?')
1652                 dev_state->saw_question_mark = true;
1653               /* ignore any extra chars between [ and first arg or command */
1654               src++;
1655             }
1656           else
1657             dev_state->state_ = gotarg1;
1658           break;
1659         }
1660     }
1661
1662   syscall_printf ("%d = write_console (,..%d)", len, len);
1663
1664   return len;
1665 }
1666
1667 static struct {
1668   int vk;
1669   const char *val[4];
1670 } keytable[] NO_COPY = {
1671                /* NORMAL */  /* SHIFT */    /* CTRL */       /* ALT */
1672   {VK_LEFT,     {"\033[D",      "\033[D",       "\033[D",       "\033\033[D"}},
1673   {VK_RIGHT,    {"\033[C",      "\033[C",       "\033[C",       "\033\033[C"}},
1674   {VK_UP,       {"\033[A",      "\033[A",       "\033[A",       "\033\033[A"}},
1675   {VK_DOWN,     {"\033[B",      "\033[B",       "\033[B",       "\033\033[B"}},
1676   {VK_PRIOR,    {"\033[5~",     "\033[5~",      "\033[5~",      "\033\033[5~"}},
1677   {VK_NEXT,     {"\033[6~",     "\033[6~",      "\033[6~",      "\033\033[6~"}},
1678   {VK_HOME,     {"\033[1~",     "\033[1~",      "\033[1~",      "\033\033[1~"}},
1679   {VK_END,      {"\033[4~",     "\033[4~",      "\033[4~",      "\033\033[4~"}},
1680   {VK_INSERT,   {"\033[2~",     "\033[2~",      "\033[2~",      "\033\033[2~"}},
1681   {VK_DELETE,   {"\033[3~",     "\033[3~",      "\033[3~",      "\033\033[3~"}},
1682   {VK_F1,       {"\033[[A",     "\033[23~",     NULL,           NULL}},
1683   {VK_F2,       {"\033[[B",     "\033[24~",     NULL,           NULL}},
1684   {VK_F3,       {"\033[[C",     "\033[25~",     NULL,           NULL}},
1685   {VK_F4,       {"\033[[D",     "\033[26~",     NULL,           NULL}},
1686   {VK_F5,       {"\033[[E",     "\033[28~",     NULL,           NULL}},
1687   {VK_F6,       {"\033[17~",    "\033[29~",     "\036",         NULL}},
1688   {VK_F7,       {"\033[18~",    "\033[31~",     NULL,           NULL}},
1689   {VK_F8,       {"\033[19~",    "\033[32~",     NULL,           NULL}},
1690   {VK_F9,       {"\033[20~",    "\033[33~",     NULL,           NULL}},
1691   {VK_F10,      {"\033[21~",    "\033[34~",     NULL,           NULL}},
1692   {VK_F11,      {"\033[23~",    NULL,           NULL,           NULL}},
1693   {VK_F12,      {"\033[24~",    NULL,           NULL,           NULL}},
1694   {VK_NUMPAD5,  {"\033[G",      NULL,           NULL,           NULL}},
1695   {VK_CLEAR,    {"\033[G",      NULL,           NULL,           NULL}},
1696   {'6',         {NULL,          NULL,           "\036",         NULL}},
1697   {0,           {"",            NULL,           NULL,           NULL}}
1698 };
1699
1700 const char *
1701 get_nonascii_key (INPUT_RECORD& input_rec, char *tmp)
1702 {
1703 #define NORMAL  0
1704 #define SHIFT   1
1705 #define CONTROL 2
1706 #define ALT     3
1707   int modifier_index = NORMAL;
1708
1709   if (input_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)
1710     modifier_index = SHIFT;
1711   else if (input_rec.Event.KeyEvent.dwControlKeyState &
1712                 (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
1713     modifier_index = CONTROL;
1714   else if (input_rec.Event.KeyEvent.dwControlKeyState &
1715                 (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
1716     modifier_index = ALT;
1717
1718   for (int i = 0; keytable[i].vk; i++)
1719     if (input_rec.Event.KeyEvent.wVirtualKeyCode == keytable[i].vk)
1720       return keytable[i].val[modifier_index];
1721
1722   if (input_rec.Event.KeyEvent.uChar.AsciiChar)
1723     {
1724       tmp[0] = input_rec.Event.KeyEvent.uChar.AsciiChar;
1725       tmp[1] = '\0';
1726       return tmp;
1727     }
1728   return NULL;
1729 }
1730
1731 void
1732 fhandler_console::init (HANDLE f, DWORD a, mode_t bin)
1733 {
1734   // this->fhandler_termios::init (f, mode, bin);
1735   /* Ensure both input and output console handles are open */
1736   int flags = 0;
1737
1738   a &= GENERIC_READ | GENERIC_WRITE;
1739   if (a == GENERIC_READ)
1740     flags = O_RDONLY;
1741   if (a == GENERIC_WRITE)
1742     flags = O_WRONLY;
1743   if (a == (GENERIC_READ | GENERIC_WRITE))
1744     flags = O_RDWR;
1745   open (flags | O_BINARY);
1746   if (f != INVALID_HANDLE_VALUE)
1747     CloseHandle (f);    /* Reopened by open */
1748
1749   tcsetattr (0, &tc->ti);
1750 }
1751
1752 int
1753 fhandler_console::igncr_enabled ()
1754 {
1755   return tc->ti.c_iflag & IGNCR;
1756 }
1757
1758 void
1759 fhandler_console::set_close_on_exec (bool val)
1760 {
1761   fhandler_base::set_close_on_exec (val);
1762   set_no_inheritance (output_handle, val);
1763 }
1764
1765 void
1766 fhandler_console::fixup_after_fork (HANDLE)
1767 {
1768   HANDLE h = get_handle ();
1769   HANDLE oh = get_output_handle ();
1770
1771   /* Windows does not allow duplication of console handles between processes
1772      so open the console explicitly. */
1773
1774   cygheap->open_fhs--;          /* The downside of storing this in cygheap. */
1775   if (!open (O_NOCTTY | get_flags (), 0))
1776     system_printf ("error opening console after fork, %E");
1777
1778   if (!close_on_exec ())
1779     {
1780       CloseHandle (h);
1781       CloseHandle (oh);
1782     }
1783 }
1784
1785 void __stdcall
1786 set_console_title (char *title)
1787 {
1788   int rc;
1789   char buf[257];
1790   strncpy (buf, title, sizeof (buf) - 1);
1791   buf[sizeof (buf) - 1] = '\0';
1792   if ((rc = WaitForSingleObject (tty_mutex, 15000)) != WAIT_OBJECT_0)
1793     sigproc_printf ("wait for title mutex failed rc %d, %E", rc);
1794   SetConsoleTitle (buf);
1795   ReleaseMutex (tty_mutex);
1796   debug_printf ("title '%s'", buf);
1797 }
1798
1799 void
1800 fhandler_console::fixup_after_exec ()
1801 {
1802   HANDLE h = get_handle ();
1803   HANDLE oh = get_output_handle ();
1804
1805   cygheap->open_fhs--;          /* The downside of storing this in cygheap. */
1806   if (!open (O_NOCTTY | get_flags (), 0))
1807     {
1808       bool sawerr = false;
1809       if (!get_io_handle ())
1810         {
1811           system_printf ("error opening input console handle after exec, errno %d, %E", get_errno ());
1812           sawerr = true;
1813         }
1814       if (!get_output_handle ())
1815         {
1816           system_printf ("error opening output console handle after exec, errno %d, %E", get_errno ());
1817           sawerr = true;
1818         }
1819
1820       if (!sawerr)
1821         system_printf ("error opening console after exec, errno %d, %E", get_errno ());
1822     }
1823
1824   CloseHandle (h);
1825   CloseHandle (oh);
1826 }