]> sourceware.org Git - newlib-cygwin.git/blob - winsup/cygwin/fhandler_console.cc
fd5f972d8936a7d0fe9eaefc90bee158a857fc58
[newlib-cygwin.git] / winsup / cygwin / fhandler_console.cc
1 /* fhandler_console.cc
2
3 This file is part of Cygwin.
4
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7 details. */
8
9 #include "winsup.h"
10 #include "miscfuncs.h"
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <wchar.h>
14 #include <ctype.h>
15 #include <sys/param.h>
16 #include <sys/cygwin.h>
17 #include <cygwin/kd.h>
18 #include <unistd.h>
19 #include "cygerrno.h"
20 #include "security.h"
21 #include "path.h"
22 #include "fhandler.h"
23 #include "dtable.h"
24 #include "cygheap.h"
25 #include "sigproc.h"
26 #include "pinfo.h"
27 #include "shared_info.h"
28 #include "cygtls.h"
29 #include "tls_pbuf.h"
30 #include "registry.h"
31 #include <asm/socket.h>
32 #include "sync.h"
33 #include "child_info.h"
34 #include "cygwait.h"
35 #include "winf.h"
36
37 /* Don't make this bigger than NT_MAX_PATH as long as the temporary buffer
38 is allocated using tmp_pathbuf!!! */
39 #define CONVERT_LIMIT NT_MAX_PATH
40
41 #define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
42 #define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
43
44 #define con (shared_console_info->con)
45 #define srTop (con.b.srWindow.Top + con.scroll_region.Top)
46 #define srBottom ((con.scroll_region.Bottom < 0) ? \
47 con.b.srWindow.Bottom : \
48 con.b.srWindow.Top + con.scroll_region.Bottom)
49 #define con_is_legacy (shared_console_info && con.is_legacy)
50
51 #define CONS_THREAD_SYNC "cygcons.thread_sync"
52 static bool NO_COPY master_thread_started = false;
53
54 const unsigned fhandler_console::MAX_WRITE_CHARS = 16384;
55
56 fhandler_console::console_state NO_COPY *fhandler_console::shared_console_info;
57
58 bool NO_COPY fhandler_console::invisible_console;
59
60 /* con_ra is shared in the same process.
61 Only one console can exist in a process, therefore, static is suitable. */
62 static struct fhandler_base::rabuf_t con_ra;
63
64 /* Write pending buffer for ESC sequence handling
65 in xterm compatible mode */
66 static wchar_t last_char;
67
68 /* simple helper class to accumulate output in a buffer
69 and send that to the console on request: */
70 static class write_pending_buffer
71 {
72 private:
73 static const size_t WPBUF_LEN = 256u;
74 char buf[WPBUF_LEN];
75 size_t ixput;
76 public:
77 inline void put (char x)
78 {
79 if (ixput < WPBUF_LEN)
80 buf[ixput++] = x;
81 }
82 inline void empty () { ixput = 0u; }
83 inline void send (HANDLE &handle)
84 {
85 wchar_t bufw[WPBUF_LEN];
86 DWORD len = sys_mbstowcs (bufw, WPBUF_LEN, buf, ixput);
87 acquire_attach_mutex (mutex_timeout);
88 WriteConsoleW (handle, bufw, len, NULL, 0);
89 release_attach_mutex ();
90 }
91 } wpbuf;
92
93 static void
94 beep ()
95 {
96 const WCHAR ding[] = L"\\media\\ding.wav";
97 reg_key r (HKEY_CURRENT_USER, KEY_ALL_ACCESS, L"AppEvents", L"Schemes",
98 L"Apps", L".Default", L".Default", L".Current", NULL);
99 if (r.created ())
100 {
101 tmp_pathbuf tp;
102
103 PWCHAR ding_path = tp.w_get ();
104 wcpcpy (wcpcpy (ding_path, windows_directory), ding);
105 r.set_string (L"", ding_path);
106 }
107 MessageBeep (MB_OK);
108 }
109
110 fhandler_console::console_state *
111 fhandler_console::open_shared_console (HWND hw, HANDLE& h, bool& create)
112 {
113 wchar_t namebuf[(sizeof "XXXXXXXXXXXXXXXXXX-consNNNNNNNNNN")];
114 __small_swprintf (namebuf, L"%S-cons%p", &cygheap->installation_key, hw);
115
116 shared_locations m = create ? SH_SHARED_CONSOLE : SH_JUSTOPEN;
117 console_state *res = (console_state *)
118 open_shared (namebuf, 0, h, sizeof (*shared_console_info), &m);
119 create = m != SH_JUSTOPEN;
120 return res;
121 }
122
123 class console_unit
124 {
125 int n;
126 unsigned long bitmask;
127 HWND me;
128
129 public:
130 operator int () const {return n;}
131 console_unit (HWND);
132 friend BOOL CALLBACK enum_windows (HWND, LPARAM);
133 };
134
135 BOOL CALLBACK
136 enum_windows (HWND hw, LPARAM lp)
137 {
138 console_unit *this1 = (console_unit *) lp;
139 if (hw == this1->me)
140 return TRUE;
141 HANDLE h = NULL;
142 fhandler_console::console_state *cs;
143 if ((cs = fhandler_console::open_shared_console (hw, h)))
144 {
145 this1->bitmask ^= 1 << cs->tty_min_state.getntty ();
146 UnmapViewOfFile ((void *) cs);
147 CloseHandle (h);
148 }
149 return TRUE;
150 }
151
152 console_unit::console_unit (HWND me0):
153 bitmask (0xffffffff), me (me0)
154 {
155 EnumWindows (enum_windows, (LPARAM) this);
156 n = (_minor_t) ffs (bitmask) - 1;
157 if (n < 0)
158 api_fatal ("console device allocation failure - too many consoles in use, max consoles is 32");
159 }
160
161 static DWORD WINAPI
162 cons_master_thread (VOID *arg)
163 {
164 fhandler_console *fh = (fhandler_console *) arg;
165 tty *ttyp = (tty *) fh->tc ();
166 fhandler_console::handle_set_t handle_set;
167 fh->get_duplicated_handle_set (&handle_set);
168 HANDLE thread_sync_event;
169 DuplicateHandle (GetCurrentProcess (), fh->thread_sync_event,
170 GetCurrentProcess (), &thread_sync_event,
171 0, FALSE, DUPLICATE_SAME_ACCESS);
172 SetEvent (thread_sync_event);
173 master_thread_started = true;
174 /* Do not touch class members after here because the class instance
175 may have been destroyed. */
176 fhandler_console::cons_master_thread (&handle_set, ttyp);
177 fhandler_console::close_handle_set (&handle_set);
178 SetEvent (thread_sync_event);
179 CloseHandle (thread_sync_event);
180 return 0;
181 }
182
183 /* Compare two INPUT_RECORD sequences */
184 static inline bool
185 inrec_eq (const INPUT_RECORD *a, const INPUT_RECORD *b, DWORD n)
186 {
187 for (DWORD i = 0; i < n; i++)
188 {
189 if (a[i].EventType == KEY_EVENT && b[i].EventType == KEY_EVENT)
190 { /* wVirtualKeyCode, wVirtualScanCode and dwControlKeyState
191 of the readback key event may be different from that of
192 written event. Therefore they are ignored. */
193 const KEY_EVENT_RECORD *ak = &a[i].Event.KeyEvent;
194 const KEY_EVENT_RECORD *bk = &b[i].Event.KeyEvent;
195 if (ak->bKeyDown != bk->bKeyDown
196 || ak->uChar.UnicodeChar != bk->uChar.UnicodeChar
197 || ak->wRepeatCount != bk->wRepeatCount)
198 return false;
199 }
200 else if (memcmp (a + i, b + i, sizeof (INPUT_RECORD)) != 0)
201 return false;
202 }
203 return true;
204 }
205
206 /* This thread processes signals derived from input messages.
207 Without this thread, those signals can be handled only when
208 the process calls read() or select(). This thread reads input
209 records, processes signals and removes corresponding record.
210 The other input records are kept back for read() or select(). */
211 void
212 fhandler_console::cons_master_thread (handle_set_t *p, tty *ttyp)
213 {
214 const int additional_space = 128; /* Possible max number of incoming events
215 during the process. Additional space
216 should be left for writeback fix. */
217 const int inrec_size = INREC_SIZE + additional_space;
218 struct m
219 {
220 inline static size_t bytes (size_t n)
221 {
222 return sizeof (INPUT_RECORD) * n;
223 }
224 };
225 termios &ti = ttyp->ti;
226 while (con.owner == myself->pid)
227 {
228 DWORD total_read, n, i;
229 INPUT_RECORD input_rec[inrec_size];
230
231 if (con.disable_master_thread)
232 {
233 cygwait (40);
234 continue;
235 }
236
237 WaitForSingleObject (p->input_mutex, mutex_timeout);
238 total_read = 0;
239 bool nowait = false;
240 switch (cygwait (p->input_handle, (DWORD) 0))
241 {
242 case WAIT_OBJECT_0:
243 acquire_attach_mutex (mutex_timeout);
244 ReadConsoleInputW (p->input_handle,
245 input_rec, INREC_SIZE, &total_read);
246 if (total_read == INREC_SIZE /* Working space full */
247 && cygwait (p->input_handle, (DWORD) 0) == WAIT_OBJECT_0)
248 {
249 const int incr = min (con.num_processed, additional_space);
250 ReadConsoleInputW (p->input_handle,
251 input_rec + total_read, incr, &n);
252 /* Discard oldest n events. */
253 memmove (input_rec, input_rec + n, m::bytes (total_read));
254 con.num_processed -= n;
255 nowait = true;
256 }
257 release_attach_mutex ();
258 break;
259 case WAIT_TIMEOUT:
260 con.num_processed = 0;
261 case WAIT_SIGNALED:
262 case WAIT_CANCELED:
263 break;
264 default: /* Error */
265 ReleaseMutex (p->input_mutex);
266 return;
267 }
268 /* If ENABLE_VIRTUAL_TERMINAL_INPUT is not set, changing
269 window height does not generate WINDOW_BUFFER_SIZE_EVENT.
270 Therefore, check windows size every time here. */
271 if (!wincap.has_con_24bit_colors () || con_is_legacy)
272 {
273 SHORT y = con.dwWinSize.Y;
274 SHORT x = con.dwWinSize.X;
275 con.fillin (p->output_handle);
276 if (y != con.dwWinSize.Y || x != con.dwWinSize.X)
277 {
278 con.scroll_region.Top = 0;
279 con.scroll_region.Bottom = -1;
280 ttyp->kill_pgrp (SIGWINCH);
281 }
282 }
283 for (i = con.num_processed; i < total_read; i++)
284 {
285 wchar_t wc;
286 char c;
287 bool processed = false;
288 switch (input_rec[i].EventType)
289 {
290 case KEY_EVENT:
291 if (!input_rec[i].Event.KeyEvent.bKeyDown)
292 continue;
293 wc = input_rec[i].Event.KeyEvent.uChar.UnicodeChar;
294 if (!wc || (wint_t) wc >= 0x80)
295 continue;
296 c = (char) wc;
297 switch (process_sigs (c, ttyp, NULL))
298 {
299 case signalled:
300 case not_signalled_but_done:
301 case done_with_debugger:
302 processed = true;
303 ttyp->output_stopped = false;
304 if (ti.c_lflag & NOFLSH)
305 goto remove_record;
306 con.num_processed = 0;
307 goto skip_writeback;
308 default: /* not signalled */
309 break;
310 }
311 processed = process_stop_start (c, ttyp);
312 break;
313 case WINDOW_BUFFER_SIZE_EVENT:
314 SHORT y = con.dwWinSize.Y;
315 SHORT x = con.dwWinSize.X;
316 con.fillin (p->output_handle);
317 if (y != con.dwWinSize.Y || x != con.dwWinSize.X)
318 {
319 con.scroll_region.Top = 0;
320 con.scroll_region.Bottom = -1;
321 if (wincap.has_con_24bit_colors () && !con_is_legacy
322 && wincap.has_con_broken_tabs ())
323 fix_tab_position (p->output_handle);
324 ttyp->kill_pgrp (SIGWINCH);
325 }
326 processed = true;
327 break;
328 }
329 remove_record:
330 if (processed)
331 { /* Remove corresponding record. */
332 if (total_read > i + 1)
333 memmove (input_rec + i, input_rec + i + 1,
334 m::bytes (total_read - i - 1));
335 total_read--;
336 i--;
337 }
338 }
339 con.num_processed = total_read;
340 if (total_read)
341 {
342 do
343 {
344 INPUT_RECORD tmp[inrec_size];
345 /* Writeback input records other than interrupt. */
346 acquire_attach_mutex (mutex_timeout);
347 WriteConsoleInputW (p->input_handle, input_rec, total_read, &n);
348 /* Check if writeback was successfull. */
349 PeekConsoleInputW (p->input_handle, tmp, inrec_size, &n);
350 release_attach_mutex ();
351 if (n < total_read)
352 break; /* Someone has read input without acquiring
353 input_mutex. ConEmu cygwin-connector? */
354 if (inrec_eq (input_rec, tmp, total_read))
355 break; /* OK */
356 /* Try to fix */
357 acquire_attach_mutex (mutex_timeout);
358 ReadConsoleInputW (p->input_handle, tmp, inrec_size, &n);
359 release_attach_mutex ();
360 for (DWORD i = 0, j = 0; j < n; j++)
361 if (i == total_read || !inrec_eq (input_rec + i, tmp + j, 1))
362 {
363 if (total_read + j - i >= n)
364 { /* Something is wrong. Giving up. */
365 acquire_attach_mutex (mutex_timeout);
366 WriteConsoleInputW (p->input_handle, tmp, n, &n);
367 release_attach_mutex ();
368 goto skip_writeback;
369 }
370 input_rec[total_read + j - i] = tmp[j];
371 }
372 else
373 i++;
374 total_read = n;
375 }
376 while (true);
377 }
378 skip_writeback:
379 ReleaseMutex (p->input_mutex);
380 if (!nowait)
381 cygwait (40);
382 }
383 }
384
385 bool
386 fhandler_console::set_unit ()
387 {
388 bool created;
389 fh_devices devset;
390 lock_ttys here;
391 HWND me;
392 fh_devices this_unit = dev ();
393 bool generic_console = this_unit == FH_CONIN || this_unit == FH_CONOUT;
394 if (shared_console_info)
395 {
396 fh_devices shared_unit =
397 (fh_devices) shared_console_info->tty_min_state.getntty ();
398 devset = (shared_unit == this_unit || this_unit == FH_CONSOLE
399 || generic_console
400 || this_unit == FH_TTY) ?
401 shared_unit : FH_ERROR;
402 created = false;
403 }
404 else if ((!generic_console &&
405 (myself->ctty != -1 && !iscons_dev (myself->ctty)))
406 || !(me = GetConsoleWindow ()))
407 devset = FH_ERROR;
408 else
409 {
410 created = true;
411 shared_console_info =
412 open_shared_console (me, cygheap->console_h, created);
413 ProtectHandleINH (cygheap->console_h);
414 if (created)
415 shared_console_info->
416 tty_min_state.setntty (DEV_CONS_MAJOR, console_unit (me));
417 devset = (fh_devices) shared_console_info->tty_min_state.getntty ();
418 if (created)
419 con.owner = myself->pid;
420 }
421 if (!created && shared_console_info)
422 {
423 while (con.owner > MAX_PID)
424 Sleep (1);
425 pinfo p (con.owner);
426 if (!p)
427 con.owner = myself->pid;
428 }
429
430 dev ().parse (devset);
431 if (devset != FH_ERROR)
432 pc.file_attributes (FILE_ATTRIBUTE_NORMAL);
433 else
434 {
435 set_handle (NULL);
436 set_output_handle (NULL);
437 created = false;
438 }
439 return created;
440 }
441
442 /* Allocate and initialize the shared record for the current console. */
443 void
444 fhandler_console::setup ()
445 {
446 if (set_unit ())
447 {
448 con.scroll_region.Bottom = -1;
449 con.dwLastCursorPosition.X = -1;
450 con.dwLastCursorPosition.Y = -1;
451 con.dwLastMousePosition.X = -1;
452 con.dwLastMousePosition.Y = -1;
453 con.savex = con.savey = -1;
454 con.screen_alternated = false;
455 con.dwLastButtonState = 0; /* none pressed */
456 con.last_button_code = 3; /* released */
457 con.underline_color = FOREGROUND_GREEN | FOREGROUND_BLUE;
458 con.dim_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
459 con.meta_mask = LEFT_ALT_PRESSED;
460 /* Set the mask that determines if an input keystroke is modified by
461 META. We set this based on the keyboard layout language loaded
462 for the current thread. The left <ALT> key always generates
463 META, but the right <ALT> key only generates META if we are using
464 an English keyboard because many "international" keyboards
465 replace common shell symbols ('[', '{', etc.) with accented
466 language-specific characters (umlaut, accent grave, etc.). On
467 these keyboards right <ALT> (called AltGr) is used to produce the
468 shell symbols and should not be interpreted as META. */
469 if (PRIMARYLANGID (LOWORD (GetKeyboardLayout (0))) == LANG_ENGLISH)
470 con.meta_mask |= RIGHT_ALT_PRESSED;
471 con.set_default_attr ();
472 con.backspace_keycode = CERASE;
473 con.cons_rapoi = NULL;
474 shared_console_info->tty_min_state.is_console = true;
475 con.cursor_key_app_mode = false;
476 con.disable_master_thread = true;
477 con.num_processed = 0;
478 }
479 }
480
481 char *&
482 fhandler_console::rabuf ()
483 {
484 return con_ra.rabuf;
485 }
486
487 size_t &
488 fhandler_console::ralen ()
489 {
490 return con_ra.ralen;
491 }
492
493 size_t &
494 fhandler_console::raixget ()
495 {
496 return con_ra.raixget;
497 }
498
499 size_t &
500 fhandler_console::raixput ()
501 {
502 return con_ra.raixput;
503 }
504
505 size_t &
506 fhandler_console::rabuflen ()
507 {
508 return con_ra.rabuflen;
509 }
510
511 /* The function set_{in,out}put_mode() should be static so that they
512 can be called even after the fhandler_console instance is deleted. */
513 void
514 fhandler_console::set_input_mode (tty::cons_mode m, const termios *t,
515 const handle_set_t *p)
516 {
517 DWORD oflags;
518 WaitForSingleObject (p->input_mutex, mutex_timeout);
519 acquire_attach_mutex (mutex_timeout);
520 GetConsoleMode (p->input_handle, &oflags);
521 DWORD flags = oflags
522 & (ENABLE_EXTENDED_FLAGS | ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE);
523 switch (m)
524 {
525 case tty::restore:
526 flags |= ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
527 break;
528 case tty::cygwin:
529 flags |= ENABLE_WINDOW_INPUT;
530 if (wincap.has_con_24bit_colors () && !con_is_legacy)
531 flags |= ENABLE_VIRTUAL_TERMINAL_INPUT;
532 else
533 flags |= ENABLE_MOUSE_INPUT;
534 break;
535 case tty::native:
536 if (t->c_lflag & ECHO)
537 flags |= ENABLE_ECHO_INPUT;
538 if (t->c_lflag & ICANON)
539 flags |= ENABLE_LINE_INPUT;
540 if (flags & ENABLE_ECHO_INPUT && !(flags & ENABLE_LINE_INPUT))
541 /* This is illegal, so turn off the echo here, and fake it
542 when we read the characters */
543 flags &= ~ENABLE_ECHO_INPUT;
544 if (t->c_lflag & ISIG)
545 flags |= ENABLE_PROCESSED_INPUT;
546 break;
547 }
548 SetConsoleMode (p->input_handle, flags);
549 if (!(oflags & ENABLE_VIRTUAL_TERMINAL_INPUT)
550 && (flags & ENABLE_VIRTUAL_TERMINAL_INPUT)
551 && con.cursor_key_app_mode)
552 { /* Restore DECCKM */
553 set_output_mode (tty::cygwin, t, p);
554 WriteConsoleW (p->output_handle, L"\033[?1h", 5, NULL, 0);
555 }
556 release_attach_mutex ();
557 ReleaseMutex (p->input_mutex);
558 }
559
560 void
561 fhandler_console::set_output_mode (tty::cons_mode m, const termios *t,
562 const handle_set_t *p)
563 {
564 DWORD flags = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
565 WaitForSingleObject (p->output_mutex, mutex_timeout);
566 switch (m)
567 {
568 case tty::restore:
569 break;
570 case tty::cygwin:
571 if (wincap.has_con_24bit_colors () && !con_is_legacy)
572 flags |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
573 fallthrough;
574 case tty::native:
575 if (wincap.has_con_24bit_colors () && !con_is_legacy
576 && (!(t->c_oflag & OPOST) || !(t->c_oflag & ONLCR)))
577 flags |= DISABLE_NEWLINE_AUTO_RETURN;
578 break;
579 }
580 acquire_attach_mutex (mutex_timeout);
581 SetConsoleMode (p->output_handle, flags);
582 release_attach_mutex ();
583 ReleaseMutex (p->output_mutex);
584 }
585
586 void
587 fhandler_console::setup_for_non_cygwin_app ()
588 {
589 /* Setting-up console mode for non-cygwin app. */
590 /* If conmode is set to tty::native for non-cygwin apps
591 in background, tty settings of the shell is reflected
592 to the console mode of the app. So, use tty::restore
593 for background process instead. */
594 tty::cons_mode conmode =
595 (get_ttyp ()->getpgid ()== myself->pgid) ? tty::native : tty::restore;
596 set_input_mode (conmode, &tc ()->ti, get_handle_set ());
597 set_output_mode (conmode, &tc ()->ti, get_handle_set ());
598 con.disable_master_thread = true;
599 }
600
601 void
602 fhandler_console::cleanup_for_non_cygwin_app (handle_set_t *p)
603 {
604 termios dummy = {0, };
605 termios *ti =
606 shared_console_info ? &(shared_console_info->tty_min_state.ti) : &dummy;
607 /* Cleaning-up console mode for non-cygwin app. */
608 /* conmode can be tty::restore when non-cygwin app is
609 exec'ed from login shell. */
610 tty::cons_mode conmode =
611 (con.owner == myself->pid) ? tty::restore : tty::cygwin;
612 set_output_mode (conmode, ti, p);
613 set_input_mode (conmode, ti, p);
614 con.disable_master_thread = (con.owner == myself->pid);
615 }
616
617 /* Return the tty structure associated with a given tty number. If the
618 tty number is < 0, just return a dummy record. */
619 tty_min *
620 tty_list::get_cttyp ()
621 {
622 dev_t n = myself->ctty;
623 if (iscons_dev (n))
624 return fhandler_console::shared_console_info ?
625 &fhandler_console::shared_console_info->tty_min_state : NULL;
626 else if (istty_slave_dev (n))
627 return &ttys[device::minor (n)];
628 else
629 return NULL;
630 }
631
632 void
633 fhandler_console::setup_io_mutex (void)
634 {
635 char buf[MAX_PATH];
636 DWORD res;
637
638 res = WAIT_FAILED;
639 if (!input_mutex || WAIT_FAILED == (res = acquire_input_mutex (0)))
640 {
641 shared_name (buf, "cygcons.input.mutex", get_minor ());
642 input_mutex = OpenMutex (MAXIMUM_ALLOWED, TRUE, buf);
643 if (!input_mutex)
644 input_mutex = CreateMutex (&sec_none, FALSE, buf);
645 if (!input_mutex)
646 {
647 __seterrno ();
648 return;
649 }
650 }
651 if (res == WAIT_OBJECT_0)
652 release_input_mutex ();
653
654 res = WAIT_FAILED;
655 if (!output_mutex || WAIT_FAILED == (res = acquire_output_mutex (0)))
656 {
657 shared_name (buf, "cygcons.output.mutex", get_minor ());
658 output_mutex = OpenMutex (MAXIMUM_ALLOWED, TRUE, buf);
659 if (!output_mutex)
660 output_mutex = CreateMutex (&sec_none, FALSE, buf);
661 if (!output_mutex)
662 {
663 __seterrno ();
664 return;
665 }
666 }
667 if (res == WAIT_OBJECT_0)
668 release_output_mutex ();
669 }
670
671 inline DWORD
672 dev_console::con_to_str (char *d, int dlen, WCHAR w)
673 {
674 return sys_wcstombs (d, dlen, &w, 1);
675 }
676
677 inline UINT
678 dev_console::get_console_cp ()
679 {
680 /* The alternate charset is always 437, just as in the Linux console. */
681 return alternate_charset_active ? 437 : 0;
682 }
683
684 inline DWORD
685 dev_console::str_to_con (mbtowc_p f_mbtowc, PWCHAR d, const char *s, DWORD sz)
686 {
687 return _sys_mbstowcs (f_mbtowc, d, CONVERT_LIMIT, s, sz);
688 }
689
690 bool
691 fhandler_console::set_raw_win32_keyboard_mode (bool new_mode)
692 {
693 bool old_mode = con.raw_win32_keyboard_mode;
694 con.raw_win32_keyboard_mode = new_mode;
695 syscall_printf ("raw keyboard mode %sabled",
696 con.raw_win32_keyboard_mode ? "en" : "dis");
697 return old_mode;
698 };
699
700 void
701 fhandler_console::set_cursor_maybe ()
702 {
703 con.fillin (get_output_handle ());
704 /* Nothing to do for xterm compatible mode. */
705 if (wincap.has_con_24bit_colors () && !con_is_legacy)
706 return;
707 if (con.dwLastCursorPosition.X != con.b.dwCursorPosition.X ||
708 con.dwLastCursorPosition.Y != con.b.dwCursorPosition.Y)
709 {
710 acquire_attach_mutex (mutex_timeout);
711 SetConsoleCursorPosition (get_output_handle (), con.b.dwCursorPosition);
712 release_attach_mutex ();
713 con.dwLastCursorPosition = con.b.dwCursorPosition;
714 }
715 }
716
717 /* Workaround for a bug of windows xterm compatible mode. */
718 /* The horizontal tab positions are broken after resize. */
719 void
720 fhandler_console::fix_tab_position (HANDLE h)
721 {
722 /* Re-setting ENABLE_VIRTUAL_TERMINAL_PROCESSING
723 fixes the tab position. */
724 DWORD mode;
725 acquire_attach_mutex (mutex_timeout);
726 GetConsoleMode (h, &mode);
727 SetConsoleMode (h, mode & ~ENABLE_VIRTUAL_TERMINAL_PROCESSING);
728 SetConsoleMode (h, mode);
729 release_attach_mutex ();
730 }
731
732 bool
733 fhandler_console::send_winch_maybe ()
734 {
735 SHORT y = con.dwWinSize.Y;
736 SHORT x = con.dwWinSize.X;
737 con.fillin (get_output_handle ());
738
739 if (y != con.dwWinSize.Y || x != con.dwWinSize.X)
740 {
741 con.scroll_region.Top = 0;
742 con.scroll_region.Bottom = -1;
743 if (wincap.has_con_24bit_colors () && !con_is_legacy
744 && wincap.has_con_broken_tabs ())
745 fix_tab_position (get_output_handle ());
746 get_ttyp ()->kill_pgrp (SIGWINCH);
747 return true;
748 }
749 return false;
750 }
751
752 /* Check whether a mouse event is to be reported as an escape sequence */
753 bool
754 fhandler_console::mouse_aware (MOUSE_EVENT_RECORD& mouse_event)
755 {
756 if (!con.use_mouse)
757 return 0;
758
759 /* Adjust mouse position by window scroll buffer offset
760 and remember adjusted position in state for use by read() */
761 CONSOLE_SCREEN_BUFFER_INFO now;
762 acquire_attach_mutex (mutex_timeout);
763 BOOL r = GetConsoleScreenBufferInfo (get_output_handle (), &now);
764 release_attach_mutex ();
765 if (!r)
766 /* Cannot adjust position by window scroll buffer offset */
767 return 0;
768
769 con.dwMousePosition.X = mouse_event.dwMousePosition.X - now.srWindow.Left;
770 con.dwMousePosition.Y = mouse_event.dwMousePosition.Y - now.srWindow.Top;
771
772 return ((mouse_event.dwEventFlags == 0
773 || mouse_event.dwEventFlags == DOUBLE_CLICK)
774 && mouse_event.dwButtonState != con.dwLastButtonState)
775 || mouse_event.dwEventFlags == MOUSE_WHEELED
776 || (mouse_event.dwEventFlags == MOUSE_MOVED
777 && (con.dwMousePosition.X != con.dwLastMousePosition.X
778 || con.dwMousePosition.Y != con.dwLastMousePosition.Y)
779 && ((con.use_mouse >= 2 && mouse_event.dwButtonState)
780 || con.use_mouse >= 3));
781 }
782
783
784 bg_check_types
785 fhandler_console::bg_check (int sig, bool dontsignal)
786 {
787 /* Setting-up console mode for cygwin app. This is necessary if the
788 cygwin app and other non-cygwin apps are started simultaneously
789 in the same process group. */
790 if (sig == SIGTTIN)
791 {
792 set_input_mode (tty::cygwin, &tc ()->ti, get_handle_set ());
793 con.disable_master_thread = false;
794 }
795 if (sig == SIGTTOU)
796 set_output_mode (tty::cygwin, &tc ()->ti, get_handle_set ());
797
798 return fhandler_termios::bg_check (sig, dontsignal);
799 }
800
801 void __reg3
802 fhandler_console::read (void *pv, size_t& buflen)
803 {
804 termios_printf ("read(%p,%d)", pv, buflen);
805
806 push_process_state process_state (PID_TTYIN);
807
808 int copied_chars = 0;
809
810 DWORD timeout = is_nonblocking () ? 0 : INFINITE;
811
812 while (!input_ready && !get_cons_readahead_valid ())
813 {
814 int bgres;
815 if ((bgres = bg_check (SIGTTIN)) <= bg_eof)
816 {
817 buflen = bgres;
818 return;
819 }
820
821 set_cursor_maybe (); /* to make cursor appear on the screen immediately */
822 wait_retry:
823 switch (cygwait (get_handle (), timeout))
824 {
825 case WAIT_OBJECT_0:
826 break;
827 case WAIT_SIGNALED:
828 goto sig_exit;
829 case WAIT_CANCELED:
830 process_state.pop ();
831 pthread::static_cancel_self ();
832 /*NOTREACHED*/
833 case WAIT_TIMEOUT:
834 set_sig_errno (EAGAIN);
835 buflen = (size_t) -1;
836 return;
837 default:
838 if (GetLastError () == ERROR_INVALID_HANDLE)
839 { /* Confirm the handle is still valid */
840 DWORD mode;
841 acquire_attach_mutex (mutex_timeout);
842 BOOL res = GetConsoleMode (get_handle (), &mode);
843 release_attach_mutex ();
844 if (res)
845 goto wait_retry;
846 }
847 goto err;
848 }
849
850 #define buf ((char *) pv)
851
852 int ret;
853 acquire_input_mutex (mutex_timeout);
854 ret = process_input_message ();
855 switch (ret)
856 {
857 case input_error:
858 release_input_mutex ();
859 goto err;
860 case input_processing:
861 release_input_mutex ();
862 continue;
863 case input_ok: /* input ready */
864 break;
865 case input_signalled: /* signalled */
866 case input_winch:
867 release_input_mutex ();
868 if (global_sigs[get_ttyp ()->last_sig].sa_flags & SA_RESTART)
869 continue;
870 goto sig_exit;
871 default:
872 /* Should not come here */
873 release_input_mutex ();
874 goto err;
875 }
876 }
877
878 /* Check console read-ahead buffer filled from terminal requests */
879 while (con.cons_rapoi && *con.cons_rapoi && buflen)
880 {
881 buf[copied_chars++] = *con.cons_rapoi++;
882 buflen --;
883 }
884
885 copied_chars +=
886 get_readahead_into_buffer (buf + copied_chars, buflen);
887
888 if (!con_ra.ralen)
889 input_ready = false;
890 release_input_mutex ();
891
892 #undef buf
893
894 buflen = copied_chars;
895 return;
896
897 err:
898 __seterrno ();
899 buflen = (size_t) -1;
900 return;
901
902 sig_exit:
903 set_sig_errno (EINTR);
904 buflen = (size_t) -1;
905 }
906
907 fhandler_console::input_states
908 fhandler_console::process_input_message (void)
909 {
910 char tmp[60];
911
912 if (!shared_console_info)
913 return input_error;
914
915 termios *ti = &(get_ttyp ()->ti);
916
917 fhandler_console::input_states stat = input_processing;
918 DWORD total_read, i;
919 INPUT_RECORD input_rec[INREC_SIZE];
920
921 acquire_attach_mutex (mutex_timeout);
922 BOOL r =
923 PeekConsoleInputW (get_handle (), input_rec, INREC_SIZE, &total_read);
924 release_attach_mutex ();
925 if (!r)
926 {
927 termios_printf ("PeekConsoleInput failed, %E");
928 return input_error;
929 }
930
931 for (i = 0; i < total_read; i ++)
932 {
933 DWORD nread = 1;
934 const char *toadd = NULL;
935
936 const WCHAR &unicode_char =
937 input_rec[i].Event.KeyEvent.uChar.UnicodeChar;
938 const DWORD &ctrl_key_state =
939 input_rec[i].Event.KeyEvent.dwControlKeyState;
940
941 /* check the event that occurred */
942 switch (input_rec[i].EventType)
943 {
944 case KEY_EVENT:
945
946 con.nModifiers = 0;
947
948 #ifdef DEBUGGING
949 /* allow manual switching to/from raw mode via ctrl-alt-scrolllock */
950 if (input_rec[i].Event.KeyEvent.bKeyDown
951 && input_rec[i].Event.KeyEvent.wVirtualKeyCode == VK_SCROLL
952 && (ctrl_key_state & (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED))
953 == (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED))
954 {
955 set_raw_win32_keyboard_mode (!con.raw_win32_keyboard_mode);
956 continue;
957 }
958 #endif
959
960 if (con.raw_win32_keyboard_mode)
961 {
962 __small_sprintf (tmp, "\033{%u;%u;%u;%u;%u;%luK",
963 input_rec[i].Event.KeyEvent.bKeyDown,
964 input_rec[i].Event.KeyEvent.wRepeatCount,
965 input_rec[i].Event.KeyEvent.wVirtualKeyCode,
966 input_rec[i].Event.KeyEvent.wVirtualScanCode,
967 input_rec[i].Event.KeyEvent.uChar.UnicodeChar,
968 input_rec[i].Event.KeyEvent.dwControlKeyState);
969 toadd = tmp;
970 nread = strlen (toadd);
971 break;
972 }
973
974 /* Ignore key up events, except for Alt+Numpad events. */
975 if (!input_rec[i].Event.KeyEvent.bKeyDown &&
976 !is_alt_numpad_event (&input_rec[i]))
977 continue;
978 /* Ignore Alt+Numpad keys. They are eventually handled below after
979 releasing the Alt key. */
980 if (input_rec[i].Event.KeyEvent.bKeyDown
981 && is_alt_numpad_key (&input_rec[i]))
982 continue;
983
984 if (ctrl_key_state & SHIFT_PRESSED)
985 con.nModifiers |= 1;
986 if (ctrl_key_state & RIGHT_ALT_PRESSED)
987 con.nModifiers |= 2;
988 if (ctrl_key_state & CTRL_PRESSED)
989 con.nModifiers |= 4;
990 if (ctrl_key_state & LEFT_ALT_PRESSED)
991 con.nModifiers |= 8;
992
993 /* Allow Backspace to emit ^? and escape sequences. */
994 if (input_rec[i].Event.KeyEvent.wVirtualKeyCode == VK_BACK)
995 {
996 char c = con.backspace_keycode;
997 nread = 0;
998 if (ctrl_key_state & ALT_PRESSED)
999 {
1000 if (con.metabit)
1001 c |= 0x80;
1002 else
1003 tmp[nread++] = '\e';
1004 }
1005 tmp[nread++] = c;
1006 tmp[nread] = 0;
1007 toadd = tmp;
1008 }
1009 /* Allow Ctrl-Space to emit ^@ */
1010 else if (input_rec[i].Event.KeyEvent.wVirtualKeyCode
1011 == ((wincap.has_con_24bit_colors () && !con_is_legacy) ?
1012 '2' : VK_SPACE)
1013 && (ctrl_key_state & CTRL_PRESSED)
1014 && !(ctrl_key_state & ALT_PRESSED))
1015 toadd = "";
1016 else if (unicode_char == 0
1017 /* arrow/function keys */
1018 || (input_rec[i].Event.KeyEvent.dwControlKeyState
1019 & ENHANCED_KEY))
1020 {
1021 toadd = get_nonascii_key (input_rec[i], tmp);
1022 if (!toadd)
1023 {
1024 con.nModifiers = 0;
1025 continue;
1026 }
1027 nread = strlen (toadd);
1028 }
1029 else
1030 {
1031 WCHAR second = unicode_char >= 0xd800 && unicode_char <= 0xdbff
1032 && i + 1 < total_read ?
1033 input_rec[i + 1].Event.KeyEvent.uChar.UnicodeChar : 0;
1034
1035 if (second < 0xdc00 || second > 0xdfff)
1036 {
1037 nread = con.con_to_str (tmp + 1, 59, unicode_char);
1038 }
1039 else
1040 {
1041 /* handle surrogate pairs */
1042 WCHAR pair[2] = { unicode_char, second };
1043 nread = sys_wcstombs (tmp + 1, 59, pair, 2);
1044 i++;
1045 }
1046
1047 /* Determine if the keystroke is modified by META. The tricky
1048 part is to distinguish whether the right Alt key should be
1049 recognized as Alt, or as AltGr. */
1050 bool meta =
1051 /* Alt but not AltGr (= left ctrl + right alt)? */
1052 (ctrl_key_state & ALT_PRESSED) != 0
1053 && ((ctrl_key_state & CTRL_PRESSED) == 0
1054 /* but also allow Alt-AltGr: */
1055 || (ctrl_key_state & ALT_PRESSED) == ALT_PRESSED
1056 || (unicode_char <= 0x1f || unicode_char == 0x7f));
1057 if (!meta)
1058 {
1059 /* Determine if the character is in the current multibyte
1060 charset. The test is easy. If the multibyte sequence
1061 is > 1 and the first byte is ASCII CAN, the character
1062 has been translated into the ASCII CAN + UTF-8 replacement
1063 sequence. If so, just ignore the keypress.
1064 FIXME: Is there a better solution? */
1065 if (nread > 1 && tmp[1] == 0x18)
1066 beep ();
1067 else
1068 toadd = tmp + 1;
1069 }
1070 else if (con.metabit)
1071 {
1072 tmp[1] |= 0x80;
1073 toadd = tmp + 1;
1074 }
1075 else
1076 {
1077 tmp[0] = '\033';
1078 tmp[1] = cyg_tolower (tmp[1]);
1079 toadd = tmp;
1080 nread++;
1081 con.nModifiers &= ~4;
1082 }
1083 }
1084 break;
1085
1086 case MOUSE_EVENT:
1087 send_winch_maybe ();
1088 {
1089 MOUSE_EVENT_RECORD& mouse_event = input_rec[i].Event.MouseEvent;
1090 /* As a unique guard for mouse report generation,
1091 call mouse_aware() which is common with select(), so the result
1092 of select() and the actual read() will be consistent on the
1093 issue of whether input (i.e. a mouse escape sequence) will
1094 be available or not */
1095 if (mouse_aware (mouse_event))
1096 {
1097 /* Note: Reported mouse position was already retrieved by
1098 mouse_aware() and adjusted by window scroll buffer offset */
1099
1100 /* Treat the double-click event like a regular button press */
1101 if (mouse_event.dwEventFlags == DOUBLE_CLICK)
1102 {
1103 syscall_printf ("mouse: double-click -> click");
1104 mouse_event.dwEventFlags = 0;
1105 }
1106
1107 /* This code assumes Windows never reports multiple button
1108 events at the same time. */
1109 int b = 0;
1110 char sz[32];
1111 char mode6_term = 'M';
1112
1113 if (mouse_event.dwEventFlags == MOUSE_WHEELED)
1114 {
1115 if (mouse_event.dwButtonState & 0xFF800000)
1116 {
1117 b = 0x41;
1118 strcpy (sz, "wheel down");
1119 }
1120 else
1121 {
1122 b = 0x40;
1123 strcpy (sz, "wheel up");
1124 }
1125 }
1126 else
1127 {
1128 /* Ignore unimportant mouse buttons */
1129 mouse_event.dwButtonState &= 0x7;
1130
1131 if (mouse_event.dwEventFlags == MOUSE_MOVED)
1132 {
1133 b = con.last_button_code;
1134 }
1135 else if (mouse_event.dwButtonState < con.dwLastButtonState
1136 && !con.ext_mouse_mode6)
1137 {
1138 b = 3;
1139 strcpy (sz, "btn up");
1140 }
1141 else if ((mouse_event.dwButtonState & 1)
1142 != (con.dwLastButtonState & 1))
1143 {
1144 b = 0;
1145 strcpy (sz, "btn1 down");
1146 }
1147 else if ((mouse_event.dwButtonState & 2)
1148 != (con.dwLastButtonState & 2))
1149 {
1150 b = 2;
1151 strcpy (sz, "btn2 down");
1152 }
1153 else if ((mouse_event.dwButtonState & 4)
1154 != (con.dwLastButtonState & 4))
1155 {
1156 b = 1;
1157 strcpy (sz, "btn3 down");
1158 }
1159
1160 if (con.ext_mouse_mode6 /* distinguish release */
1161 && mouse_event.dwButtonState < con.dwLastButtonState)
1162 mode6_term = 'm';
1163
1164 con.last_button_code = b;
1165
1166 if (mouse_event.dwEventFlags == MOUSE_MOVED)
1167 {
1168 b += 32;
1169 strcpy (sz, "move");
1170 }
1171 else
1172 {
1173 /* Remember the modified button state */
1174 con.dwLastButtonState = mouse_event.dwButtonState;
1175 }
1176 }
1177
1178 /* Remember mouse position */
1179 con.dwLastMousePosition.X = con.dwMousePosition.X;
1180 con.dwLastMousePosition.Y = con.dwMousePosition.Y;
1181
1182 /* Remember the modifiers */
1183 con.nModifiers = 0;
1184 if (mouse_event.dwControlKeyState & SHIFT_PRESSED)
1185 con.nModifiers |= 0x4;
1186 if (mouse_event.dwControlKeyState & ALT_PRESSED)
1187 con.nModifiers |= 0x8;
1188 if (mouse_event.dwControlKeyState & CTRL_PRESSED)
1189 con.nModifiers |= 0x10;
1190
1191 /* Indicate the modifiers */
1192 b |= con.nModifiers;
1193
1194 /* We can now create the code. */
1195 if (con.ext_mouse_mode6)
1196 {
1197 __small_sprintf (tmp, "\033[<%d;%d;%d%c", b,
1198 con.dwMousePosition.X + 1,
1199 con.dwMousePosition.Y + 1,
1200 mode6_term);
1201 nread = strlen (tmp);
1202 }
1203 else if (con.ext_mouse_mode15)
1204 {
1205 __small_sprintf (tmp, "\033[%d;%d;%dM", b + 32,
1206 con.dwMousePosition.X + 1,
1207 con.dwMousePosition.Y + 1);
1208 nread = strlen (tmp);
1209 }
1210 else if (con.ext_mouse_mode5)
1211 {
1212 unsigned int xcode = con.dwMousePosition.X + ' ' + 1;
1213 unsigned int ycode = con.dwMousePosition.Y + ' ' + 1;
1214
1215 __small_sprintf (tmp, "\033[M%c", b + ' ');
1216 nread = 4;
1217 /* the neat nested encoding function of mintty
1218 does not compile in g++, so let's unfold it: */
1219 if (xcode < 0x80)
1220 tmp [nread++] = xcode;
1221 else if (xcode < 0x800)
1222 {
1223 tmp [nread++] = 0xC0 + (xcode >> 6);
1224 tmp [nread++] = 0x80 + (xcode & 0x3F);
1225 }
1226 else
1227 tmp [nread++] = 0;
1228 if (ycode < 0x80)
1229 tmp [nread++] = ycode;
1230 else if (ycode < 0x800)
1231 {
1232 tmp [nread++] = 0xC0 + (ycode >> 6);
1233 tmp [nread++] = 0x80 + (ycode & 0x3F);
1234 }
1235 else
1236 tmp [nread++] = 0;
1237 }
1238 else
1239 {
1240 unsigned int xcode = con.dwMousePosition.X + ' ' + 1;
1241 unsigned int ycode = con.dwMousePosition.Y + ' ' + 1;
1242 if (xcode >= 256)
1243 xcode = 0;
1244 if (ycode >= 256)
1245 ycode = 0;
1246 __small_sprintf (tmp, "\033[M%c%c%c", b + ' ',
1247 xcode, ycode);
1248 nread = 6; /* tmp may contain NUL bytes */
1249 }
1250 syscall_printf ("mouse: %s at (%d,%d)", sz,
1251 con.dwMousePosition.X,
1252 con.dwMousePosition.Y);
1253
1254 toadd = tmp;
1255 }
1256 }
1257 break;
1258
1259 case FOCUS_EVENT:
1260 if (con.use_focus)
1261 {
1262 if (input_rec[i].Event.FocusEvent.bSetFocus)
1263 __small_sprintf (tmp, "\033[I");
1264 else
1265 __small_sprintf (tmp, "\033[O");
1266
1267 toadd = tmp;
1268 nread = 3;
1269 }
1270 break;
1271
1272 case WINDOW_BUFFER_SIZE_EVENT:
1273 if (send_winch_maybe ())
1274 {
1275 stat = input_winch;
1276 goto out;
1277 }
1278 /* fall through */
1279 default:
1280 continue;
1281 }
1282
1283 if (toadd)
1284 {
1285 ssize_t ret;
1286 line_edit_status res = line_edit (toadd, nread, *ti, &ret);
1287 if (res == line_edit_signalled)
1288 {
1289 stat = input_signalled;
1290 goto out;
1291 }
1292 else if (res == line_edit_input_done)
1293 {
1294 input_ready = true;
1295 stat = input_ok;
1296 if (ti->c_lflag & ICANON)
1297 goto out;
1298 }
1299 }
1300 }
1301 out:
1302 /* Discard processed recored. */
1303 DWORD discard_len = min (total_read, i + 1);
1304 /* If input is signalled, do not discard input here because
1305 tcflush() is already called from line_edit(). */
1306 if (stat == input_signalled && !(ti->c_lflag & NOFLSH))
1307 discard_len = 0;
1308 if (discard_len)
1309 {
1310 DWORD discarded;
1311 acquire_attach_mutex (mutex_timeout);
1312 ReadConsoleInputW (get_handle (), input_rec, discard_len, &discarded);
1313 release_attach_mutex ();
1314 con.num_processed -= min (con.num_processed, discarded);
1315 }
1316 return stat;
1317 }
1318
1319 bool
1320 dev_console::fillin (HANDLE h)
1321 {
1322 acquire_attach_mutex (mutex_timeout);
1323 bool ret = GetConsoleScreenBufferInfo (h, &b);
1324 release_attach_mutex ();
1325
1326 if (ret)
1327 {
1328 dwWinSize.Y = 1 + b.srWindow.Bottom - b.srWindow.Top;
1329 dwWinSize.X = 1 + b.srWindow.Right - b.srWindow.Left;
1330 if (b.dwCursorPosition.Y > dwEnd.Y
1331 || (b.dwCursorPosition.Y >= dwEnd.Y
1332 && b.dwCursorPosition.X > dwEnd.X))
1333 dwEnd = b.dwCursorPosition;
1334 }
1335 else
1336 {
1337 memset (&b, 0, sizeof (b));
1338 dwWinSize.Y = 25;
1339 dwWinSize.X = 80;
1340 b.srWindow.Bottom = 24;
1341 b.srWindow.Right = 79;
1342 }
1343
1344 return ret;
1345 }
1346
1347 void __reg3
1348 dev_console::scroll_buffer (HANDLE h, int x1, int y1, int x2, int y2,
1349 int xn, int yn)
1350 {
1351 /* Scroll the screen context.
1352 x1, y1 - ul corner
1353 x2, y2 - dr corner
1354 xn, yn - new ul corner
1355 Negative values represents current screen dimensions
1356 */
1357 SMALL_RECT sr1, sr2;
1358 CHAR_INFO fill;
1359 COORD dest;
1360 fill.Char.UnicodeChar = L' ';
1361 fill.Attributes = current_win32_attr;
1362
1363 fillin (h);
1364 sr1.Left = x1 >= 0 ? x1 : dwWinSize.X - 1;
1365 sr1.Top = y1 >= 0 ? y1 : b.srWindow.Bottom;
1366 sr1.Right = x2 >= 0 ? x2 : dwWinSize.X - 1;
1367 sr1.Bottom = y2 >= 0 ? y2 : b.srWindow.Bottom;
1368 sr2.Top = b.srWindow.Top + scroll_region.Top;
1369 sr2.Left = 0;
1370 sr2.Bottom = (scroll_region.Bottom < 0) ?
1371 b.srWindow.Bottom : b.srWindow.Top + scroll_region.Bottom;
1372 sr2.Right = dwWinSize.X - 1;
1373 if (sr1.Bottom > sr2.Bottom && sr1.Top <= sr2.Bottom)
1374 sr1.Bottom = sr2.Bottom;
1375 dest.X = xn >= 0 ? xn : dwWinSize.X - 1;
1376 dest.Y = yn >= 0 ? yn : b.srWindow.Bottom;
1377 acquire_attach_mutex (mutex_timeout);
1378 ScrollConsoleScreenBufferW (h, &sr1, &sr2, dest, &fill);
1379 release_attach_mutex ();
1380 }
1381
1382 inline void
1383 fhandler_console::scroll_buffer (int x1, int y1, int x2, int y2,
1384 int xn, int yn)
1385 {
1386 con.scroll_buffer (get_output_handle (), x1, y1, x2, y2, xn, yn);
1387 }
1388
1389 inline void
1390 fhandler_console::scroll_buffer_screen (int x1, int y1, int x2, int y2,
1391 int xn, int yn)
1392 {
1393 if (y1 >= 0)
1394 y1 += con.b.srWindow.Top;
1395 if (y2 >= 0)
1396 y2 += con.b.srWindow.Top;
1397 if (yn >= 0)
1398 yn += con.b.srWindow.Top;
1399 con.scroll_buffer (get_output_handle (), x1, y1, x2, y2, xn, yn);
1400 }
1401
1402 int
1403 fhandler_console::dup (fhandler_base *child, int flags)
1404 {
1405 /* See comments in fhandler_pty_slave::dup */
1406 if (myself->ctty != -2)
1407 myself->set_ctty (this, flags);
1408 return 0;
1409 }
1410
1411 int
1412 fhandler_console::open (int flags, mode_t)
1413 {
1414 HANDLE h;
1415
1416 if (dev () == FH_ERROR)
1417 {
1418 set_errno (EPERM); /* constructor found an error */
1419 return 0;
1420 }
1421
1422 tcinit (false);
1423
1424 set_handle (NULL);
1425 set_output_handle (NULL);
1426
1427 /* Open the input handle as handle_ */
1428 h = CreateFileW (L"CONIN$", GENERIC_READ | GENERIC_WRITE,
1429 FILE_SHARE_READ | FILE_SHARE_WRITE, &sec_none,
1430 OPEN_EXISTING, 0, 0);
1431
1432 if (h == INVALID_HANDLE_VALUE)
1433 {
1434 __seterrno ();
1435 return 0;
1436 }
1437 set_handle (h);
1438 handle_set.input_handle = h;
1439
1440 h = CreateFileW (L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
1441 FILE_SHARE_READ | FILE_SHARE_WRITE, &sec_none,
1442 OPEN_EXISTING, 0, 0);
1443
1444 if (h == INVALID_HANDLE_VALUE)
1445 {
1446 __seterrno ();
1447 return 0;
1448 }
1449 set_output_handle (h);
1450 handle_set.output_handle = h;
1451
1452 setup_io_mutex ();
1453 handle_set.input_mutex = input_mutex;
1454 handle_set.output_mutex = output_mutex;
1455
1456 if (con.fillin (get_output_handle ()))
1457 {
1458 con.current_win32_attr = con.b.wAttributes;
1459 if (!con.default_color)
1460 con.default_color = con.b.wAttributes;
1461 con.set_default_attr ();
1462 }
1463
1464 set_open_status ();
1465
1466 if (myself->pid == con.owner && wincap.has_con_24bit_colors ())
1467 {
1468 bool is_legacy = false;
1469 DWORD dwMode;
1470 /* Check xterm compatible mode in output */
1471 acquire_attach_mutex (mutex_timeout);
1472 GetConsoleMode (get_output_handle (), &dwMode);
1473 if (!SetConsoleMode (get_output_handle (),
1474 dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING))
1475 is_legacy = true;
1476 SetConsoleMode (get_output_handle (), dwMode);
1477 /* Check xterm compatible mode in input */
1478 GetConsoleMode (get_handle (), &dwMode);
1479 if (!SetConsoleMode (get_handle (),
1480 dwMode | ENABLE_VIRTUAL_TERMINAL_INPUT))
1481 is_legacy = true;
1482 SetConsoleMode (get_handle (), dwMode);
1483 release_attach_mutex ();
1484 con.is_legacy = is_legacy;
1485 extern int sawTERM;
1486 if (con_is_legacy && !sawTERM)
1487 setenv ("TERM", "cygwin", 1);
1488 }
1489
1490 debug_printf ("opened conin$ %p, conout$ %p", get_handle (),
1491 get_output_handle ());
1492
1493 if (myself->pid == con.owner)
1494 {
1495 char name[MAX_PATH];
1496 shared_name (name, CONS_THREAD_SYNC, get_minor ());
1497 thread_sync_event = CreateEvent(NULL, FALSE, FALSE, name);
1498 new cygthread (::cons_master_thread, this, "consm");
1499 WaitForSingleObject (thread_sync_event, INFINITE);
1500 CloseHandle (thread_sync_event);
1501 }
1502 return 1;
1503 }
1504
1505 bool
1506 fhandler_console::open_setup (int flags)
1507 {
1508 set_flags ((flags & ~O_TEXT) | O_BINARY);
1509 if (myself->set_ctty (this, flags) && !myself->cygstarted)
1510 init_console_handler (true);
1511 return fhandler_base::open_setup (flags);
1512 }
1513
1514 void
1515 fhandler_console::post_open_setup (int fd)
1516 {
1517 /* Setting-up console mode for cygwin app started from non-cygwin app. */
1518 if (fd == 0)
1519 {
1520 set_input_mode (tty::cygwin, &get_ttyp ()->ti, &handle_set);
1521 con.disable_master_thread = false;
1522 }
1523 else if (fd == 1 || fd == 2)
1524 set_output_mode (tty::cygwin, &get_ttyp ()->ti, &handle_set);
1525
1526 fhandler_base::post_open_setup (fd);
1527 }
1528
1529 int
1530 fhandler_console::close ()
1531 {
1532 debug_printf ("closing: %p, %p", get_handle (), get_output_handle ());
1533
1534 acquire_output_mutex (mutex_timeout);
1535
1536 if (shared_console_info)
1537 {
1538 /* Restore console mode if this is the last closure. */
1539 OBJECT_BASIC_INFORMATION obi;
1540 NTSTATUS status;
1541 status = NtQueryObject (get_handle (), ObjectBasicInformation,
1542 &obi, sizeof obi, NULL);
1543 if ((NT_SUCCESS (status) && obi.HandleCount == 1)
1544 || myself->pid == con.owner)
1545 {
1546 /* Cleaning-up console mode for cygwin apps. */
1547 set_output_mode (tty::restore, &get_ttyp ()->ti, &handle_set);
1548 set_input_mode (tty::restore, &get_ttyp ()->ti, &handle_set);
1549 con.disable_master_thread = true;
1550 }
1551 }
1552
1553 release_output_mutex ();
1554
1555 if (shared_console_info && con.owner == myself->pid
1556 && master_thread_started)
1557 {
1558 char name[MAX_PATH];
1559 shared_name (name, CONS_THREAD_SYNC, get_minor ());
1560 thread_sync_event = OpenEvent (MAXIMUM_ALLOWED, FALSE, name);
1561 con.owner = MAX_PID + 1;
1562 WaitForSingleObject (thread_sync_event, INFINITE);
1563 CloseHandle (thread_sync_event);
1564 con.owner = 0;
1565 }
1566
1567 CloseHandle (input_mutex);
1568 input_mutex = NULL;
1569 CloseHandle (output_mutex);
1570 output_mutex = NULL;
1571
1572 CloseHandle (get_handle ());
1573 CloseHandle (get_output_handle ());
1574
1575 if (con_ra.rabuf)
1576 free (con_ra.rabuf);
1577
1578 if (!have_execed)
1579 free_console ();
1580 return 0;
1581 }
1582
1583 int
1584 fhandler_console::ioctl (unsigned int cmd, void *arg)
1585 {
1586 int res = fhandler_termios::ioctl (cmd, arg);
1587 if (res <= 0)
1588 return res;
1589 acquire_output_mutex (mutex_timeout);
1590 switch (cmd)
1591 {
1592 case TIOCGWINSZ:
1593 int st;
1594
1595 st = con.fillin (get_output_handle ());
1596 if (st)
1597 {
1598 /* *not* the buffer size, the actual screen size... */
1599 /* based on Left Top Right Bottom of srWindow */
1600 ((struct winsize *) arg)->ws_row = con.dwWinSize.Y;
1601 ((struct winsize *) arg)->ws_col = con.dwWinSize.X;
1602 syscall_printf ("WINSZ: (row=%d,col=%d)",
1603 ((struct winsize *) arg)->ws_row,
1604 ((struct winsize *) arg)->ws_col);
1605 release_output_mutex ();
1606 return 0;
1607 }
1608 else
1609 {
1610 syscall_printf ("WINSZ failed");
1611 __seterrno ();
1612 release_output_mutex ();
1613 return -1;
1614 }
1615 release_output_mutex ();
1616 return 0;
1617 case TIOCSWINSZ:
1618 bg_check (SIGTTOU);
1619 release_output_mutex ();
1620 return 0;
1621 case KDGKBMETA:
1622 *(int *) arg = (con.metabit) ? K_METABIT : K_ESCPREFIX;
1623 release_output_mutex ();
1624 return 0;
1625 case KDSKBMETA:
1626 if ((intptr_t) arg == K_METABIT)
1627 con.metabit = TRUE;
1628 else if ((intptr_t) arg == K_ESCPREFIX)
1629 con.metabit = FALSE;
1630 else
1631 {
1632 set_errno (EINVAL);
1633 release_output_mutex ();
1634 return -1;
1635 }
1636 release_output_mutex ();
1637 return 0;
1638 case TIOCLINUX:
1639 if (*(unsigned char *) arg == 6)
1640 {
1641 *(unsigned char *) arg = (unsigned char) con.nModifiers;
1642 release_output_mutex ();
1643 return 0;
1644 }
1645 set_errno (EINVAL);
1646 release_output_mutex ();
1647 return -1;
1648 case FIONREAD:
1649 {
1650 DWORD n;
1651 int ret = 0;
1652 INPUT_RECORD inp[INREC_SIZE];
1653 acquire_attach_mutex (mutex_timeout);
1654 BOOL r = PeekConsoleInputW (get_handle (), inp, INREC_SIZE, &n);
1655 release_attach_mutex ();
1656 if (!r)
1657 {
1658 set_errno (EINVAL);
1659 release_output_mutex ();
1660 return -1;
1661 }
1662 bool saw_eol = false;
1663 for (DWORD i=0; i<n; i++)
1664 if (inp[i].EventType == KEY_EVENT &&
1665 inp[i].Event.KeyEvent.bKeyDown &&
1666 inp[i].Event.KeyEvent.uChar.UnicodeChar)
1667 {
1668 WCHAR wc = inp[i].Event.KeyEvent.uChar.UnicodeChar;
1669 char mbs[8];
1670 int len = con.con_to_str (mbs, sizeof (mbs), wc);
1671 if ((get_ttyp ()->ti.c_lflag & ICANON) &&
1672 len == 1 && CCEQ (get_ttyp ()->ti.c_cc[VEOF], mbs[0]))
1673 {
1674 saw_eol = true;
1675 break;
1676 }
1677 ret += len;
1678 const char eols[] = {
1679 '\n',
1680 '\r',
1681 (char) get_ttyp ()->ti.c_cc[VEOL],
1682 (char) get_ttyp ()->ti.c_cc[VEOL2]
1683 };
1684 if ((get_ttyp ()->ti.c_lflag & ICANON) &&
1685 len == 1 && memchr (eols, mbs[0], sizeof (eols)))
1686 {
1687 saw_eol = true;
1688 break;
1689 }
1690 }
1691 if ((get_ttyp ()->ti.c_lflag & ICANON) && !saw_eol)
1692 *(int *) arg = 0;
1693 else
1694 *(int *) arg = ret;
1695 release_output_mutex ();
1696 return 0;
1697 }
1698 break;
1699 }
1700
1701 release_output_mutex ();
1702 return fhandler_base::ioctl (cmd, arg);
1703 }
1704
1705 int
1706 fhandler_console::tcflush (int queue)
1707 {
1708 int res = 0;
1709 if (queue == TCIFLUSH
1710 || queue == TCIOFLUSH)
1711 {
1712 acquire_attach_mutex (mutex_timeout);
1713 BOOL r = FlushConsoleInputBuffer (get_handle ());
1714 release_attach_mutex ();
1715 if (!r)
1716 {
1717 __seterrno ();
1718 res = -1;
1719 }
1720 con.num_processed = 0;
1721 }
1722 return res;
1723 }
1724
1725 int
1726 fhandler_console::tcsetattr (int a, struct termios const *t)
1727 {
1728 get_ttyp ()->ti = *t;
1729 return 0;
1730 }
1731
1732 int
1733 fhandler_console::tcgetattr (struct termios *t)
1734 {
1735 *t = get_ttyp ()->ti;
1736 t->c_cflag |= CS8;
1737 return 0;
1738 }
1739
1740 fhandler_console::fhandler_console (fh_devices unit) :
1741 fhandler_termios (), input_ready (false), thread_sync_event (NULL),
1742 input_mutex (NULL), output_mutex (NULL)
1743 {
1744 if (unit > 0)
1745 dev ().parse (unit);
1746 setup ();
1747 trunc_buf.len = 0;
1748 _tc = &(shared_console_info->tty_min_state);
1749 }
1750
1751 void
1752 dev_console::set_color (HANDLE h)
1753 {
1754 WORD win_fg = fg;
1755 WORD win_bg = bg;
1756 if (reverse)
1757 {
1758 WORD save_fg = win_fg;
1759 win_fg = (win_bg & BACKGROUND_RED ? FOREGROUND_RED : 0) |
1760 (win_bg & BACKGROUND_GREEN ? FOREGROUND_GREEN : 0) |
1761 (win_bg & BACKGROUND_BLUE ? FOREGROUND_BLUE : 0) |
1762 (win_bg & BACKGROUND_INTENSITY ? FOREGROUND_INTENSITY : 0);
1763 win_bg = (save_fg & FOREGROUND_RED ? BACKGROUND_RED : 0) |
1764 (save_fg & FOREGROUND_GREEN ? BACKGROUND_GREEN : 0) |
1765 (save_fg & FOREGROUND_BLUE ? BACKGROUND_BLUE : 0) |
1766 (save_fg & FOREGROUND_INTENSITY ? BACKGROUND_INTENSITY : 0);
1767 }
1768
1769 /* apply attributes */
1770 if (underline)
1771 win_fg = underline_color;
1772 /* emulate blink with bright background */
1773 if (blink)
1774 win_bg |= BACKGROUND_INTENSITY;
1775 if (intensity == INTENSITY_INVISIBLE)
1776 win_fg = win_bg;
1777 else if (intensity != INTENSITY_BOLD)
1778 /* nothing to do */;
1779 /* apply foreground intensity only in non-reverse mode! */
1780 else if (reverse)
1781 win_bg |= BACKGROUND_INTENSITY;
1782 else
1783 win_fg |= FOREGROUND_INTENSITY;
1784
1785 current_win32_attr = win_fg | win_bg;
1786 if (h)
1787 {
1788 acquire_attach_mutex (mutex_timeout);
1789 SetConsoleTextAttribute (h, current_win32_attr);
1790 release_attach_mutex ();
1791 }
1792 }
1793
1794 #define FOREGROUND_ATTR_MASK (FOREGROUND_RED | FOREGROUND_GREEN | \
1795 FOREGROUND_BLUE | FOREGROUND_INTENSITY)
1796 #define BACKGROUND_ATTR_MASK (BACKGROUND_RED | BACKGROUND_GREEN | \
1797 BACKGROUND_BLUE | BACKGROUND_INTENSITY)
1798 void
1799 dev_console::set_default_attr ()
1800 {
1801 blink = underline = reverse = false;
1802 intensity = INTENSITY_NORMAL;
1803 fg = default_color & FOREGROUND_ATTR_MASK;
1804 bg = default_color & BACKGROUND_ATTR_MASK;
1805 set_color (NULL);
1806 }
1807
1808 int
1809 dev_console::set_cl_x (cltype x)
1810 {
1811 if (x == cl_disp_beg || x == cl_buf_beg)
1812 return 0;
1813 if (x == cl_disp_end)
1814 return dwWinSize.X - 1;
1815 if (x == cl_buf_end)
1816 return b.dwSize.X - 1;
1817 return b.dwCursorPosition.X;
1818 }
1819
1820 int
1821 dev_console::set_cl_y (cltype y)
1822 {
1823 if (y == cl_buf_beg)
1824 return 0;
1825 if (y == cl_disp_beg)
1826 return b.srWindow.Top;
1827 if (y == cl_disp_end)
1828 return b.srWindow.Bottom;
1829 if (y == cl_buf_end)
1830 return b.dwSize.Y - 1;
1831 return b.dwCursorPosition.Y;
1832 }
1833
1834 bool
1835 dev_console::scroll_window (HANDLE h, int x1, int y1, int x2, int y2)
1836 {
1837 if (save_buf || x1 != 0 || x2 != dwWinSize.X - 1 || y1 != b.srWindow.Top
1838 || y2 != b.srWindow.Bottom || b.dwSize.Y <= dwWinSize.Y)
1839 return false;
1840
1841 SMALL_RECT sr;
1842 int toscroll = dwEnd.Y - b.srWindow.Top + 1;
1843 sr.Left = sr.Right = dwEnd.X = 0;
1844
1845 acquire_attach_mutex (mutex_timeout);
1846 if (b.srWindow.Bottom + toscroll >= b.dwSize.Y)
1847 {
1848 /* So we're at the end of the buffer and scrolling the console window
1849 would move us beyond the buffer. What we do here is to scroll the
1850 console buffer upward by just as much so that the current last line
1851 becomes the last line just prior to the first window line. That
1852 keeps the end of the console buffer intact, as desired. */
1853 SMALL_RECT br;
1854 COORD dest;
1855 CHAR_INFO fill;
1856
1857 br.Left = 0;
1858 br.Top = (b.srWindow.Bottom - b.srWindow.Top) + 1
1859 - (b.dwSize.Y - dwEnd.Y - 1);
1860 br.Right = b.dwSize.X - 1;
1861 br.Bottom = b.dwSize.Y - 1;
1862 dest.X = dest.Y = 0;
1863 fill.Char.UnicodeChar = L' ';
1864 fill.Attributes = current_win32_attr;
1865 ScrollConsoleScreenBufferW (h, &br, NULL, dest, &fill);
1866 /* Since we're moving the console buffer under the console window
1867 we only have to move the console window if the user scrolled the
1868 window upwards. The number of lines is the distance to the
1869 buffer bottom. */
1870 toscroll = b.dwSize.Y - b.srWindow.Bottom - 1;
1871 /* Fix dwEnd to reflect the new cursor line. Take the above scrolling
1872 into account and subtract 1 to account for the increment below. */
1873 dwEnd.Y = b.dwCursorPosition.Y + toscroll - 1;
1874 }
1875 if (toscroll)
1876 {
1877 /* FIXME: For some reason SetConsoleWindowInfo does not correctly
1878 set the scrollbars. Calling SetConsoleCursorPosition here is
1879 just a workaround which doesn't cover all cases. In some scenarios
1880 the scrollbars are still off by one console window size. */
1881
1882 /* The reminder of the console buffer is big enough to simply move
1883 the console window. We have to set the cursor first, otherwise
1884 the scroll bars will not be corrected. */
1885 SetConsoleCursorPosition (h, dwEnd);
1886 /* If the user scolled manually, setting the cursor position might scroll
1887 the console window so that the cursor is not at the top. Correct
1888 the action by moving the window down again so the cursor is one line
1889 above the new window position. */
1890 GetConsoleScreenBufferInfo (h, &b);
1891 if (b.dwCursorPosition.Y >= b.srWindow.Top)
1892 toscroll = b.dwCursorPosition.Y - b.srWindow.Top + 1;
1893 /* Move the window accordingly. */
1894 sr.Top = sr.Bottom = toscroll;
1895 SetConsoleWindowInfo (h, FALSE, &sr);
1896 }
1897 /* Eventually set cursor to new end position at the top of the window. */
1898 dwEnd.Y++;
1899 SetConsoleCursorPosition (h, dwEnd);
1900 release_attach_mutex ();
1901 /* Fix up console buffer info. */
1902 fillin (h);
1903 return true;
1904 }
1905
1906 /*
1907 * Clear the screen context from x1/y1 to x2/y2 cell.
1908 * Negative values represents current screen dimensions
1909 */
1910 void __reg3
1911 fhandler_console::clear_screen (cltype xc1, cltype yc1, cltype xc2, cltype yc2)
1912 {
1913 HANDLE h = get_output_handle ();
1914 SHORT oldEndY = con.dwEnd.Y;
1915
1916 con.fillin (h);
1917
1918 int x1 = con.set_cl_x (xc1);
1919 int y1 = con.set_cl_y (yc1);
1920 int x2 = con.set_cl_x (xc2);
1921 int y2 = con.set_cl_y (yc2);
1922
1923 /* Make correction for the following situation: The console buffer
1924 is only partially used and the user scrolled down into the as yet
1925 unused area so far that the cursor is outside the window buffer. */
1926 if (oldEndY < con.dwEnd.Y && oldEndY < con.b.srWindow.Top)
1927 {
1928 con.dwEnd.Y = con.b.dwCursorPosition.Y = oldEndY;
1929 y1 = con.b.srWindow.Top;
1930 }
1931
1932 /* Detect special case - scroll the screen if we have a buffer in order to
1933 preserve the buffer. */
1934 if (!con.scroll_window (h, x1, y1, x2, y2))
1935 con.clear_screen (h, x1, y1, x2, y2);
1936 }
1937
1938 void __reg3
1939 dev_console::clear_screen (HANDLE h, int x1, int y1, int x2, int y2)
1940 {
1941 COORD tlc;
1942 DWORD done;
1943 int num;
1944
1945 num = abs (y1 - y2) * b.dwSize.X + abs (x1 - x2) + 1;
1946
1947 if ((y2 * b.dwSize.X + x2) > (y1 * b.dwSize.X + x1))
1948 {
1949 tlc.X = x1;
1950 tlc.Y = y1;
1951 }
1952 else
1953 {
1954 tlc.X = x2;
1955 tlc.Y = y2;
1956 }
1957 acquire_attach_mutex (mutex_timeout);
1958 FillConsoleOutputCharacterW (h, L' ', num, tlc, &done);
1959 FillConsoleOutputAttribute (h, current_win32_attr, num, tlc, &done);
1960 release_attach_mutex ();
1961 }
1962
1963 void __reg3
1964 fhandler_console::cursor_set (bool rel_to_top, int x, int y)
1965 {
1966 COORD pos;
1967
1968 con.fillin (get_output_handle ());
1969 #if 0
1970 /* Setting y to the current b.srWindow.Bottom here is the reason that the window
1971 isn't scrolled back to the current cursor position like it's done in
1972 any other terminal. Rather, the curser is forced to the bottom of the
1973 currently scrolled region. This breaks the console buffer content if
1974 output is generated while the user had the window scrolled back. This
1975 behaviour is very old, it has no matching ChangeLog entry.
1976 Just disable for now but keep the code in for future reference. */
1977 if (y > con.b.srWindow.Bottom)
1978 y = con.b.srWindow.Bottom;
1979 else
1980 #endif
1981 if (y < 0)
1982 y = 0;
1983 else if (rel_to_top)
1984 y += con.b.srWindow.Top;
1985
1986 if (x > con.dwWinSize.X)
1987 x = con.dwWinSize.X - 1;
1988 else if (x < 0)
1989 x = 0;
1990
1991 pos.X = x;
1992 pos.Y = y;
1993 acquire_attach_mutex (mutex_timeout);
1994 SetConsoleCursorPosition (get_output_handle (), pos);
1995 release_attach_mutex ();
1996 }
1997
1998 void __reg3
1999 fhandler_console::cursor_rel (int x, int y)
2000 {
2001 con.fillin (get_output_handle ());
2002 x += con.b.dwCursorPosition.X;
2003 y += con.b.dwCursorPosition.Y;
2004 cursor_set (false, x, y);
2005 }
2006
2007 void __reg3
2008 fhandler_console::cursor_get (int *x, int *y)
2009 {
2010 con.fillin (get_output_handle ());
2011 *y = con.b.dwCursorPosition.Y;
2012 *x = con.b.dwCursorPosition.X;
2013 }
2014
2015 /* VT100 line drawing graphics mode maps `abcdefghijklmnopqrstuvwxyz{|}~ to
2016 graphical characters */
2017 static const wchar_t __vt100_conv[31] = {
2018 0x25C6, /* Black Diamond */
2019 0x2592, /* Medium Shade */
2020 0x2409, /* Symbol for Horizontal Tabulation */
2021 0x240C, /* Symbol for Form Feed */
2022 0x240D, /* Symbol for Carriage Return */
2023 0x240A, /* Symbol for Line Feed */
2024 0x00B0, /* Degree Sign */
2025 0x00B1, /* Plus-Minus Sign */
2026 0x2424, /* Symbol for Newline */
2027 0x240B, /* Symbol for Vertical Tabulation */
2028 0x2518, /* Box Drawings Light Up And Left */
2029 0x2510, /* Box Drawings Light Down And Left */
2030 0x250C, /* Box Drawings Light Down And Right */
2031 0x2514, /* Box Drawings Light Up And Right */
2032 0x253C, /* Box Drawings Light Vertical And Horizontal */
2033 0x23BA, /* Horizontal Scan Line-1 */
2034 0x23BB, /* Horizontal Scan Line-3 */
2035 0x2500, /* Box Drawings Light Horizontal */
2036 0x23BC, /* Horizontal Scan Line-7 */
2037 0x23BD, /* Horizontal Scan Line-9 */
2038 0x251C, /* Box Drawings Light Vertical And Right */
2039 0x2524, /* Box Drawings Light Vertical And Left */
2040 0x2534, /* Box Drawings Light Up And Horizontal */
2041 0x252C, /* Box Drawings Light Down And Horizontal */
2042 0x2502, /* Box Drawings Light Vertical */
2043 0x2264, /* Less-Than Or Equal To */
2044 0x2265, /* Greater-Than Or Equal To */
2045 0x03C0, /* Greek Small Letter Pi */
2046 0x2260, /* Not Equal To */
2047 0x00A3, /* Pound Sign */
2048 0x00B7, /* Middle Dot */
2049 };
2050
2051 inline bool
2052 fhandler_console::write_console (PWCHAR buf, DWORD len, DWORD& done)
2053 {
2054 if (con.iso_2022_G1
2055 ? con.vt100_graphics_mode_G1
2056 : con.vt100_graphics_mode_G0)
2057 for (DWORD i = 0; i < len; i ++)
2058 if (buf[i] >= (unsigned char) '`' && buf[i] <= (unsigned char) '~')
2059 buf[i] = __vt100_conv[buf[i] - (unsigned char) '`'];
2060
2061 if (len > 0)
2062 last_char = buf[len-1];
2063
2064 while (len > 0)
2065 {
2066 DWORD nbytes = len > MAX_WRITE_CHARS ? MAX_WRITE_CHARS : len;
2067 acquire_attach_mutex (mutex_timeout);
2068 BOOL r = WriteConsoleW (get_output_handle (), buf, nbytes, &done, 0);
2069 release_attach_mutex ();
2070 if (!r)
2071 {
2072 __seterrno ();
2073 return false;
2074 }
2075 len -= done;
2076 buf += done;
2077 }
2078 return true;
2079 }
2080
2081 /* The following three functions were adapted (i.e., mildly modified) from
2082 http://stackoverflow.com/questions/14699043/replacement-to-systemcolor */
2083
2084 /* Split a rectangular region into two smaller rectangles based on the
2085 largest dimension. */
2086 static void
2087 region_split (PCHAR_INFO& buf, COORD& bufsiz, SMALL_RECT& region,
2088 PCHAR_INFO& buf_b, COORD& bufsiz_b, SMALL_RECT& region_b)
2089 {
2090 region_b = region;
2091 bufsiz_b = bufsiz;
2092
2093 SHORT half = (1 + region.Bottom - region.Top) / 2;
2094 region_b.Top += half;
2095 region.Bottom = (bufsiz.Y = region_b.Top) - 1;
2096 buf_b = buf + (half * (1 + region.Right));
2097 bufsiz_b.Y = region_b.Bottom - region_b.Top;
2098 }
2099
2100 /* Utility function to figure out the distance between two points. */
2101 static SHORT
2102 delta (SHORT first, SHORT second)
2103 {
2104 return (second >= first) ? (second - first + 1) : 0;
2105 }
2106
2107 /* Subdivide the ReadConsoleInput operation into smaller and smaller chunks as
2108 needed until it succeeds in reading the entire screen buffer. */
2109 static BOOL
2110 ReadConsoleOutputWrapper (HANDLE h, PCHAR_INFO buf, COORD bufsiz,
2111 SMALL_RECT region)
2112 {
2113 COORD coord = {};
2114 SHORT width = delta (region.Left, region.Right);
2115 SHORT height = delta (region.Top, region.Bottom);
2116
2117 if ((width == 0) || (height == 0))
2118 return TRUE;
2119
2120 acquire_attach_mutex (mutex_timeout);
2121 BOOL success = ReadConsoleOutputW (h, buf, bufsiz, coord, &region);
2122 release_attach_mutex ();
2123 if (success)
2124 /* it worked */;
2125 else if (GetLastError () == ERROR_NOT_ENOUGH_MEMORY && (width * height) > 1)
2126 {
2127 PCHAR_INFO buf_b;
2128 COORD bufsiz_b;
2129 SMALL_RECT region_b;
2130 region_split (buf, bufsiz, region, buf_b, bufsiz_b, region_b);
2131 success = ReadConsoleOutputWrapper (h, buf, bufsiz, region)
2132 && ReadConsoleOutputWrapper (h, buf_b, bufsiz_b, region_b);
2133 }
2134 return success;
2135 }
2136
2137 void
2138 dev_console::save_restore (HANDLE h, char c)
2139 {
2140 if (c == 'h') /* save */
2141 {
2142 fillin (h);
2143 save_bufsize.X = b.dwSize.X;
2144 if ((save_bufsize.Y = dwEnd.Y + 1) > b.dwSize.Y)
2145 save_bufsize.X = b.dwSize.Y;
2146
2147 if (save_buf)
2148 cfree (save_buf);
2149 size_t screen_size = sizeof (CHAR_INFO) * save_bufsize.X * save_bufsize.Y;
2150 save_buf = (PCHAR_INFO) cmalloc_abort (HEAP_1_BUF, screen_size);
2151
2152 save_cursor = b.dwCursorPosition; /* Remember where we were. */
2153 save_top = b.srWindow.Top;
2154
2155 SMALL_RECT now = {}; /* Read the whole buffer */
2156 now.Bottom = save_bufsize.Y - 1;
2157 now.Right = save_bufsize.X - 1;
2158 if (!ReadConsoleOutputWrapper (h, save_buf, save_bufsize, now))
2159 debug_printf ("ReadConsoleOutputWrapper(h, ...) failed during save, %E");
2160
2161 /* Position at top of buffer */
2162 COORD cob = {};
2163 acquire_attach_mutex (mutex_timeout);
2164 if (!SetConsoleCursorPosition (h, cob))
2165 debug_printf ("SetConsoleCursorInfo(%p, ...) failed during save, %E", h);
2166 release_attach_mutex ();
2167
2168 /* Clear entire buffer */
2169 clear_screen (h, 0, 0, now.Right, now.Bottom);
2170 b.dwCursorPosition.X = b.dwCursorPosition.Y = dwEnd.X = dwEnd.Y = 0;
2171 }
2172 else if (save_buf)
2173 {
2174 COORD cob = {};
2175 SMALL_RECT now = {};
2176 now.Bottom = save_bufsize.Y - 1;
2177 now.Right = save_bufsize.X - 1;
2178 /* Restore whole buffer */
2179 clear_screen (h, 0, 0, b.dwSize.X - 1, b.dwSize.Y - 1);
2180 acquire_attach_mutex (mutex_timeout);
2181 BOOL res = WriteConsoleOutputW (h, save_buf, save_bufsize, cob, &now);
2182 release_attach_mutex ();
2183 if (!res)
2184 debug_printf ("WriteConsoleOutputW failed, %E");
2185
2186 cfree (save_buf);
2187 save_buf = NULL;
2188
2189 cob.X = 0;
2190 cob.Y = save_top;
2191 /* CGF: NOOP? Doesn't seem to position screen as expected */
2192 /* Temporarily position at top of screen */
2193 acquire_attach_mutex (mutex_timeout);
2194 if (!SetConsoleCursorPosition (h, cob))
2195 debug_printf ("SetConsoleCursorInfo(%p, cob) failed during restore, %E", h);
2196 /* Position where we were previously */
2197 if (!SetConsoleCursorPosition (h, save_cursor))
2198 debug_printf ("SetConsoleCursorInfo(%p, save_cursor) failed during restore, %E", h);
2199 release_attach_mutex ();
2200 /* Get back correct version of buffer information */
2201 dwEnd.X = dwEnd.Y = 0;
2202 fillin (h);
2203 }
2204 }
2205
2206 #define BAK 1
2207 #define ESC 2
2208 #define NOR 0
2209 #define IGN 4
2210 #if 1
2211 #define ERR 5
2212 #else
2213 #define ERR NOR
2214 #endif
2215 #define DWN 6
2216 #define BEL 7
2217 #define TAB 8 /* We should't let the console deal with these */
2218 #define CR 13
2219 #define LF 10
2220 #define SO 14
2221 #define SI 15
2222
2223 static const char base_chars[256] =
2224 {
2225 /*00 01 02 03 04 05 06 07 */ IGN, ERR, ERR, NOR, NOR, NOR, NOR, BEL,
2226 /*08 09 0A 0B 0C 0D 0E 0F */ BAK, TAB, DWN, ERR, ERR, CR, SO, SI,
2227 /*10 11 12 13 14 15 16 17 */ NOR, NOR, ERR, ERR, ERR, ERR, ERR, ERR,
2228 /*18 19 1A 1B 1C 1D 1E 1F */ NOR, NOR, ERR, ESC, ERR, ERR, ERR, ERR,
2229 /* ! " # $ % & ' */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2230 /*( ) * + , - . / */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2231 /*0 1 2 3 4 5 6 7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2232 /*8 9 : ; < = > ? */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2233 /*@ A B C D E F G */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2234 /*H I J K L M N O */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2235 /*P Q R S T U V W */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2236 /*X Y Z [ \ ] ^ _ */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2237 /*` a b c d e f g */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2238 /*h i j k l m n o */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2239 /*p q r s t u v w */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2240 /*x y z { | } ~ 7F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2241 /*80 81 82 83 84 85 86 87 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2242 /*88 89 8A 8B 8C 8D 8E 8F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2243 /*90 91 92 93 94 95 96 97 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2244 /*98 99 9A 9B 9C 9D 9E 9F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2245 /*A0 A1 A2 A3 A4 A5 A6 A7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2246 /*A8 A9 AA AB AC AD AE AF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2247 /*B0 B1 B2 B3 B4 B5 B6 B7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2248 /*B8 B9 BA BB BC BD BE BF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2249 /*C0 C1 C2 C3 C4 C5 C6 C7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2250 /*C8 C9 CA CB CC CD CE CF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2251 /*D0 D1 D2 D3 D4 D5 D6 D7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2252 /*D8 D9 DA DB DC DD DE DF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2253 /*E0 E1 E2 E3 E4 E5 E6 E7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2254 /*E8 E9 EA EB EC ED EE EF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2255 /*F0 F1 F2 F3 F4 F5 F6 F7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
2256 /*F8 F9 FA FB FC FD FE FF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR };
2257
2258 static const char table256[256] =
2259 {
2260 0, 4, 2, 6, 1, 5, 3, 7, 8,12,10,14, 9,13,11,15,
2261 0, 1, 1, 1, 9, 9, 2, 3, 3, 3, 3, 9, 2, 3, 3, 3,
2262 3,11, 2, 3, 3, 3,11,11,10, 3, 3,11,11,11,10,10,
2263 11,11,11,11, 4, 5, 5, 5, 5, 9, 6, 8, 8, 8, 8, 9,
2264 6, 8, 8, 8, 8, 7, 6, 8, 8, 8, 7, 7, 6, 8, 8, 7,
2265 7,11,10,10, 7, 7,11,11, 4, 5, 5, 5, 5,13, 6, 8,
2266 8, 8, 8, 7, 6, 8, 8, 8, 7, 7, 6, 8, 8, 7, 7, 7,
2267 6, 8, 7, 7, 7, 7,14, 7, 7, 7, 7, 7, 4, 5, 5, 5,
2268 13,13, 6, 8, 8, 8, 7, 7, 6, 8, 8, 7, 7, 7, 6, 8,
2269 7, 7, 7, 7,14, 7, 7, 7, 7, 7,14, 7, 7, 7, 7,15,
2270 12, 5, 5,13,13,13, 6, 8, 8, 7, 7,13, 6, 8, 7, 7,
2271 7, 7,14, 7, 7, 7, 7, 7,14, 7, 7, 7, 7,15,14,14,
2272 7, 7,15,15,12,12,13,13,13,13,12,12, 7, 7,13,13,
2273 14, 7, 7, 7, 7, 7,14, 7, 7, 7, 7,15,14,14, 7, 7,
2274 15,15,14,14, 7,15,15,15, 0, 0, 0, 0, 0, 0, 8, 8,
2275 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7,15,15
2276 };
2277
2278 void
2279 fhandler_console::char_command (char c)
2280 {
2281 int x, y, n;
2282 char buf[40];
2283 wchar_t bufw[40];
2284 int r, g, b;
2285
2286 if (wincap.has_con_24bit_colors () && !con_is_legacy)
2287 {
2288 /* For xterm compatible mode */
2289 switch (c)
2290 {
2291 #if 0 /* These sequences, which are supported by real xterm, are
2292 not supported by xterm compatible mode. Therefore they
2293 were implemented once. However, these are not declared
2294 in terminfo of xterm-256color, therefore, do not appear
2295 to be necessary. */
2296 case '`': /* HPA */
2297 if (con.args[0] == 0)
2298 con.args[0] = 1;
2299 cursor_get (&x, &y);
2300 cursor_set (false, con.args[0]-1, y);
2301 break;
2302 case 'a': /* HPR */
2303 if (con.args[0] == 0)
2304 con.args[0] = 1;
2305 cursor_rel (con.args[0], 0);
2306 break;
2307 case 'e': /* VPR */
2308 if (con.args[0] == 0)
2309 con.args[0] = 1;
2310 cursor_rel (0, con.args[0]);
2311 break;
2312 #endif
2313 case 'b': /* REP */
2314 wpbuf.put (c);
2315 if (wincap.has_con_esc_rep ())
2316 /* Just send the sequence */
2317 wpbuf.send (get_output_handle ());
2318 else if (last_char && last_char != L'\n')
2319 {
2320 acquire_attach_mutex (mutex_timeout);
2321 for (int i = 0; i < con.args[0]; i++)
2322 WriteConsoleW (get_output_handle (), &last_char, 1, 0, 0);
2323 release_attach_mutex ();
2324 }
2325 break;
2326 case 'r': /* DECSTBM */
2327 con.scroll_region.Top = con.args[0] ? con.args[0] - 1 : 0;
2328 con.scroll_region.Bottom = con.args[1] ? con.args[1] - 1 : -1;
2329 wpbuf.put (c);
2330 /* Just send the sequence */
2331 wpbuf.send (get_output_handle ());
2332 break;
2333 case 'L': /* IL */
2334 if (wincap.has_con_broken_il_dl ())
2335 {
2336 /* Use "CSI Ps T" instead */
2337 cursor_get (&x, &y);
2338 if (y < srTop || y > srBottom)
2339 break;
2340 if (y == con.b.srWindow.Bottom)
2341 {
2342 acquire_attach_mutex (mutex_timeout);
2343 WriteConsoleW (get_output_handle (), L"\033[2K", 4, 0, 0);
2344 release_attach_mutex ();
2345 break;
2346 }
2347 acquire_attach_mutex (mutex_timeout);
2348 if (y == con.b.srWindow.Top
2349 && srBottom == con.b.srWindow.Bottom)
2350 {
2351 /* Erase scroll down area */
2352 n = con.args[0] ? : 1;
2353 __small_swprintf (bufw, L"\033[%d;1H\033[J\033[%d;%dH",
2354 srBottom - (n-1) - con.b.srWindow.Top + 1,
2355 y + 1 - con.b.srWindow.Top, x + 1);
2356 WriteConsoleW (get_output_handle (),
2357 bufw, wcslen (bufw), 0, 0);
2358 }
2359 __small_swprintf (bufw, L"\033[%d;%dr",
2360 y + 1 - con.b.srWindow.Top,
2361 srBottom + 1 - con.b.srWindow.Top);
2362 WriteConsoleW (get_output_handle (), bufw, wcslen (bufw), 0, 0);
2363 wpbuf.put ('T');
2364 wpbuf.send (get_output_handle ());
2365 __small_swprintf (bufw, L"\033[%d;%dr",
2366 srTop + 1 - con.b.srWindow.Top,
2367 srBottom + 1 - con.b.srWindow.Top);
2368 WriteConsoleW (get_output_handle (), bufw, wcslen (bufw), 0, 0);
2369 __small_swprintf (bufw, L"\033[%d;%dH",
2370 y + 1 - con.b.srWindow.Top, x + 1);
2371 WriteConsoleW (get_output_handle (), bufw, wcslen (bufw), 0, 0);
2372 release_attach_mutex ();
2373 }
2374 else
2375 {
2376 wpbuf.put (c);
2377 /* Just send the sequence */
2378 wpbuf.send (get_output_handle ());
2379 }
2380 break;
2381 case 'M': /* DL */
2382 if (wincap.has_con_broken_il_dl ())
2383 {
2384 /* Use "CSI Ps S" instead */
2385 cursor_get (&x, &y);
2386 if (y < srTop || y > srBottom)
2387 break;
2388 if (y == con.b.srWindow.Bottom)
2389 {
2390 acquire_attach_mutex (mutex_timeout);
2391 WriteConsoleW (get_output_handle (), L"\033[2K", 4, 0, 0);
2392 release_attach_mutex ();
2393 break;
2394 }
2395 __small_swprintf (bufw, L"\033[%d;%dr",
2396 y + 1 - con.b.srWindow.Top,
2397 srBottom + 1 - con.b.srWindow.Top);
2398 acquire_attach_mutex (mutex_timeout);
2399 WriteConsoleW (get_output_handle (), bufw, wcslen (bufw), 0, 0);
2400 wpbuf.put ('S');
2401 wpbuf.send (get_output_handle ());
2402 __small_swprintf (bufw, L"\033[%d;%dr",
2403 srTop + 1 - con.b.srWindow.Top,
2404 srBottom + 1 - con.b.srWindow.Top);
2405 WriteConsoleW (get_output_handle (), bufw, wcslen (bufw), 0, 0);
2406 __small_swprintf (bufw, L"\033[%d;%dH",
2407 y + 1 - con.b.srWindow.Top, x + 1);
2408 WriteConsoleW (get_output_handle (), bufw, wcslen (bufw), 0, 0);
2409 release_attach_mutex ();
2410 }
2411 else
2412 {
2413 wpbuf.put (c);
2414 /* Just send the sequence */
2415 wpbuf.send (get_output_handle ());
2416 }
2417 break;
2418 case 'J': /* ED */
2419 wpbuf.put (c);
2420 if (con.args[0] == 3 && con.savey >= 0)
2421 {
2422 con.fillin (get_output_handle ());
2423 con.savey -= con.b.srWindow.Top;
2424 }
2425 if (con.args[0] == 3 && wincap.has_con_broken_csi3j ())
2426 { /* Workaround for broken CSI3J in Win10 1809 */
2427 CONSOLE_SCREEN_BUFFER_INFO sbi;
2428 acquire_attach_mutex (mutex_timeout);
2429 GetConsoleScreenBufferInfo (get_output_handle (), &sbi);
2430 SMALL_RECT r = {0, sbi.srWindow.Top,
2431 (SHORT) (sbi.dwSize.X - 1), (SHORT) (sbi.dwSize.Y - 1)};
2432 CHAR_INFO f = {' ', sbi.wAttributes};
2433 COORD d = {0, 0};
2434 ScrollConsoleScreenBufferA (get_output_handle (),
2435 &r, NULL, d, &f);
2436 SetConsoleCursorPosition (get_output_handle (), d);
2437 d = sbi.dwCursorPosition;
2438 d.Y -= sbi.srWindow.Top;
2439 SetConsoleCursorPosition (get_output_handle (), d);
2440 release_attach_mutex ();
2441 }
2442 else
2443 /* Just send the sequence */
2444 wpbuf.send (get_output_handle ());
2445 break;
2446 case 'h': /* DECSET */
2447 case 'l': /* DECRST */
2448 wpbuf.put (c);
2449 /* Just send the sequence */
2450 wpbuf.send (get_output_handle ());
2451 if (con.saw_question_mark)
2452 {
2453 bool need_fix_tab_position = false;
2454 for (int i = 0; i < con.nargs; i++)
2455 {
2456 if (con.args[i] == 1049)
2457 {
2458 con.screen_alternated = (c == 'h');
2459 need_fix_tab_position = wincap.has_con_broken_tabs ();
2460 }
2461 if (con.args[i] == 1) /* DECCKM */
2462 con.cursor_key_app_mode = (c == 'h');
2463 }
2464 /* Call fix_tab_position() if screen has been alternated. */
2465 if (need_fix_tab_position)
2466 fix_tab_position (get_output_handle ());
2467 }
2468 break;
2469 case 'p':
2470 if (con.saw_exclamation_mark) /* DECSTR Soft reset */
2471 {
2472 con.scroll_region.Top = 0;
2473 con.scroll_region.Bottom = -1;
2474 con.savex = con.savey = -1;
2475 con.cursor_key_app_mode = false;
2476 }
2477 wpbuf.put (c);
2478 /* Just send the sequence */
2479 wpbuf.send (get_output_handle ());
2480 break;
2481 case 'm':
2482 if (con.saw_greater_than_sign)
2483 break; /* Ignore unsupported CSI > Pm m */
2484 /* Text attribute settings */
2485 wpbuf.put (c);
2486 /* Just send the sequence */
2487 wpbuf.send (get_output_handle ());
2488 break;
2489 default:
2490 /* Other escape sequences */
2491 wpbuf.put (c);
2492 /* Just send the sequence */
2493 wpbuf.send (get_output_handle ());
2494 break;
2495 }
2496 return;
2497 }
2498
2499 /* For legacy cygwin treminal */
2500 switch (c)
2501 {
2502 case 'm': /* Set Graphics Rendition */
2503 for (int i = 0; i < con.nargs; i++)
2504 switch (con.args[i])
2505 {
2506 case 0: /* normal color */
2507 con.set_default_attr ();
2508 break;
2509 case 1: /* bold */
2510 con.intensity = INTENSITY_BOLD;
2511 break;
2512 case 2: /* dim */
2513 con.intensity = INTENSITY_DIM;
2514 break;
2515 case 4: /* underlined */
2516 con.underline = 1;
2517 break;
2518 case 5: /* blink mode */
2519 con.blink = true;
2520 break;
2521 case 7: /* reverse */
2522 con.reverse = true;
2523 break;
2524 case 8: /* invisible */
2525 con.intensity = INTENSITY_INVISIBLE;
2526 break;
2527 case 10: /* end alternate charset */
2528 con.alternate_charset_active = false;
2529 break;
2530 case 11: /* start alternate charset */
2531 con.alternate_charset_active = true;
2532 break;
2533 case 22:
2534 case 28:
2535 con.intensity = INTENSITY_NORMAL;
2536 break;
2537 case 24:
2538 con.underline = false;
2539 break;
2540 case 25:
2541 con.blink = false;
2542 break;
2543 case 27:
2544 con.reverse = false;
2545 break;
2546 case 30: /* BLACK foreground */
2547 con.fg = 0;
2548 break;
2549 case 31: /* RED foreground */
2550 con.fg = FOREGROUND_RED;
2551 break;
2552 case 32: /* GREEN foreground */
2553 con.fg = FOREGROUND_GREEN;
2554 break;
2555 case 33: /* YELLOW foreground */
2556 con.fg = FOREGROUND_RED | FOREGROUND_GREEN;
2557 break;
2558 case 34: /* BLUE foreground */
2559 con.fg = FOREGROUND_BLUE;
2560 break;
2561 case 35: /* MAGENTA foreground */
2562 con.fg = FOREGROUND_RED | FOREGROUND_BLUE;
2563 break;
2564 case 36: /* CYAN foreground */
2565 con.fg = FOREGROUND_BLUE | FOREGROUND_GREEN;
2566 break;
2567 case 37: /* WHITE foreg */
2568 con.fg = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
2569 break;
2570 case 38:
2571 if (con.nargs < i + 2)
2572 /* Sequence error (abort) */
2573 break;
2574 switch (con.args[i + 1])
2575 {
2576 case 2:
2577 if (con.nargs < i + 5)
2578 /* Sequence error (abort) */
2579 break;
2580 r = con.args[i + 2];
2581 g = con.args[i + 3];
2582 b = con.args[i + 4];
2583 r = r < (95 + 1) / 2 ? 0 : r > 255 ? 5 : (r - 55 + 20) / 40;
2584 g = g < (95 + 1) / 2 ? 0 : g > 255 ? 5 : (g - 55 + 20) / 40;
2585 b = b < (95 + 1) / 2 ? 0 : b > 255 ? 5 : (b - 55 + 20) / 40;
2586 con.fg = table256[16 + r*36 + g*6 + b];
2587 i += 4;
2588 break;
2589 case 5:
2590 if (con.nargs < i + 3)
2591 /* Sequence error (abort) */
2592 break;
2593 {
2594 int idx = con.args[i + 2];
2595 if (idx < 0)
2596 idx = 0;
2597 if (idx > 255)
2598 idx = 255;
2599 con.fg = table256[idx];
2600 i += 2;
2601 }
2602 break;
2603 }
2604 break;
2605 case 39:
2606 con.fg = con.default_color & FOREGROUND_ATTR_MASK;
2607 break;
2608 case 40: /* BLACK background */
2609 con.bg = 0;
2610 break;
2611 case 41: /* RED background */
2612 con.bg = BACKGROUND_RED;
2613 break;
2614 case 42: /* GREEN background */
2615 con.bg = BACKGROUND_GREEN;
2616 break;
2617 case 43: /* YELLOW background */
2618 con.bg = BACKGROUND_RED | BACKGROUND_GREEN;
2619 break;
2620 case 44: /* BLUE background */
2621 con.bg = BACKGROUND_BLUE;
2622 break;
2623 case 45: /* MAGENTA background */
2624 con.bg = BACKGROUND_RED | BACKGROUND_BLUE;
2625 break;
2626 case 46: /* CYAN background */
2627 con.bg = BACKGROUND_BLUE | BACKGROUND_GREEN;
2628 break;
2629 case 47: /* WHITE background */
2630 con.bg = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
2631 break;
2632 case 48:
2633 if (con.nargs < i + 2)
2634 /* Sequence error (abort) */
2635 break;
2636 switch (con.args[i + 1])
2637 {
2638 case 2:
2639 if (con.nargs < i + 5)
2640 /* Sequence error (abort) */
2641 break;
2642 r = con.args[i + 2];
2643 g = con.args[i + 3];
2644 b = con.args[i + 4];
2645 r = r < (95 + 1) / 2 ? 0 : r > 255 ? 5 : (r - 55 + 20) / 40;
2646 g = g < (95 + 1) / 2 ? 0 : g > 255 ? 5 : (g - 55 + 20) / 40;
2647 b = b < (95 + 1) / 2 ? 0 : b > 255 ? 5 : (b - 55 + 20) / 40;
2648 con.bg = table256[16 + r*36 + g*6 + b] << 4;
2649 i += 4;
2650 break;
2651 case 5:
2652 if (con.nargs < i + 3)
2653 /* Sequence error (abort) */
2654 break;
2655 {
2656 int idx = con.args[i + 2];
2657 if (idx < 0)
2658 idx = 0;
2659 if (idx > 255)
2660 idx = 255;
2661 con.bg = table256[idx] << 4;
2662 i += 2;
2663 }
2664 break;
2665 }
2666 break;
2667 case 49:
2668 con.bg = con.default_color & BACKGROUND_ATTR_MASK;
2669 break;
2670 }
2671 con.set_color (get_output_handle ());
2672 break;
2673 case 'q': /* Set cursor style (DECSCUSR) */
2674 if (con.saw_space)
2675 {
2676 CONSOLE_CURSOR_INFO console_cursor_info;
2677 acquire_attach_mutex (mutex_timeout);
2678 GetConsoleCursorInfo (get_output_handle (), &console_cursor_info);
2679 switch (con.args[0])
2680 {
2681 case 0: /* blinking block */
2682 case 1: /* blinking block (default) */
2683 case 2: /* steady block */
2684 console_cursor_info.dwSize = 100;
2685 SetConsoleCursorInfo (get_output_handle (),
2686 &console_cursor_info);
2687 break;
2688 case 3: /* blinking underline */
2689 case 4: /* steady underline */
2690 console_cursor_info.dwSize = 10; /* or Windows default 25? */
2691 SetConsoleCursorInfo (get_output_handle (),
2692 &console_cursor_info);
2693 break;
2694 default: /* use value as percentage */
2695 console_cursor_info.dwSize = con.args[0];
2696 SetConsoleCursorInfo (get_output_handle (),
2697 &console_cursor_info);
2698 break;
2699 }
2700 release_attach_mutex ();
2701 }
2702 break;
2703 case 'h':
2704 case 'l':
2705 if (!con.saw_question_mark)
2706 {
2707 switch (con.args[0])
2708 {
2709 case 4: /* Insert mode */
2710 con.insert_mode = (c == 'h') ? true : false;
2711 syscall_printf ("insert mode %sabled",
2712 con.insert_mode ? "en" : "dis");
2713 break;
2714 }
2715 break;
2716 }
2717 switch (con.args[0])
2718 {
2719 case 25: /* Show/Hide Cursor (DECTCEM) */
2720 {
2721 CONSOLE_CURSOR_INFO console_cursor_info;
2722 acquire_attach_mutex (mutex_timeout);
2723 GetConsoleCursorInfo (get_output_handle (), & console_cursor_info);
2724 if (c == 'h')
2725 console_cursor_info.bVisible = TRUE;
2726 else
2727 console_cursor_info.bVisible = FALSE;
2728 SetConsoleCursorInfo (get_output_handle (), & console_cursor_info);
2729 release_attach_mutex ();
2730 break;
2731 }
2732 case 47: /* Save/Restore screen */
2733 con.save_restore (get_output_handle (), c);
2734 break;
2735
2736 case 67: /* DECBKM ("DEC Backarrow Key Mode") */
2737 con.backspace_keycode = (c == 'h' ? CTRL('H') : CERASE);
2738 break;
2739
2740 case 1000: /* Mouse tracking */
2741 con.use_mouse = (c == 'h') ? 1 : 0;
2742 break;
2743
2744 case 1002: /* Mouse button event tracking */
2745 con.use_mouse = (c == 'h') ? 2 : 0;
2746 break;
2747
2748 case 1003: /* Mouse any event tracking */
2749 con.use_mouse = (c == 'h') ? 3 : 0;
2750 break;
2751
2752 case 1004: /* Focus in/out event reporting */
2753 con.use_focus = (c == 'h') ? true : false;
2754 break;
2755
2756 case 1005: /* Extended mouse mode */
2757 con.ext_mouse_mode5 = c == 'h';
2758 break;
2759
2760 case 1006: /* SGR extended mouse mode */
2761 con.ext_mouse_mode6 = c == 'h';
2762 break;
2763
2764 case 1015: /* Urxvt extended mouse mode */
2765 con.ext_mouse_mode15 = c == 'h';
2766 break;
2767
2768 case 2000: /* Raw keyboard mode */
2769 set_raw_win32_keyboard_mode ((c == 'h') ? true : false);
2770 break;
2771
2772 default: /* Ignore */
2773 syscall_printf ("unknown h/l command: %d", con.args[0]);
2774 break;
2775 }
2776 break;
2777 case 'J':
2778 switch (con.args[0])
2779 {
2780 case 0: /* Clear to end of screen */
2781 clear_screen (cl_curr_pos, cl_curr_pos, cl_disp_end, cl_disp_end);
2782 break;
2783 case 1: /* Clear from beginning of screen to cursor */
2784 clear_screen (cl_disp_beg, cl_disp_beg, cl_curr_pos, cl_curr_pos);
2785 break;
2786 case 2: /* Clear screen */
2787 cursor_get (&x, &y);
2788 clear_screen (cl_disp_beg, cl_disp_beg, cl_disp_end, cl_disp_end);
2789 cursor_set (false, x, y);
2790 break;
2791 default:
2792 goto bad_escape;
2793 }
2794 break;
2795
2796 case 'A':
2797 cursor_rel (0, -(con.args[0] ?: 1));
2798 break;
2799 case 'B':
2800 cursor_rel (0, con.args[0] ?: 1);
2801 break;
2802 case 'C':
2803 cursor_rel (con.args[0] ?: 1, 0);
2804 break;
2805 case 'D':
2806 cursor_rel (-(con.args[0] ?: 1),0);
2807 break;
2808 case 'K':
2809 switch (con.args[0])
2810 {
2811 case 0: /* Clear to end of line */
2812 clear_screen (cl_curr_pos, cl_curr_pos, cl_disp_end, cl_curr_pos);
2813 break;
2814 case 2: /* Clear line */
2815 clear_screen (cl_disp_beg, cl_curr_pos, cl_disp_end, cl_curr_pos);
2816 break;
2817 case 1: /* Clear from bol to cursor */
2818 clear_screen (cl_disp_beg, cl_curr_pos, cl_curr_pos, cl_curr_pos);
2819 break;
2820 default:
2821 goto bad_escape;
2822 }
2823 break;
2824 case 'H':
2825 case 'f':
2826 cursor_set (true, (con.args[1] ?: 1) - 1,
2827 (con.args[0] ?: 1) - 1);
2828 break;
2829 case 'G': /* hpa - position cursor at column n - 1 */
2830 cursor_get (&x, &y);
2831 cursor_set (false, (con.args[0] ? con.args[0] - 1 : 0), y);
2832 break;
2833 case 'd': /* vpa - position cursor at line n */
2834 cursor_get (&x, &y);
2835 cursor_set (true, x, (con.args[0] ? con.args[0] - 1 : 0));
2836 break;
2837 case 's': /* Save cursor position */
2838 cursor_get (&con.savex, &con.savey);
2839 con.savey -= con.b.srWindow.Top;
2840 break;
2841 case 'u': /* Restore cursor position */
2842 cursor_set (true, con.savex, con.savey);
2843 break;
2844 case 'I': /* TAB */
2845 cursor_get (&x, &y);
2846 cursor_set (false, 8 * (x / 8 + 1), y);
2847 break;
2848 case 'L': /* AL - insert blank lines */
2849 n = con.args[0] ?: 1;
2850 cursor_get (&x, &y);
2851 scroll_buffer (0, y, -1, -1, 0, y + n);
2852 break;
2853 case 'M': /* DL - delete lines */
2854 n = con.args[0] ?: 1;
2855 cursor_get (&x, &y);
2856 scroll_buffer (0, y + n, -1, -1, 0, y);
2857 break;
2858 case '@': /* IC - insert chars */
2859 n = con.args[0] ?: 1;
2860 cursor_get (&x, &y);
2861 scroll_buffer (x, y, -1, y, x + n, y);
2862 break;
2863 case 'P': /* DC - delete chars */
2864 n = con.args[0] ?: 1;
2865 cursor_get (&x, &y);
2866 scroll_buffer (x + n, y, -1, y, x, y);
2867 break;
2868 case 'S': /* SF - Scroll forward */
2869 n = con.args[0] ?: 1;
2870 scroll_buffer_screen (0, n, -1, -1, 0, 0);
2871 break;
2872 case 'T': /* SR - Scroll down */
2873 con.fillin (get_output_handle ());
2874 n = con.b.srWindow.Top + con.args[0] ?: 1;
2875 scroll_buffer_screen (0, 0, -1, -1, 0, n);
2876 break;
2877 case 'X': /* ec - erase chars */
2878 n = con.args[0] ?: 1;
2879 cursor_get (&x, &y);
2880 scroll_buffer (x + n, y, -1, y, x, y);
2881 scroll_buffer (x, y, -1, y, x + n, y);
2882 break;
2883 case 'Z': /* Back tab */
2884 cursor_get (&x, &y);
2885 cursor_set (false, ((8 * (x / 8 + 1)) - 8), y);
2886 break;
2887 case 'b': /* Repeat char #1 #2 times */
2888 if (con.insert_mode)
2889 {
2890 cursor_get (&x, &y);
2891 scroll_buffer (x, y, -1, y, x + con.args[1], y);
2892 }
2893 while (con.args[1]--)
2894 WriteFile (get_output_handle (), &con.args[0], 1, (DWORD *) &x, 0);
2895 break;
2896 case 'c': /* u9 - Terminal enquire string */
2897 if (con.saw_greater_than_sign)
2898 /* Generate Secondary Device Attribute report, using 67 = ASCII 'C'
2899 to indicate Cygwin (convention used by Rxvt, Urxvt, Screen, Mintty),
2900 and cygwin version for terminal version. */
2901 __small_sprintf (buf, "\033[>67;%d%02d;0c",
2902 CYGWIN_VERSION_DLL_MAJOR, CYGWIN_VERSION_DLL_MINOR);
2903 else
2904 strcpy (buf, "\033[?6c");
2905 /* The generated report needs to be injected for read-ahead into the
2906 fhandler_console object associated with standard input.
2907 So puts_readahead does not work.
2908 Use a common console read-ahead buffer instead. */
2909 acquire_input_mutex (mutex_timeout);
2910 con.cons_rapoi = NULL;
2911 strcpy (con.cons_rabuf, buf);
2912 con.cons_rapoi = con.cons_rabuf;
2913 release_input_mutex ();
2914 /* Wake up read() or select() by sending a message
2915 which has no effect */
2916 PostMessageW (GetConsoleWindow (), WM_SETFOCUS, 0, 0);
2917 break;
2918 case 'n':
2919 switch (con.args[0])
2920 {
2921 case 6: /* u7 - Cursor position request */
2922 cursor_get (&x, &y);
2923 y -= con.b.srWindow.Top;
2924 /* x -= con.b.srWindow.Left; // not available yet */
2925 __small_sprintf (buf, "\033[%d;%dR", y + 1, x + 1);
2926 acquire_input_mutex (mutex_timeout);
2927 con.cons_rapoi = NULL;
2928 strcpy (con.cons_rabuf, buf);
2929 con.cons_rapoi = con.cons_rabuf;
2930 release_input_mutex ();
2931 /* Wake up read() or select() by sending a message
2932 which has no effect */
2933 PostMessageW (GetConsoleWindow (), WM_SETFOCUS, 0, 0);
2934 break;
2935 default:
2936 goto bad_escape;
2937 }
2938 break;
2939 case 'r': /* Set Scroll region */
2940 con.scroll_region.Top = con.args[0] ? con.args[0] - 1 : 0;
2941 con.scroll_region.Bottom = con.args[1] ? con.args[1] - 1 : -1;
2942 cursor_set (true, 0, 0);
2943 break;
2944 case 'g': /* TAB set/clear */
2945 break;
2946 default:
2947 bad_escape:
2948 break;
2949 }
2950 }
2951
2952 #define NUM_REPLACEMENT_CHARS 3
2953
2954 static const wchar_t replacement_char[NUM_REPLACEMENT_CHARS] =
2955 {
2956 0xfffd, /* REPLACEMENT CHARACTER */
2957 0x25a1, /* WHITE SQUARE */
2958 0x2592 /* MEDIUM SHADE */
2959 };
2960 /* nFont member is always 0 so we have to use the facename. */
2961 static WCHAR cons_facename[LF_FACESIZE];
2962 static WCHAR rp_char;
2963 static NO_COPY HDC cdc;
2964
2965 static int CALLBACK
2966 enum_proc (const LOGFONTW *lf, const TEXTMETRICW *tm,
2967 DWORD FontType, LPARAM lParam)
2968 {
2969 int *done = (int *) lParam;
2970 *done = 1;
2971 return 0;
2972 }
2973
2974 static void
2975 check_font (HANDLE hdl)
2976 {
2977 CONSOLE_FONT_INFOEX cfi;
2978 LOGFONTW lf;
2979
2980 cfi.cbSize = sizeof cfi;
2981 acquire_attach_mutex (mutex_timeout);
2982 BOOL r = GetCurrentConsoleFontEx (hdl, 0, &cfi);
2983 release_attach_mutex ();
2984 if (!r)
2985 return;
2986 /* Switched font? */
2987 if (wcscmp (cons_facename, cfi.FaceName) == 0)
2988 return;
2989 if (!cdc && !(cdc = GetDC (GetConsoleWindow ())))
2990 return;
2991 /* Some FaceNames like DejaVu Sans Mono are sometimes returned with stray
2992 trailing chars. Fix it. */
2993 lf.lfCharSet = DEFAULT_CHARSET;
2994 lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
2995 wchar_t *cp = wcpcpy (lf.lfFaceName, cfi.FaceName) - 1;
2996 int done = 0;
2997 do
2998 {
2999 EnumFontFamiliesExW (cdc, &lf, enum_proc, (LPARAM) &done, 0);
3000 if (!done)
3001 *cp-- = L'\0';
3002 }
3003 while (!done && cp >= lf.lfFaceName);
3004 /* What, really? No recognizable font? */
3005 if (!done)
3006 {
3007 rp_char = L'?';
3008 return;
3009 }
3010 /* Yes. Check for the best replacement char. */
3011 HFONT f = CreateFontW (0, 0, 0, 0,
3012 cfi.FontWeight, FALSE, FALSE, FALSE,
3013 DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
3014 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
3015 FIXED_PITCH | FF_DONTCARE, lf.lfFaceName);
3016 if (!f)
3017 return;
3018
3019 HFONT old_f = (HFONT) SelectObject(cdc, f);
3020 if (old_f)
3021 {
3022 WORD glyph_idx[NUM_REPLACEMENT_CHARS];
3023
3024 if (GetGlyphIndicesW (cdc, replacement_char,
3025 NUM_REPLACEMENT_CHARS, glyph_idx,
3026 GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
3027 {
3028 int i;
3029
3030 for (i = 0; i < NUM_REPLACEMENT_CHARS; ++i)
3031 if (glyph_idx[i] != 0xffff)
3032 break;
3033 if (i == NUM_REPLACEMENT_CHARS)
3034 i = 0;
3035 rp_char = replacement_char[i];
3036 /* Note that we copy the original name returned by
3037 GetCurrentConsoleFontEx, even if it was broken.
3038 This allows an early return, rather than to store
3039 the fixed name and then having to enum font families
3040 all over again. */
3041 wcscpy (cons_facename, cfi.FaceName);
3042 }
3043 SelectObject (cdc, old_f);
3044 }
3045 DeleteObject (f);
3046 }
3047
3048 /* This gets called when we found an invalid input character.
3049 Print one of the above Unicode chars as replacement char. */
3050 inline void
3051 fhandler_console::write_replacement_char ()
3052 {
3053 check_font (get_output_handle ());
3054
3055 DWORD done;
3056 acquire_attach_mutex (mutex_timeout);
3057 WriteConsoleW (get_output_handle (), &rp_char, 1, &done, 0);
3058 release_attach_mutex ();
3059 }
3060
3061 const unsigned char *
3062 fhandler_console::write_normal (const unsigned char *src,
3063 const unsigned char *end)
3064 {
3065 /* Scan forward to see what a char which needs special treatment */
3066 DWORD done;
3067 DWORD buf_len;
3068 const unsigned char *found = src;
3069 int ret;
3070 mbstate_t ps;
3071 mbtowc_p f_mbtowc;
3072
3073 /* The alternate charset is always 437, just as in the Linux console. */
3074 f_mbtowc = con.get_console_cp () ? __cp_mbtowc (437) : __MBTOWC;
3075 if (f_mbtowc == __ascii_mbtowc)
3076 f_mbtowc = __utf8_mbtowc;
3077
3078 /* First check if we have cached lead bytes of a former try to write
3079 a truncated multibyte sequence. If so, process it. */
3080 if (trunc_buf.len)
3081 {
3082 const unsigned char *nfound;
3083 int cp_len = MIN (end - src, 4 - trunc_buf.len);
3084 memcpy (trunc_buf.buf + trunc_buf.len, src, cp_len);
3085 memset (&ps, 0, sizeof ps);
3086 switch (ret = f_mbtowc (_REENT, NULL, (const char *) trunc_buf.buf,
3087 trunc_buf.len + cp_len, &ps))
3088 {
3089 case -2:
3090 /* Still truncated multibyte sequence? Keep in trunc_buf. */
3091 trunc_buf.len += cp_len;
3092 return end;
3093 case -1:
3094 /* Give up, print replacement chars for trunc_buf... */
3095 for (int i = 0; i < trunc_buf.len; ++i)
3096 write_replacement_char ();
3097 /* ... mark trunc_buf as unused... */
3098 trunc_buf.len = 0;
3099 /* ... and proceed. */
3100 nfound = NULL;
3101 break;
3102 case 0:
3103 nfound = trunc_buf.buf + 1;
3104 break;
3105 default:
3106 nfound = trunc_buf.buf + ret;
3107 break;
3108 }
3109 /* Valid multibyte sequence? Process. */
3110 if (nfound)
3111 {
3112 buf_len = con.str_to_con (f_mbtowc, write_buf,
3113 (const char *) trunc_buf.buf,
3114 nfound - trunc_buf.buf);
3115 if (!write_console (write_buf, buf_len, done))
3116 {
3117 debug_printf ("multibyte sequence write failed, handle %p",
3118 get_output_handle ());
3119 return 0;
3120 }
3121 found = src + (nfound - trunc_buf.buf - trunc_buf.len);
3122 trunc_buf.len = 0;
3123 return found;
3124 }
3125 }
3126
3127 /* Loop over src buffer as long as we have just simple characters. Stop
3128 as soon as we reach the conversion limit, or if we encounter a control
3129 character or a truncated or invalid mutibyte sequence. */
3130 /* If system has 24 bit color capability, just write all control
3131 sequences to console since xterm compatible mode is enabled. */
3132 memset (&ps, 0, sizeof ps);
3133 while (found < end
3134 && found - src < CONVERT_LIMIT
3135 && base_chars[*found] != IGN
3136 && base_chars[*found] != ESC
3137 && ((wincap.has_con_24bit_colors () && !con_is_legacy)
3138 || base_chars[*found] == NOR))
3139 {
3140 switch (ret = f_mbtowc (_REENT, NULL, (const char *) found,
3141 end - found, &ps))
3142 {
3143 case -2: /* Truncated multibyte sequence. Store for next write. */
3144 trunc_buf.len = end - found;
3145 memcpy (trunc_buf.buf, found, trunc_buf.len);
3146 goto do_print;
3147 case -1: /* Invalid multibyte sequence. Handled below. */
3148 goto do_print;
3149 case 0:
3150 found++;
3151 break;
3152 default:
3153 found += ret;
3154 break;
3155 }
3156 }
3157
3158 do_print:
3159
3160 /* Print all the base characters out */
3161 if (found != src)
3162 {
3163 DWORD len = found - src;
3164 buf_len = con.str_to_con (f_mbtowc, write_buf, (const char *) src, len);
3165 if (!buf_len)
3166 {
3167 debug_printf ("conversion error, handle %p",
3168 get_output_handle ());
3169 __seterrno ();
3170 return 0;
3171 }
3172
3173 if (con.insert_mode)
3174 {
3175 int x, y;
3176 cursor_get (&x, &y);
3177 scroll_buffer (x, y, -1, y, x + buf_len, y);
3178 }
3179
3180 if (!write_console (write_buf, buf_len, done))
3181 {
3182 debug_printf ("write failed, handle %p", get_output_handle ());
3183 return 0;
3184 }
3185 /* Stop here if we reached the conversion limit. */
3186 if (len >= CONVERT_LIMIT)
3187 return found + trunc_buf.len;
3188 }
3189 /* If there's still something in the src buffer, but it's not a truncated
3190 multibyte sequence, then we stumbled over a control character or an
3191 invalid multibyte sequence. Print it. */
3192 if (found < end && trunc_buf.len == 0)
3193 {
3194 int x, y;
3195 switch (base_chars[*found])
3196 {
3197 case SO: /* Shift Out: Invoke G1 character set (ISO 2022) */
3198 con.iso_2022_G1 = true;
3199 break;
3200 case SI: /* Shift In: Invoke G0 character set (ISO 2022) */
3201 con.iso_2022_G1 = false;
3202 break;
3203 case BEL:
3204 beep ();
3205 break;
3206 case ESC:
3207 con.state = gotesc;
3208 wpbuf.put (*found);
3209 break;
3210 case DWN:
3211 cursor_get (&x, &y);
3212 if (y >= srBottom)
3213 {
3214 if (y >= con.b.srWindow.Bottom && !con.scroll_region.Top)
3215 {
3216 acquire_attach_mutex (mutex_timeout);
3217 WriteConsoleW (get_output_handle (), L"\n", 1, &done, 0);
3218 release_attach_mutex ();
3219 }
3220 else
3221 {
3222 scroll_buffer (0, srTop + 1, -1, srBottom, 0, srTop);
3223 y--;
3224 }
3225 }
3226 cursor_set (false,
3227 ((get_ttyp ()->ti.c_oflag & ONLCR) ? 0 : x), y + 1);
3228 break;
3229 case BAK:
3230 cursor_rel (-1, 0);
3231 break;
3232 case IGN:
3233 /* Up to release 3.1.3 we called cursor_rel (1, 0); to move the cursor
3234 one step to the right. However, that neither matches the terminfo
3235 for the cygwin terminal, nor the one for the xterm terminal. */
3236 break;
3237 case CR:
3238 cursor_get (&x, &y);
3239 cursor_set (false, 0, y);
3240 break;
3241 case ERR:
3242 /* Don't print chars marked as ERR chars, except for a ASCII CAN
3243 sequence which is printed as singlebyte chars from the UTF
3244 Basic Latin and Latin 1 Supplement plains. */
3245 if (*found == 0x18)
3246 {
3247 write_replacement_char ();
3248 if (found + 1 < end)
3249 {
3250 ret = __utf8_mbtowc (_REENT, NULL, (const char *) found + 1,
3251 end - found - 1, &ps);
3252 if (ret != -1)
3253 {
3254 acquire_attach_mutex (mutex_timeout);
3255 while (ret-- > 0)
3256 {
3257 WCHAR w = *(found + 1);
3258 WriteConsoleW (get_output_handle (), &w, 1, &done, 0);
3259 found++;
3260 }
3261 release_attach_mutex ();
3262 }
3263 }
3264 }
3265 break;
3266 case TAB:
3267 cursor_get (&x, &y);
3268 cursor_set (false, 8 * (x / 8 + 1), y);
3269 break;
3270 case NOR:
3271 write_replacement_char ();
3272 break;
3273 }
3274 found++;
3275 }
3276 return found + trunc_buf.len;
3277 }
3278
3279 ssize_t __stdcall
3280 fhandler_console::write (const void *vsrc, size_t len)
3281 {
3282 bg_check_types bg = bg_check (SIGTTOU);
3283 if (bg <= bg_eof)
3284 return (ssize_t) bg;
3285
3286 if (get_ttyp ()->ti.c_lflag & FLUSHO)
3287 return len; /* Discard write data */
3288
3289 if (get_ttyp ()->output_stopped && is_nonblocking ())
3290 {
3291 set_errno (EAGAIN);
3292 return -1;
3293 }
3294 while (get_ttyp ()->output_stopped)
3295 cygwait (10);
3296
3297 push_process_state process_state (PID_TTYOU);
3298
3299 acquire_output_mutex (mutex_timeout);
3300
3301 /* Run and check for ansi sequences */
3302 unsigned const char *src = (unsigned char *) vsrc;
3303 unsigned const char *end = src + len;
3304 /* This might look a bit far fetched, but using the TLS path buffer allows
3305 to allocate a big buffer without using the stack too much. Doing it here
3306 in write instead of in write_normal should be faster, too. */
3307 tmp_pathbuf tp;
3308 write_buf = tp.w_get ();
3309
3310 debug_printf ("%p, %ld", vsrc, len);
3311
3312 while (src < end)
3313 {
3314 paranoid_printf ("char %0c state is %d", *src, con.state);
3315 switch (con.state)
3316 {
3317 case normal:
3318 src = write_normal (src, end);
3319 if (!src) /* write_normal failed */
3320 {
3321 release_output_mutex ();
3322 return -1;
3323 }
3324 break;
3325 case gotesc:
3326 if (*src == '[') /* CSI Control Sequence Introducer */
3327 {
3328 wpbuf.put (*src);
3329 con.state = gotsquare;
3330 memset (con.args, 0, sizeof con.args);
3331 con.nargs = 0;
3332 con.saw_question_mark = false;
3333 con.saw_greater_than_sign = false;
3334 con.saw_space = false;
3335 con.saw_exclamation_mark = false;
3336 }
3337 else if (*src == '8') /* DECRC Restore cursor position */
3338 {
3339 if (con.screen_alternated)
3340 {
3341 /* For xterm mode only */
3342 /* Just send the sequence */
3343 wpbuf.put (*src);
3344 wpbuf.send (get_output_handle ());
3345 }
3346 else if (con.savex >= 0 && con.savey >= 0)
3347 cursor_set (false, con.savex, con.savey);
3348 con.state = normal;
3349 wpbuf.empty();
3350 }
3351 else if (*src == '7') /* DECSC Save cursor position */
3352 {
3353 if (con.screen_alternated)
3354 {
3355 /* For xterm mode only */
3356 /* Just send the sequence */
3357 wpbuf.put (*src);
3358 wpbuf.send (get_output_handle ());
3359 }
3360 else
3361 cursor_get (&con.savex, &con.savey);
3362 con.state = normal;
3363 wpbuf.empty();
3364 }
3365 else if (wincap.has_con_24bit_colors () && !con_is_legacy
3366 && wincap.has_con_broken_il_dl () && *src == 'M')
3367 { /* Reverse Index (scroll down) */
3368 int x, y;
3369 cursor_get (&x, &y);
3370 if (y == srTop)
3371 {
3372 if (y == con.b.srWindow.Top
3373 && srBottom == con.b.srWindow.Bottom)
3374 {
3375 /* Erase scroll down area */
3376 wchar_t buf[] = L"\033[32768;1H\033[J\033[32768;32768";
3377 __small_swprintf (buf, L"\033[%d;1H\033[J\033[%d;%dH",
3378 srBottom - con.b.srWindow.Top + 1,
3379 y + 1 - con.b.srWindow.Top, x + 1);
3380 acquire_attach_mutex (mutex_timeout);
3381 WriteConsoleW (get_output_handle (),
3382 buf, wcslen (buf), 0, 0);
3383 release_attach_mutex ();
3384 }
3385 /* Substitute "CSI Ps T" */
3386 wpbuf.put ('[');
3387 wpbuf.put ('T');
3388 }
3389 else
3390 wpbuf.put (*src);
3391 wpbuf.send (get_output_handle ());
3392 con.state = normal;
3393 wpbuf.empty();
3394 }
3395 else if (*src == ']') /* OSC Operating System Command */
3396 {
3397 wpbuf.put (*src);
3398 con.rarg = 0;
3399 con.my_title_buf[0] = '\0';
3400 con.state = gotrsquare;
3401 }
3402 else if (wincap.has_con_24bit_colors () && !con_is_legacy)
3403 {
3404 if (*src == 'c') /* RIS Full reset */
3405 {
3406 con.scroll_region.Top = 0;
3407 con.scroll_region.Bottom = -1;
3408 con.savex = con.savey = -1;
3409 con.cursor_key_app_mode = false;
3410 }
3411 /* ESC sequences below (e.g. OSC, etc) are left to xterm
3412 emulation in xterm compatible mode, therefore, are not
3413 handled and just sent them. */
3414 wpbuf.put (*src);
3415 /* Just send the sequence */
3416 wpbuf.send (get_output_handle ());
3417 con.state = normal;
3418 wpbuf.empty();
3419 }
3420 else if (*src == '(') /* Designate G0 character set */
3421 {
3422 wpbuf.put (*src);
3423 con.state = gotparen;
3424 }
3425 else if (*src == ')') /* Designate G1 character set */
3426 {
3427 wpbuf.put (*src);
3428 con.state = gotrparen;
3429 }
3430 else if (*src == 'M') /* Reverse Index (scroll down) */
3431 {
3432 con.fillin (get_output_handle ());
3433 scroll_buffer_screen (0, 0, -1, -1, 0, 1);
3434 con.state = normal;
3435 wpbuf.empty();
3436 }
3437 else if (*src == 'c') /* RIS Full Reset */
3438 {
3439 con.set_default_attr ();
3440 con.vt100_graphics_mode_G0 = false;
3441 con.vt100_graphics_mode_G1 = false;
3442 con.iso_2022_G1 = false;
3443 cursor_set (false, 0, 0);
3444 clear_screen (cl_buf_beg, cl_buf_beg, cl_buf_end, cl_buf_end);
3445 con.state = normal;
3446 wpbuf.empty();
3447 }
3448 else if (*src == 'R') /* ? */
3449 {
3450 con.state = normal;
3451 wpbuf.empty();
3452 }
3453 else
3454 {
3455 con.state = normal;
3456 wpbuf.empty();
3457 }
3458 src++;
3459 break;
3460 case gotarg1:
3461 if (isdigit (*src))
3462 {
3463 if (con.nargs < MAXARGS)
3464 con.args[con.nargs] = con.args[con.nargs] * 10 + *src - '0';
3465 wpbuf.put (*src);
3466 src++;
3467 }
3468 else if (*src == ';')
3469 {
3470 wpbuf.put (*src);
3471 src++;
3472 if (con.nargs < MAXARGS)
3473 con.nargs++;
3474 }
3475 else if (*src == ' ')
3476 {
3477 wpbuf.put (*src);
3478 src++;
3479 con.saw_space = true;
3480 con.state = gotcommand;
3481 }
3482 else
3483 con.state = gotcommand;
3484 break;
3485 case gotcommand:
3486 if (con.nargs < MAXARGS)
3487 con.nargs++;
3488 char_command (*src++);
3489 con.state = normal;
3490 wpbuf.empty();
3491 break;
3492 case gotrsquare:
3493 if (isdigit (*src))
3494 con.rarg = con.rarg * 10 + (*src - '0');
3495 else if (*src == ';')
3496 {
3497 if (con.rarg == 0 || con.rarg == 2)
3498 con.state = gettitle;
3499 else if ((con.rarg >= 4 && con.rarg <= 6)
3500 || (con.rarg >=10 && con.rarg <= 19)
3501 || (con.rarg >=104 && con.rarg <= 106)
3502 || (con.rarg >=110 && con.rarg <= 119))
3503 con.state = eatpalette;
3504 else
3505 con.state = eattitle;
3506 }
3507 else if (*src == '\033')
3508 con.state = endpalette;
3509 else if (*src == '\007')
3510 {
3511 wpbuf.put (*src);
3512 if (wincap.has_con_24bit_colors () && !con_is_legacy)
3513 wpbuf.send (get_output_handle ());
3514 wpbuf.empty ();
3515 con.state = normal;
3516 src++;
3517 break;
3518 }
3519 wpbuf.put (*src);
3520 src++;
3521 break;
3522 case eattitle:
3523 case gettitle:
3524 {
3525 wpbuf.put (*src);
3526 int n = strlen (con.my_title_buf);
3527 if (*src < ' ')
3528 {
3529 if (wincap.has_con_24bit_colors () && !con_is_legacy)
3530 wpbuf.send (get_output_handle ());
3531 else if (*src == '\007' && con.state == gettitle)
3532 set_console_title (con.my_title_buf);
3533 con.state = normal;
3534 wpbuf.empty();
3535 }
3536 else if (n < TITLESIZE)
3537 {
3538 con.my_title_buf[n++] = *src;
3539 con.my_title_buf[n] = '\0';
3540 }
3541 src++;
3542 break;
3543 }
3544 case eatpalette:
3545 wpbuf.put (*src);
3546 if (*src == '?')
3547 con.saw_question_mark = true;
3548 else if (*src == '\033')
3549 con.state = endpalette;
3550 else if (*src == '\a')
3551 {
3552 /* Send OSC Ps; Pt BEL other than OSC Ps; ? BEL */
3553 if (wincap.has_con_24bit_colors () && !con_is_legacy
3554 && !con.saw_question_mark)
3555 wpbuf.send (get_output_handle ());
3556 con.state = normal;
3557 wpbuf.empty();
3558 }
3559 src++;
3560 break;
3561 case endpalette:
3562 wpbuf.put (*src);
3563 if (*src == '\\')
3564 {
3565 /* Send OSC Ps; Pt ST other than OSC Ps; ? ST */
3566 if (wincap.has_con_24bit_colors () && !con_is_legacy
3567 && !con.saw_question_mark)
3568 wpbuf.send (get_output_handle ());
3569 con.state = normal;
3570 }
3571 else
3572 /* Sequence error (abort) */
3573 con.state = normal;
3574 wpbuf.empty();
3575 src++;
3576 break;
3577 case gotsquare:
3578 if (*src == ';')
3579 {
3580 con.state = gotarg1;
3581 wpbuf.put (*src);
3582 if (con.nargs < MAXARGS)
3583 con.nargs++;
3584 src++;
3585 }
3586 else if (isalpha (*src))
3587 con.state = gotcommand;
3588 else if (*src != '@' && !isalpha (*src) && !isdigit (*src))
3589 {
3590 if (*src == '?')
3591 con.saw_question_mark = true;
3592 else if (*src == '>')
3593 con.saw_greater_than_sign = true;
3594 else if (*src == '!')
3595 con.saw_exclamation_mark = true;
3596 wpbuf.put (*src);
3597 /* ignore any extra chars between [ and first arg or command */
3598 src++;
3599 }
3600 else
3601 con.state = gotarg1;
3602 break;
3603 case gotparen: /* Designate G0 Character Set (ISO 2022) */
3604 if (*src == '0')
3605 con.vt100_graphics_mode_G0 = true;
3606 else
3607 con.vt100_graphics_mode_G0 = false;
3608 con.state = normal;
3609 wpbuf.empty();
3610 src++;
3611 break;
3612 case gotrparen: /* Designate G1 Character Set (ISO 2022) */
3613 if (*src == '0')
3614 con.vt100_graphics_mode_G1 = true;
3615 else
3616 con.vt100_graphics_mode_G1 = false;
3617 con.state = normal;
3618 wpbuf.empty();
3619 src++;
3620 break;
3621 }
3622 }
3623 release_output_mutex ();
3624
3625 syscall_printf ("%ld = fhandler_console::write(...)", len);
3626
3627 return len;
3628 }
3629
3630 void
3631 fhandler_console::doecho (const void *str, DWORD len)
3632 {
3633 bool stopped = get_ttyp ()->output_stopped;
3634 get_ttyp ()->output_stopped = false;
3635 write (str, len);
3636 get_ttyp ()->output_stopped = stopped;
3637 }
3638
3639 static const struct {
3640 int vk;
3641 const char *val[4];
3642 } keytable[] = {
3643 /* NORMAL */ /* SHIFT */ /* CTRL */ /* CTRL-SHIFT */
3644 /* Unmodified and Alt-modified keypad keys comply with linux console
3645 SHIFT, CTRL, CTRL-SHIFT modifiers comply with xterm modifier usage */
3646 {VK_NUMPAD5, {"\033[G", "\033[1;2G", "\033[1;5G", "\033[1;6G"}},
3647 {VK_CLEAR, {"\033[G", "\033[1;2G", "\033[1;5G", "\033[1;6G"}},
3648 {VK_LEFT, {"\033[D", "\033[1;2D", "\033[1;5D", "\033[1;6D"}},
3649 {VK_RIGHT, {"\033[C", "\033[1;2C", "\033[1;5C", "\033[1;6C"}},
3650 {VK_UP, {"\033[A", "\033[1;2A", "\033[1;5A", "\033[1;6A"}},
3651 {VK_DOWN, {"\033[B", "\033[1;2B", "\033[1;5B", "\033[1;6B"}},
3652 {VK_PRIOR, {"\033[5~", "\033[5;2~", "\033[5;5~", "\033[5;6~"}},
3653 {VK_NEXT, {"\033[6~", "\033[6;2~", "\033[6;5~", "\033[6;6~"}},
3654 {VK_HOME, {"\033[1~", "\033[1;2~", "\033[1;5~", "\033[1;6~"}},
3655 {VK_END, {"\033[4~", "\033[4;2~", "\033[4;5~", "\033[4;6~"}},
3656 {VK_INSERT, {"\033[2~", "\033[2;2~", "\033[2;5~", "\033[2;6~"}},
3657 {VK_DELETE, {"\033[3~", "\033[3;2~", "\033[3;5~", "\033[3;6~"}},
3658 /* F1...F12, SHIFT-F1...SHIFT-F10 comply with linux console
3659 F6...F12, and all modified F-keys comply with rxvt (compatible extension) */
3660 {VK_F1, {"\033[[A", "\033[23~", "\033[11^", "\033[23^"}},
3661 {VK_F2, {"\033[[B", "\033[24~", "\033[12^", "\033[24^"}},
3662 {VK_F3, {"\033[[C", "\033[25~", "\033[13^", "\033[25^"}},
3663 {VK_F4, {"\033[[D", "\033[26~", "\033[14^", "\033[26^"}},
3664 {VK_F5, {"\033[[E", "\033[28~", "\033[15^", "\033[28^"}},
3665 {VK_F6, {"\033[17~", "\033[29~", "\033[17^", "\033[29^"}},
3666 {VK_F7, {"\033[18~", "\033[31~", "\033[18^", "\033[31^"}},
3667 {VK_F8, {"\033[19~", "\033[32~", "\033[19^", "\033[32^"}},
3668 {VK_F9, {"\033[20~", "\033[33~", "\033[20^", "\033[33^"}},
3669 {VK_F10, {"\033[21~", "\033[34~", "\033[21^", "\033[34^"}},
3670 {VK_F11, {"\033[23~", "\033[23$", "\033[23^", "\033[23@"}},
3671 {VK_F12, {"\033[24~", "\033[24$", "\033[24^", "\033[24@"}},
3672 /* CTRL-6 complies with Windows cmd console but should be fixed */
3673 {'6', {NULL, NULL, "\036", NULL}},
3674 /* Table end marker */
3675 {0}
3676 };
3677
3678 const char *
3679 fhandler_console::get_nonascii_key (INPUT_RECORD& input_rec, char *tmp)
3680 {
3681 #define NORMAL 0
3682 #define SHIFT 1
3683 #define CONTROL 2
3684 /*#define CONTROLSHIFT 3*/
3685
3686 int modifier_index = NORMAL;
3687 if (input_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)
3688 modifier_index = SHIFT;
3689 if (input_rec.Event.KeyEvent.dwControlKeyState & CTRL_PRESSED)
3690 modifier_index += CONTROL;
3691
3692 for (int i = 0; keytable[i].vk; i++)
3693 if (input_rec.Event.KeyEvent.wVirtualKeyCode == keytable[i].vk)
3694 {
3695 if ((input_rec.Event.KeyEvent.dwControlKeyState & ALT_PRESSED)
3696 && keytable[i].val[modifier_index] != NULL)
3697 { /* Generic ESC prefixing if Alt is pressed */
3698 tmp[0] = '\033';
3699 strcpy (tmp + 1, keytable[i].val[modifier_index]);
3700 return tmp;
3701 }
3702 else
3703 return keytable[i].val[modifier_index];
3704 }
3705
3706 if (input_rec.Event.KeyEvent.uChar.AsciiChar)
3707 {
3708 tmp[0] = input_rec.Event.KeyEvent.uChar.AsciiChar;
3709 tmp[1] = '\0';
3710 return tmp;
3711 }
3712 return NULL;
3713 }
3714
3715 int
3716 fhandler_console::init (HANDLE h, DWORD a, mode_t bin)
3717 {
3718 // this->fhandler_termios::init (h, mode, bin);
3719 /* Ensure both input and output console handles are open */
3720 int flags = 0;
3721
3722 a &= GENERIC_READ | GENERIC_WRITE;
3723 if (a == GENERIC_READ)
3724 flags = O_RDONLY;
3725 if (a == GENERIC_WRITE)
3726 flags = O_WRONLY;
3727 if (a == (GENERIC_READ | GENERIC_WRITE))
3728 flags = O_RDWR;
3729 open_with_arch (flags | O_BINARY | (h ? 0 : O_NOCTTY));
3730
3731 return !tcsetattr (0, &get_ttyp ()->ti);
3732 }
3733
3734 int
3735 fhandler_console::igncr_enabled ()
3736 {
3737 return get_ttyp ()->ti.c_iflag & IGNCR;
3738 }
3739
3740 void
3741 fhandler_console::set_close_on_exec (bool val)
3742 {
3743 close_on_exec (val);
3744 }
3745
3746 void __stdcall
3747 set_console_title (char *title)
3748 {
3749 wchar_t buf[TITLESIZE + 1];
3750 sys_mbstowcs (buf, TITLESIZE + 1, title);
3751 lock_ttys here (15000);
3752 acquire_attach_mutex (mutex_timeout);
3753 SetConsoleTitleW (buf);
3754 release_attach_mutex ();
3755 debug_printf ("title '%W'", buf);
3756 }
3757
3758 static bool NO_COPY gdb_inferior_noncygwin = false;
3759
3760 void
3761 fhandler_console::set_console_mode_to_native ()
3762 {
3763 /* Setting-up console mode for non-cygwin app started by GDB. This is
3764 called from hooked CreateProcess() and ContinueDebugEvent(). */
3765 cygheap_fdenum cfd (false);
3766 while (cfd.next () >= 0)
3767 if (cfd->get_major () == DEV_CONS_MAJOR)
3768 {
3769 fhandler_console *cons = (fhandler_console *) (fhandler_base *) cfd;
3770 if (cons->get_device () == cons->tc ()->getntty ())
3771 {
3772 termios *cons_ti = &cons->tc ()->ti;
3773 set_input_mode (tty::native, cons_ti, cons->get_handle_set ());
3774 set_output_mode (tty::native, cons_ti, cons->get_handle_set ());
3775 con.disable_master_thread = true;
3776 break;
3777 }
3778 }
3779 }
3780
3781 #define DEF_HOOK(name) static __typeof__ (name) *name##_Orig
3782 /* CreateProcess() is hooked for GDB etc. */
3783 DEF_HOOK (CreateProcessA);
3784 DEF_HOOK (CreateProcessW);
3785 DEF_HOOK (ContinueDebugEvent);
3786
3787 static BOOL WINAPI
3788 CreateProcessA_Hooked
3789 (LPCSTR n, LPSTR c, LPSECURITY_ATTRIBUTES pa, LPSECURITY_ATTRIBUTES ta,
3790 BOOL inh, DWORD f, LPVOID e, LPCSTR d,
3791 LPSTARTUPINFOA si, LPPROCESS_INFORMATION pi)
3792 {
3793 if (f & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
3794 mutex_timeout = 0; /* to avoid deadlock in GDB */
3795 gdb_inferior_noncygwin = !fhandler_termios::path_iscygexec_a (n, c);
3796 if (gdb_inferior_noncygwin)
3797 fhandler_console::set_console_mode_to_native ();
3798 init_console_handler (false);
3799 return CreateProcessA_Orig (n, c, pa, ta, inh, f, e, d, si, pi);
3800 }
3801
3802 static BOOL WINAPI
3803 CreateProcessW_Hooked
3804 (LPCWSTR n, LPWSTR c, LPSECURITY_ATTRIBUTES pa, LPSECURITY_ATTRIBUTES ta,
3805 BOOL inh, DWORD f, LPVOID e, LPCWSTR d,
3806 LPSTARTUPINFOW si, LPPROCESS_INFORMATION pi)
3807 {
3808 if (f & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
3809 mutex_timeout = 0; /* to avoid deadlock in GDB */
3810 gdb_inferior_noncygwin = !fhandler_termios::path_iscygexec_w (n, c);
3811 if (gdb_inferior_noncygwin)
3812 fhandler_console::set_console_mode_to_native ();
3813 init_console_handler (false);
3814 return CreateProcessW_Orig (n, c, pa, ta, inh, f, e, d, si, pi);
3815 }
3816
3817 static BOOL WINAPI
3818 ContinueDebugEvent_Hooked
3819 (DWORD p, DWORD t, DWORD s)
3820 {
3821 if (gdb_inferior_noncygwin)
3822 fhandler_console::set_console_mode_to_native ();
3823 init_console_handler (false);
3824 return ContinueDebugEvent_Orig (p, t, s);
3825 }
3826
3827 void
3828 fhandler_console::fixup_after_fork_exec (bool execing)
3829 {
3830 set_unit ();
3831 setup_io_mutex ();
3832
3833 if (!execing)
3834 return;
3835
3836 #define DO_HOOK(module, name) \
3837 if (!name##_Orig) \
3838 { \
3839 void *api = hook_api (module, #name, (void *) name##_Hooked); \
3840 name##_Orig = (__typeof__ (name) *) api; \
3841 /*if (api) system_printf (#name " hooked.");*/ \
3842 }
3843 /* CreateProcess() is hooked for GDB etc. */
3844 DO_HOOK (NULL, CreateProcessA);
3845 DO_HOOK (NULL, CreateProcessW);
3846 DO_HOOK (NULL, ContinueDebugEvent);
3847 }
3848
3849 /* Ugly workaround to create invisible console required since Windows 7.
3850
3851 First try to just attach to any console which may have started this
3852 app. If that works use this as our "invisible console".
3853
3854 This will fail if not started from the command prompt. In that case, start
3855 a dummy console application in a hidden state so that we can use its console
3856 as our invisible console. This probably works everywhere but process
3857 creation is slow and to be avoided if possible so the window station method
3858 is vastly preferred.
3859
3860 FIXME: This is not completely thread-safe since it creates two inheritable
3861 handles which are known only to this function. If another thread starts
3862 a process the new process will inherit these handles. However, since this
3863 function is currently only called at startup and during exec, it shouldn't
3864 be a big deal. */
3865 bool
3866 fhandler_console::create_invisible_console_workaround (bool force)
3867 {
3868 /* If force is set, avoid to reattach to existing console. */
3869 if (force || !AttachConsole (-1))
3870 {
3871 bool taskbar;
3872 DWORD err = force ? 0 : GetLastError ();
3873 path_conv helper ("/bin/cygwin-console-helper.exe");
3874 HANDLE hello = NULL;
3875 HANDLE goodbye = NULL;
3876 /* If err == ERROR_PROC_FOUND then this method won't work. But that's
3877 ok. The window station method should work ok when AttachConsole doesn't
3878 work.
3879
3880 If the helper doesn't exist or we can't create event handles then we
3881 can't use this method. */
3882 if (err == ERROR_PROC_NOT_FOUND || !helper.exists ()
3883 || !(hello = CreateEvent (&sec_none, true, false, NULL))
3884 || !(goodbye = CreateEvent (&sec_none, true, false, NULL)))
3885 {
3886 AllocConsole (); /* This is just sanity check code. We should
3887 never actually hit here unless we're running
3888 in an environment which lacks the helper
3889 app. */
3890 taskbar = true;
3891 }
3892 else
3893 {
3894 STARTUPINFOW si = {};
3895 PROCESS_INFORMATION pi;
3896 size_t len = helper.get_wide_win32_path_len ();
3897 WCHAR cmd[len + 1];
3898 WCHAR args[len + 1 + (2 * sizeof (" 0xffffffffffffffff")) + 1];
3899 WCHAR title[] = L"invisible cygwin console";
3900
3901 /* Create a new hidden process. Use the two event handles as
3902 argv[1] and argv[2]. */
3903
3904 helper.get_wide_win32_path (cmd);
3905 __small_swprintf (args, L"\"%W\" %p %p", cmd, hello, goodbye);
3906
3907 si.cb = sizeof (si);
3908 si.dwFlags = STARTF_USESHOWWINDOW;
3909 si.wShowWindow = SW_HIDE;
3910 si.lpTitle = title;
3911
3912 BOOL x = CreateProcessW (cmd, args,
3913 &sec_none_nih, &sec_none_nih, true,
3914 CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
3915 if (x)
3916 {
3917 CloseHandle (pi.hProcess); /* Don't need */
3918 CloseHandle (pi.hThread); /* these. */
3919 }
3920 taskbar = false;
3921 /* Wait for subprocess to indicate that it is live. This may not
3922 actually be needed but it's hard to say since it is possible that
3923 there will be no console for a brief time after the process
3924 returns and there is no easy way to determine if/when this happens
3925 in Windows. So play it safe. */
3926 if (!x || (WaitForSingleObject (hello, 10000) != WAIT_OBJECT_0)
3927 || !AttachConsole (pi.dwProcessId))
3928 AllocConsole (); /* Oh well. Watch the flash. */
3929 }
3930
3931 if (!taskbar)
3932 /* Setting the owner of the console window to HWND_MESSAGE seems to
3933 hide it from the taskbar. Don't know if this method is faster than
3934 calling ShowWindowAsync but it should guarantee no taskbar presence
3935 for the hidden console. */
3936 SetParent (GetConsoleWindow (), HWND_MESSAGE);
3937 if (hello)
3938 CloseHandle (hello);
3939 if (goodbye)
3940 {
3941 SetEvent (goodbye); /* Tell helper process it's ok to exit. */
3942 CloseHandle (goodbye);
3943 }
3944 }
3945 return invisible_console = true;
3946 }
3947
3948 void
3949 fhandler_console::free_console ()
3950 {
3951 BOOL res = FreeConsole ();
3952 debug_printf ("freed console, res %d", res);
3953 init_console_handler (false);
3954 }
3955
3956 bool
3957 fhandler_console::need_invisible (bool force)
3958 {
3959 BOOL b = false;
3960 /* If force is set, forcibly create a new invisible console
3961 even if a console device already exists. */
3962 if (exists () && !force)
3963 invisible_console = false;
3964 else
3965 {
3966 HWINSTA h;
3967 /* The intent here is to allocate an "invisible" console if we have no
3968 controlling tty or to reuse the existing console if we already have
3969 a tty. So, first get the old window station. If there is no controlling
3970 terminal, create a new window station and then set it as the current
3971 window station. The subsequent AllocConsole will then be allocated
3972 invisibly. But, after doing that we have to restore any existing windows
3973 station or, strangely, characters will not be displayed in any windows
3974 drawn on the current screen. We only do this if we have changed to
3975 a new window station and if we had an existing windows station previously.
3976 We also close the previously opened window station even though AllocConsole
3977 is now "using" it. This doesn't seem to cause any problems.
3978
3979 Things to watch out for if you make changes in this code:
3980
3981 - Flashing, black consoles showing up when you start, e.g., ssh in
3982 an xterm.
3983 - Non-displaying of characters in rxvt or xemacs if you start a
3984 process using setsid: bash -lc "setsid rxvt". */
3985
3986 h = GetProcessWindowStation ();
3987
3988 USEROBJECTFLAGS oi;
3989 DWORD len;
3990 if (!h
3991 || !GetUserObjectInformationW (h, UOI_FLAGS, &oi, sizeof (oi), &len)
3992 || !(oi.dwFlags & WSF_VISIBLE))
3993 {
3994 b = true;
3995 debug_printf ("window station is not visible");
3996 AllocConsole ();
3997 invisible_console = true;
3998 }
3999 b = create_invisible_console_workaround (force);
4000 }
4001
4002 debug_printf ("invisible_console %d", invisible_console);
4003 return b;
4004 }
4005
4006 DWORD
4007 fhandler_console::__acquire_input_mutex (const char *fn, int ln, DWORD ms)
4008 {
4009 #ifdef DEBUGGING
4010 strace.prntf (_STRACE_TERMIOS, fn, "(%d): trying to get input_mutex", ln);
4011 #endif
4012 DWORD res = WaitForSingleObject (input_mutex, ms);
4013 if (res != WAIT_OBJECT_0)
4014 strace.prntf (_STRACE_TERMIOS, fn,
4015 "(%d): Failed to acquire input_mutex %08x",
4016 ln, GetLastError ());
4017 #ifdef DEBUGGING
4018 else
4019 strace.prntf (_STRACE_TERMIOS, fn, "(%d): got input_mutex", ln);
4020 #endif
4021 return res;
4022 }
4023
4024 void
4025 fhandler_console::__release_input_mutex (const char *fn, int ln)
4026 {
4027 ReleaseMutex (input_mutex);
4028 #ifdef DEBUGGING
4029 strace.prntf (_STRACE_TERMIOS, fn, "(%d): release input_mutex", ln);
4030 #endif
4031 }
4032
4033 DWORD
4034 fhandler_console::__acquire_output_mutex (const char *fn, int ln, DWORD ms)
4035 {
4036 #ifdef DEBUGGING
4037 strace.prntf (_STRACE_TERMIOS, fn, "(%d): trying to get output_mutex", ln);
4038 #endif
4039 DWORD res = WaitForSingleObject (output_mutex, ms);
4040 if (res != WAIT_OBJECT_0)
4041 strace.prntf (_STRACE_TERMIOS, fn,
4042 "(%d): Failed to acquire output_mutex %08x",
4043 ln, GetLastError ());
4044 #ifdef DEBUGGING
4045 else
4046 strace.prntf (_STRACE_TERMIOS, fn, "(%d): got output_mutex", ln);
4047 #endif
4048 return res;
4049 }
4050
4051 void
4052 fhandler_console::__release_output_mutex (const char *fn, int ln)
4053 {
4054 ReleaseMutex (output_mutex);
4055 #ifdef DEBUGGING
4056 strace.prntf (_STRACE_TERMIOS, fn, "(%d): release output_mutex", ln);
4057 #endif
4058 }
4059
4060 void
4061 fhandler_console::get_duplicated_handle_set (handle_set_t *p)
4062 {
4063 DuplicateHandle (GetCurrentProcess (), get_handle (),
4064 GetCurrentProcess (), &p->input_handle,
4065 0, FALSE, DUPLICATE_SAME_ACCESS);
4066 DuplicateHandle (GetCurrentProcess (), get_output_handle (),
4067 GetCurrentProcess (), &p->output_handle,
4068 0, FALSE, DUPLICATE_SAME_ACCESS);
4069 DuplicateHandle (GetCurrentProcess (), input_mutex,
4070 GetCurrentProcess (), &p->input_mutex,
4071 0, FALSE, DUPLICATE_SAME_ACCESS);
4072 DuplicateHandle (GetCurrentProcess (), output_mutex,
4073 GetCurrentProcess (), &p->output_mutex,
4074 0, FALSE, DUPLICATE_SAME_ACCESS);
4075 }
4076
4077 /* The function close_handle_set() should be static so that they can
4078 be called even after the fhandler_console instance is deleted. */
4079 void
4080 fhandler_console::close_handle_set (handle_set_t *p)
4081 {
4082 CloseHandle (p->input_handle);
4083 p->input_handle = NULL;
4084 CloseHandle (p->output_handle);
4085 p->output_handle = NULL;
4086 CloseHandle (p->input_mutex);
4087 p->input_mutex = NULL;
4088 CloseHandle (p->output_mutex);
4089 p->output_mutex = NULL;
4090 }
4091
4092 bool
4093 fhandler_console::need_console_handler ()
4094 {
4095 return con.disable_master_thread;
4096 }
This page took 0.213543 seconds and 4 git commands to generate.