3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
10 #include "miscfuncs.h"
15 #include <sys/param.h>
16 #include <sys/cygwin.h>
17 #include <cygwin/kd.h>
27 #include "shared_info.h"
31 #include <asm/socket.h>
33 #include "child_info.h"
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
41 #define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
42 #define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
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)
51 #define CONS_THREAD_SYNC "cygcons.thread_sync"
52 static bool NO_COPY master_thread_started
= false;
54 const unsigned fhandler_console::MAX_WRITE_CHARS
= 16384;
56 fhandler_console::console_state NO_COPY
*fhandler_console::shared_console_info
;
58 bool NO_COPY
fhandler_console::invisible_console
;
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
;
64 /* Write pending buffer for ESC sequence handling
65 in xterm compatible mode */
66 static wchar_t last_char
;
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
73 static const size_t WPBUF_LEN
= 256u;
77 inline void put (char x
)
79 if (ixput
< WPBUF_LEN
)
82 inline void empty () { ixput
= 0u; }
83 inline void send (HANDLE
&handle
)
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 ();
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
);
103 PWCHAR ding_path
= tp
.w_get ();
104 wcpcpy (wcpcpy (ding_path
, windows_directory
), ding
);
105 r
.set_string (L
"", ding_path
);
110 fhandler_console::console_state
*
111 fhandler_console::open_shared_console (HWND hw
, HANDLE
& h
, bool& create
)
113 wchar_t namebuf
[(sizeof "XXXXXXXXXXXXXXXXXX-consNNNNNNNNNN")];
114 __small_swprintf (namebuf
, L
"%S-cons%p", &cygheap
->installation_key
, hw
);
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
;
126 unsigned long bitmask
;
130 operator int () const {return n
;}
132 friend BOOL CALLBACK
enum_windows (HWND
, LPARAM
);
136 enum_windows (HWND hw
, LPARAM lp
)
138 console_unit
*this1
= (console_unit
*) lp
;
142 fhandler_console::console_state
*cs
;
143 if ((cs
= fhandler_console::open_shared_console (hw
, h
)))
145 this1
->bitmask
^= 1 << cs
->tty_min_state
.getntty ();
146 UnmapViewOfFile ((void *) cs
);
152 console_unit::console_unit (HWND me0
):
153 bitmask (0xffffffff), me (me0
)
155 EnumWindows (enum_windows
, (LPARAM
) this);
156 n
= (_minor_t
) ffs (bitmask
) - 1;
158 api_fatal ("console device allocation failure - too many consoles in use, max consoles is 32");
162 cons_master_thread (VOID
*arg
)
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
);
183 /* Compare two INPUT_RECORD sequences */
185 inrec_eq (const INPUT_RECORD
*a
, const INPUT_RECORD
*b
, DWORD n
)
187 for (DWORD i
= 0; i
< n
; i
++)
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
)
200 else if (memcmp (a
+ i
, b
+ i
, sizeof (INPUT_RECORD
)) != 0)
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(). */
212 fhandler_console::cons_master_thread (handle_set_t
*p
, tty
*ttyp
)
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
;
220 inline static size_t bytes (size_t n
)
222 return sizeof (INPUT_RECORD
) * n
;
225 termios
&ti
= ttyp
->ti
;
226 while (con
.owner
== myself
->pid
)
228 DWORD total_read
, n
, i
;
229 INPUT_RECORD input_rec
[inrec_size
];
231 if (con
.disable_master_thread
)
237 WaitForSingleObject (p
->input_mutex
, mutex_timeout
);
240 switch (cygwait (p
->input_handle
, (DWORD
) 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
)
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
;
257 release_attach_mutex ();
260 con
.num_processed
= 0;
265 ReleaseMutex (p
->input_mutex
);
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
)
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
)
278 con
.scroll_region
.Top
= 0;
279 con
.scroll_region
.Bottom
= -1;
280 ttyp
->kill_pgrp (SIGWINCH
);
283 for (i
= con
.num_processed
; i
< total_read
; i
++)
287 bool processed
= false;
288 switch (input_rec
[i
].EventType
)
291 if (!input_rec
[i
].Event
.KeyEvent
.bKeyDown
)
293 wc
= input_rec
[i
].Event
.KeyEvent
.uChar
.UnicodeChar
;
294 if (!wc
|| (wint_t) wc
>= 0x80)
297 switch (process_sigs (c
, ttyp
, NULL
))
300 case not_signalled_but_done
:
301 case done_with_debugger
:
303 ttyp
->output_stopped
= false;
304 if (ti
.c_lflag
& NOFLSH
)
306 con
.num_processed
= 0;
308 default: /* not signalled */
311 processed
= process_stop_start (c
, ttyp
);
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
)
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
);
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));
339 con
.num_processed
= total_read
;
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 ();
352 break; /* Someone has read input without acquiring
353 input_mutex. ConEmu cygwin-connector? */
354 if (inrec_eq (input_rec
, tmp
, total_read
))
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))
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 ();
370 input_rec
[total_read
+ j
- i
] = tmp
[j
];
379 ReleaseMutex (p
->input_mutex
);
386 fhandler_console::set_unit ()
392 fh_devices this_unit
= dev ();
393 bool generic_console
= this_unit
== FH_CONIN
|| this_unit
== FH_CONOUT
;
394 if (shared_console_info
)
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
400 || this_unit
== FH_TTY
) ?
401 shared_unit
: FH_ERROR
;
404 else if ((!generic_console
&&
405 (myself
->ctty
!= -1 && !iscons_dev (myself
->ctty
)))
406 || !(me
= GetConsoleWindow ()))
411 shared_console_info
=
412 open_shared_console (me
, cygheap
->console_h
, created
);
413 ProtectHandleINH (cygheap
->console_h
);
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 ();
419 con
.owner
= myself
->pid
;
421 if (!created
&& shared_console_info
)
423 while (con
.owner
> MAX_PID
)
427 con
.owner
= myself
->pid
;
430 dev ().parse (devset
);
431 if (devset
!= FH_ERROR
)
432 pc
.file_attributes (FILE_ATTRIBUTE_NORMAL
);
436 set_output_handle (NULL
);
442 /* Allocate and initialize the shared record for the current console. */
444 fhandler_console::setup ()
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;
482 fhandler_console::rabuf ()
488 fhandler_console::ralen ()
494 fhandler_console::raixget ()
496 return con_ra
.raixget
;
500 fhandler_console::raixput ()
502 return con_ra
.raixput
;
506 fhandler_console::rabuflen ()
508 return con_ra
.rabuflen
;
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. */
514 fhandler_console::set_input_mode (tty::cons_mode m
, const termios
*t
,
515 const handle_set_t
*p
)
518 WaitForSingleObject (p
->input_mutex
, mutex_timeout
);
519 acquire_attach_mutex (mutex_timeout
);
520 GetConsoleMode (p
->input_handle
, &oflags
);
522 & (ENABLE_EXTENDED_FLAGS
| ENABLE_INSERT_MODE
| ENABLE_QUICK_EDIT_MODE
);
526 flags
|= ENABLE_ECHO_INPUT
| ENABLE_LINE_INPUT
| ENABLE_PROCESSED_INPUT
;
529 flags
|= ENABLE_WINDOW_INPUT
;
530 if (wincap
.has_con_24bit_colors () && !con_is_legacy
)
531 flags
|= ENABLE_VIRTUAL_TERMINAL_INPUT
;
533 flags
|= ENABLE_MOUSE_INPUT
;
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
;
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);
556 release_attach_mutex ();
557 ReleaseMutex (p
->input_mutex
);
561 fhandler_console::set_output_mode (tty::cons_mode m
, const termios
*t
,
562 const handle_set_t
*p
)
564 DWORD flags
= ENABLE_PROCESSED_OUTPUT
| ENABLE_WRAP_AT_EOL_OUTPUT
;
565 WaitForSingleObject (p
->output_mutex
, mutex_timeout
);
571 if (wincap
.has_con_24bit_colors () && !con_is_legacy
)
572 flags
|= ENABLE_VIRTUAL_TERMINAL_PROCESSING
;
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
;
580 acquire_attach_mutex (mutex_timeout
);
581 SetConsoleMode (p
->output_handle
, flags
);
582 release_attach_mutex ();
583 ReleaseMutex (p
->output_mutex
);
587 fhandler_console::setup_for_non_cygwin_app ()
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;
602 fhandler_console::cleanup_for_non_cygwin_app (handle_set_t
*p
)
604 termios dummy
= {0, };
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
);
617 /* Return the tty structure associated with a given tty number. If the
618 tty number is < 0, just return a dummy record. */
620 tty_list::get_cttyp ()
622 dev_t n
= myself
->ctty
;
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
)];
633 fhandler_console::setup_io_mutex (void)
639 if (!input_mutex
|| WAIT_FAILED
== (res
= acquire_input_mutex (0)))
641 shared_name (buf
, "cygcons.input.mutex", get_minor ());
642 input_mutex
= OpenMutex (MAXIMUM_ALLOWED
, TRUE
, buf
);
644 input_mutex
= CreateMutex (&sec_none
, FALSE
, buf
);
651 if (res
== WAIT_OBJECT_0
)
652 release_input_mutex ();
655 if (!output_mutex
|| WAIT_FAILED
== (res
= acquire_output_mutex (0)))
657 shared_name (buf
, "cygcons.output.mutex", get_minor ());
658 output_mutex
= OpenMutex (MAXIMUM_ALLOWED
, TRUE
, buf
);
660 output_mutex
= CreateMutex (&sec_none
, FALSE
, buf
);
667 if (res
== WAIT_OBJECT_0
)
668 release_output_mutex ();
672 dev_console::con_to_str (char *d
, int dlen
, WCHAR w
)
674 return sys_wcstombs (d
, dlen
, &w
, 1);
678 dev_console::get_console_cp ()
680 /* The alternate charset is always 437, just as in the Linux console. */
681 return alternate_charset_active
? 437 : 0;
685 dev_console::str_to_con (mbtowc_p f_mbtowc
, PWCHAR d
, const char *s
, DWORD sz
)
687 return _sys_mbstowcs (f_mbtowc
, d
, CONVERT_LIMIT
, s
, sz
);
691 fhandler_console::set_raw_win32_keyboard_mode (bool new_mode
)
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");
701 fhandler_console::set_cursor_maybe ()
703 con
.fillin (get_output_handle ());
704 /* Nothing to do for xterm compatible mode. */
705 if (wincap
.has_con_24bit_colors () && !con_is_legacy
)
707 if (con
.dwLastCursorPosition
.X
!= con
.b
.dwCursorPosition
.X
||
708 con
.dwLastCursorPosition
.Y
!= con
.b
.dwCursorPosition
.Y
)
710 acquire_attach_mutex (mutex_timeout
);
711 SetConsoleCursorPosition (get_output_handle (), con
.b
.dwCursorPosition
);
712 release_attach_mutex ();
713 con
.dwLastCursorPosition
= con
.b
.dwCursorPosition
;
717 /* Workaround for a bug of windows xterm compatible mode. */
718 /* The horizontal tab positions are broken after resize. */
720 fhandler_console::fix_tab_position (HANDLE h
)
722 /* Re-setting ENABLE_VIRTUAL_TERMINAL_PROCESSING
723 fixes the tab position. */
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 ();
733 fhandler_console::send_winch_maybe ()
735 SHORT y
= con
.dwWinSize
.Y
;
736 SHORT x
= con
.dwWinSize
.X
;
737 con
.fillin (get_output_handle ());
739 if (y
!= con
.dwWinSize
.Y
|| x
!= con
.dwWinSize
.X
)
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
);
752 /* Check whether a mouse event is to be reported as an escape sequence */
754 fhandler_console::mouse_aware (MOUSE_EVENT_RECORD
& mouse_event
)
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 ();
766 /* Cannot adjust position by window scroll buffer offset */
769 con
.dwMousePosition
.X
= mouse_event
.dwMousePosition
.X
- now
.srWindow
.Left
;
770 con
.dwMousePosition
.Y
= mouse_event
.dwMousePosition
.Y
- now
.srWindow
.Top
;
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));
785 fhandler_console::bg_check (int sig
, bool dontsignal
)
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. */
792 set_input_mode (tty::cygwin
, &tc ()->ti
, get_handle_set ());
793 con
.disable_master_thread
= false;
796 set_output_mode (tty::cygwin
, &tc ()->ti
, get_handle_set ());
798 return fhandler_termios::bg_check (sig
, dontsignal
);
802 fhandler_console::read (void *pv
, size_t& buflen
)
804 termios_printf ("read(%p,%d)", pv
, buflen
);
806 push_process_state
process_state (PID_TTYIN
);
808 int copied_chars
= 0;
810 DWORD timeout
= is_nonblocking () ? 0 : INFINITE
;
812 while (!input_ready
&& !get_cons_readahead_valid ())
815 if ((bgres
= bg_check (SIGTTIN
)) <= bg_eof
)
821 set_cursor_maybe (); /* to make cursor appear on the screen immediately */
823 switch (cygwait (get_handle (), timeout
))
830 process_state
.pop ();
831 pthread::static_cancel_self ();
834 set_sig_errno (EAGAIN
);
835 buflen
= (size_t) -1;
838 if (GetLastError () == ERROR_INVALID_HANDLE
)
839 { /* Confirm the handle is still valid */
841 acquire_attach_mutex (mutex_timeout
);
842 BOOL res
= GetConsoleMode (get_handle (), &mode
);
843 release_attach_mutex ();
850 #define buf ((char *) pv)
853 acquire_input_mutex (mutex_timeout
);
854 ret
= process_input_message ();
858 release_input_mutex ();
860 case input_processing
:
861 release_input_mutex ();
863 case input_ok
: /* input ready */
865 case input_signalled
: /* signalled */
867 release_input_mutex ();
868 if (global_sigs
[get_ttyp ()->last_sig
].sa_flags
& SA_RESTART
)
872 /* Should not come here */
873 release_input_mutex ();
878 /* Check console read-ahead buffer filled from terminal requests */
879 while (con
.cons_rapoi
&& *con
.cons_rapoi
&& buflen
)
881 buf
[copied_chars
++] = *con
.cons_rapoi
++;
886 get_readahead_into_buffer (buf
+ copied_chars
, buflen
);
890 release_input_mutex ();
894 buflen
= copied_chars
;
899 buflen
= (size_t) -1;
903 set_sig_errno (EINTR
);
904 buflen
= (size_t) -1;
907 fhandler_console::input_states
908 fhandler_console::process_input_message (void)
912 if (!shared_console_info
)
915 termios
*ti
= &(get_ttyp ()->ti
);
917 fhandler_console::input_states stat
= input_processing
;
919 INPUT_RECORD input_rec
[INREC_SIZE
];
921 acquire_attach_mutex (mutex_timeout
);
923 PeekConsoleInputW (get_handle (), input_rec
, INREC_SIZE
, &total_read
);
924 release_attach_mutex ();
927 termios_printf ("PeekConsoleInput failed, %E");
931 for (i
= 0; i
< total_read
; i
++)
934 const char *toadd
= NULL
;
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
;
941 /* check the event that occurred */
942 switch (input_rec
[i
].EventType
)
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
))
955 set_raw_win32_keyboard_mode (!con
.raw_win32_keyboard_mode
);
960 if (con
.raw_win32_keyboard_mode
)
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
);
970 nread
= strlen (toadd
);
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
]))
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
]))
984 if (ctrl_key_state
& SHIFT_PRESSED
)
986 if (ctrl_key_state
& RIGHT_ALT_PRESSED
)
988 if (ctrl_key_state
& CTRL_PRESSED
)
990 if (ctrl_key_state
& LEFT_ALT_PRESSED
)
993 /* Allow Backspace to emit ^? and escape sequences. */
994 if (input_rec
[i
].Event
.KeyEvent
.wVirtualKeyCode
== VK_BACK
)
996 char c
= con
.backspace_keycode
;
998 if (ctrl_key_state
& ALT_PRESSED
)
1003 tmp
[nread
++] = '\e';
1009 /* Allow Ctrl-Space to emit ^@ */
1010 else if (input_rec
[i
].Event
.KeyEvent
.wVirtualKeyCode
1011 == ((wincap
.has_con_24bit_colors () && !con_is_legacy
) ?
1013 && (ctrl_key_state
& CTRL_PRESSED
)
1014 && !(ctrl_key_state
& ALT_PRESSED
))
1016 else if (unicode_char
== 0
1017 /* arrow/function keys */
1018 || (input_rec
[i
].Event
.KeyEvent
.dwControlKeyState
1021 toadd
= get_nonascii_key (input_rec
[i
], tmp
);
1027 nread
= strlen (toadd
);
1031 WCHAR second
= unicode_char
>= 0xd800 && unicode_char
<= 0xdbff
1032 && i
+ 1 < total_read
?
1033 input_rec
[i
+ 1].Event
.KeyEvent
.uChar
.UnicodeChar
: 0;
1035 if (second
< 0xdc00 || second
> 0xdfff)
1037 nread
= con
.con_to_str (tmp
+ 1, 59, unicode_char
);
1041 /* handle surrogate pairs */
1042 WCHAR pair
[2] = { unicode_char
, second
};
1043 nread
= sys_wcstombs (tmp
+ 1, 59, pair
, 2);
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. */
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));
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)
1070 else if (con
.metabit
)
1078 tmp
[1] = cyg_tolower (tmp
[1]);
1081 con
.nModifiers
&= ~4;
1087 send_winch_maybe ();
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
))
1097 /* Note: Reported mouse position was already retrieved by
1098 mouse_aware() and adjusted by window scroll buffer offset */
1100 /* Treat the double-click event like a regular button press */
1101 if (mouse_event
.dwEventFlags
== DOUBLE_CLICK
)
1103 syscall_printf ("mouse: double-click -> click");
1104 mouse_event
.dwEventFlags
= 0;
1107 /* This code assumes Windows never reports multiple button
1108 events at the same time. */
1111 char mode6_term
= 'M';
1113 if (mouse_event
.dwEventFlags
== MOUSE_WHEELED
)
1115 if (mouse_event
.dwButtonState
& 0xFF800000)
1118 strcpy (sz
, "wheel down");
1123 strcpy (sz
, "wheel up");
1128 /* Ignore unimportant mouse buttons */
1129 mouse_event
.dwButtonState
&= 0x7;
1131 if (mouse_event
.dwEventFlags
== MOUSE_MOVED
)
1133 b
= con
.last_button_code
;
1135 else if (mouse_event
.dwButtonState
< con
.dwLastButtonState
1136 && !con
.ext_mouse_mode6
)
1139 strcpy (sz
, "btn up");
1141 else if ((mouse_event
.dwButtonState
& 1)
1142 != (con
.dwLastButtonState
& 1))
1145 strcpy (sz
, "btn1 down");
1147 else if ((mouse_event
.dwButtonState
& 2)
1148 != (con
.dwLastButtonState
& 2))
1151 strcpy (sz
, "btn2 down");
1153 else if ((mouse_event
.dwButtonState
& 4)
1154 != (con
.dwLastButtonState
& 4))
1157 strcpy (sz
, "btn3 down");
1160 if (con
.ext_mouse_mode6
/* distinguish release */
1161 && mouse_event
.dwButtonState
< con
.dwLastButtonState
)
1164 con
.last_button_code
= b
;
1166 if (mouse_event
.dwEventFlags
== MOUSE_MOVED
)
1169 strcpy (sz
, "move");
1173 /* Remember the modified button state */
1174 con
.dwLastButtonState
= mouse_event
.dwButtonState
;
1178 /* Remember mouse position */
1179 con
.dwLastMousePosition
.X
= con
.dwMousePosition
.X
;
1180 con
.dwLastMousePosition
.Y
= con
.dwMousePosition
.Y
;
1182 /* Remember the modifiers */
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;
1191 /* Indicate the modifiers */
1192 b
|= con
.nModifiers
;
1194 /* We can now create the code. */
1195 if (con
.ext_mouse_mode6
)
1197 __small_sprintf (tmp
, "\033[<%d;%d;%d%c", b
,
1198 con
.dwMousePosition
.X
+ 1,
1199 con
.dwMousePosition
.Y
+ 1,
1201 nread
= strlen (tmp
);
1203 else if (con
.ext_mouse_mode15
)
1205 __small_sprintf (tmp
, "\033[%d;%d;%dM", b
+ 32,
1206 con
.dwMousePosition
.X
+ 1,
1207 con
.dwMousePosition
.Y
+ 1);
1208 nread
= strlen (tmp
);
1210 else if (con
.ext_mouse_mode5
)
1212 unsigned int xcode
= con
.dwMousePosition
.X
+ ' ' + 1;
1213 unsigned int ycode
= con
.dwMousePosition
.Y
+ ' ' + 1;
1215 __small_sprintf (tmp
, "\033[M%c", b
+ ' ');
1217 /* the neat nested encoding function of mintty
1218 does not compile in g++, so let's unfold it: */
1220 tmp
[nread
++] = xcode
;
1221 else if (xcode
< 0x800)
1223 tmp
[nread
++] = 0xC0 + (xcode
>> 6);
1224 tmp
[nread
++] = 0x80 + (xcode
& 0x3F);
1229 tmp
[nread
++] = ycode
;
1230 else if (ycode
< 0x800)
1232 tmp
[nread
++] = 0xC0 + (ycode
>> 6);
1233 tmp
[nread
++] = 0x80 + (ycode
& 0x3F);
1240 unsigned int xcode
= con
.dwMousePosition
.X
+ ' ' + 1;
1241 unsigned int ycode
= con
.dwMousePosition
.Y
+ ' ' + 1;
1246 __small_sprintf (tmp
, "\033[M%c%c%c", b
+ ' ',
1248 nread
= 6; /* tmp may contain NUL bytes */
1250 syscall_printf ("mouse: %s at (%d,%d)", sz
,
1251 con
.dwMousePosition
.X
,
1252 con
.dwMousePosition
.Y
);
1262 if (input_rec
[i
].Event
.FocusEvent
.bSetFocus
)
1263 __small_sprintf (tmp
, "\033[I");
1265 __small_sprintf (tmp
, "\033[O");
1272 case WINDOW_BUFFER_SIZE_EVENT
:
1273 if (send_winch_maybe ())
1286 line_edit_status res
= line_edit (toadd
, nread
, *ti
, &ret
);
1287 if (res
== line_edit_signalled
)
1289 stat
= input_signalled
;
1292 else if (res
== line_edit_input_done
)
1296 if (ti
->c_lflag
& ICANON
)
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
))
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
);
1320 dev_console::fillin (HANDLE h
)
1322 acquire_attach_mutex (mutex_timeout
);
1323 bool ret
= GetConsoleScreenBufferInfo (h
, &b
);
1324 release_attach_mutex ();
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
;
1337 memset (&b
, 0, sizeof (b
));
1340 b
.srWindow
.Bottom
= 24;
1341 b
.srWindow
.Right
= 79;
1348 dev_console::scroll_buffer (HANDLE h
, int x1
, int y1
, int x2
, int y2
,
1351 /* Scroll the screen context.
1354 xn, yn - new ul corner
1355 Negative values represents current screen dimensions
1357 SMALL_RECT sr1
, sr2
;
1360 fill
.Char
.UnicodeChar
= L
' ';
1361 fill
.Attributes
= current_win32_attr
;
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
;
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 ();
1383 fhandler_console::scroll_buffer (int x1
, int y1
, int x2
, int y2
,
1386 con
.scroll_buffer (get_output_handle (), x1
, y1
, x2
, y2
, xn
, yn
);
1390 fhandler_console::scroll_buffer_screen (int x1
, int y1
, int x2
, int y2
,
1394 y1
+= con
.b
.srWindow
.Top
;
1396 y2
+= con
.b
.srWindow
.Top
;
1398 yn
+= con
.b
.srWindow
.Top
;
1399 con
.scroll_buffer (get_output_handle (), x1
, y1
, x2
, y2
, xn
, yn
);
1403 fhandler_console::dup (fhandler_base
*child
, int flags
)
1405 /* See comments in fhandler_pty_slave::dup */
1406 if (myself
->ctty
!= -2)
1407 myself
->set_ctty (this, flags
);
1412 fhandler_console::open (int flags
, mode_t
)
1416 if (dev () == FH_ERROR
)
1418 set_errno (EPERM
); /* constructor found an error */
1425 set_output_handle (NULL
);
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);
1432 if (h
== INVALID_HANDLE_VALUE
)
1438 handle_set
.input_handle
= h
;
1440 h
= CreateFileW (L
"CONOUT$", GENERIC_READ
| GENERIC_WRITE
,
1441 FILE_SHARE_READ
| FILE_SHARE_WRITE
, &sec_none
,
1442 OPEN_EXISTING
, 0, 0);
1444 if (h
== INVALID_HANDLE_VALUE
)
1449 set_output_handle (h
);
1450 handle_set
.output_handle
= h
;
1453 handle_set
.input_mutex
= input_mutex
;
1454 handle_set
.output_mutex
= output_mutex
;
1456 if (con
.fillin (get_output_handle ()))
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 ();
1466 if (myself
->pid
== con
.owner
&& wincap
.has_con_24bit_colors ())
1468 bool is_legacy
= false;
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
))
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
))
1482 SetConsoleMode (get_handle (), dwMode
);
1483 release_attach_mutex ();
1484 con
.is_legacy
= is_legacy
;
1486 if (con_is_legacy
&& !sawTERM
)
1487 setenv ("TERM", "cygwin", 1);
1490 debug_printf ("opened conin$ %p, conout$ %p", get_handle (),
1491 get_output_handle ());
1493 if (myself
->pid
== con
.owner
)
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
);
1506 fhandler_console::open_setup (int flags
)
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
);
1515 fhandler_console::post_open_setup (int fd
)
1517 /* Setting-up console mode for cygwin app started from non-cygwin app. */
1520 set_input_mode (tty::cygwin
, &get_ttyp ()->ti
, &handle_set
);
1521 con
.disable_master_thread
= false;
1523 else if (fd
== 1 || fd
== 2)
1524 set_output_mode (tty::cygwin
, &get_ttyp ()->ti
, &handle_set
);
1526 fhandler_base::post_open_setup (fd
);
1530 fhandler_console::close ()
1532 debug_printf ("closing: %p, %p", get_handle (), get_output_handle ());
1534 acquire_output_mutex (mutex_timeout
);
1536 if (shared_console_info
)
1538 /* Restore console mode if this is the last closure. */
1539 OBJECT_BASIC_INFORMATION obi
;
1541 status
= NtQueryObject (get_handle (), ObjectBasicInformation
,
1542 &obi
, sizeof obi
, NULL
);
1543 if ((NT_SUCCESS (status
) && obi
.HandleCount
== 1)
1544 || myself
->pid
== con
.owner
)
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;
1553 release_output_mutex ();
1555 if (shared_console_info
&& con
.owner
== myself
->pid
1556 && master_thread_started
)
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
);
1567 CloseHandle (input_mutex
);
1569 CloseHandle (output_mutex
);
1570 output_mutex
= NULL
;
1572 CloseHandle (get_handle ());
1573 CloseHandle (get_output_handle ());
1576 free (con_ra
.rabuf
);
1584 fhandler_console::ioctl (unsigned int cmd
, void *arg
)
1586 int res
= fhandler_termios::ioctl (cmd
, arg
);
1589 acquire_output_mutex (mutex_timeout
);
1595 st
= con
.fillin (get_output_handle ());
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 ();
1610 syscall_printf ("WINSZ failed");
1612 release_output_mutex ();
1615 release_output_mutex ();
1619 release_output_mutex ();
1622 *(int *) arg
= (con
.metabit
) ? K_METABIT
: K_ESCPREFIX
;
1623 release_output_mutex ();
1626 if ((intptr_t) arg
== K_METABIT
)
1628 else if ((intptr_t) arg
== K_ESCPREFIX
)
1629 con
.metabit
= FALSE
;
1633 release_output_mutex ();
1636 release_output_mutex ();
1639 if (*(unsigned char *) arg
== 6)
1641 *(unsigned char *) arg
= (unsigned char) con
.nModifiers
;
1642 release_output_mutex ();
1646 release_output_mutex ();
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 ();
1659 release_output_mutex ();
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
)
1668 WCHAR wc
= inp
[i
].Event
.KeyEvent
.uChar
.UnicodeChar
;
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]))
1678 const char eols
[] = {
1681 (char) get_ttyp ()->ti
.c_cc
[VEOL
],
1682 (char) get_ttyp ()->ti
.c_cc
[VEOL2
]
1684 if ((get_ttyp ()->ti
.c_lflag
& ICANON
) &&
1685 len
== 1 && memchr (eols
, mbs
[0], sizeof (eols
)))
1691 if ((get_ttyp ()->ti
.c_lflag
& ICANON
) && !saw_eol
)
1695 release_output_mutex ();
1701 release_output_mutex ();
1702 return fhandler_base::ioctl (cmd
, arg
);
1706 fhandler_console::tcflush (int queue
)
1709 if (queue
== TCIFLUSH
1710 || queue
== TCIOFLUSH
)
1712 acquire_attach_mutex (mutex_timeout
);
1713 BOOL r
= FlushConsoleInputBuffer (get_handle ());
1714 release_attach_mutex ();
1720 con
.num_processed
= 0;
1726 fhandler_console::tcsetattr (int a
, struct termios
const *t
)
1728 get_ttyp ()->ti
= *t
;
1733 fhandler_console::tcgetattr (struct termios
*t
)
1735 *t
= get_ttyp ()->ti
;
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
)
1745 dev ().parse (unit
);
1748 _tc
= &(shared_console_info
->tty_min_state
);
1752 dev_console::set_color (HANDLE h
)
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);
1769 /* apply attributes */
1771 win_fg
= underline_color
;
1772 /* emulate blink with bright background */
1774 win_bg
|= BACKGROUND_INTENSITY
;
1775 if (intensity
== INTENSITY_INVISIBLE
)
1777 else if (intensity
!= INTENSITY_BOLD
)
1778 /* nothing to do */;
1779 /* apply foreground intensity only in non-reverse mode! */
1781 win_bg
|= BACKGROUND_INTENSITY
;
1783 win_fg
|= FOREGROUND_INTENSITY
;
1785 current_win32_attr
= win_fg
| win_bg
;
1788 acquire_attach_mutex (mutex_timeout
);
1789 SetConsoleTextAttribute (h
, current_win32_attr
);
1790 release_attach_mutex ();
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)
1799 dev_console::set_default_attr ()
1801 blink
= underline
= reverse
= false;
1802 intensity
= INTENSITY_NORMAL
;
1803 fg
= default_color
& FOREGROUND_ATTR_MASK
;
1804 bg
= default_color
& BACKGROUND_ATTR_MASK
;
1809 dev_console::set_cl_x (cltype x
)
1811 if (x
== cl_disp_beg
|| x
== cl_buf_beg
)
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
;
1821 dev_console::set_cl_y (cltype y
)
1823 if (y
== cl_buf_beg
)
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
;
1835 dev_console::scroll_window (HANDLE h
, int x1
, int y1
, int x2
, int y2
)
1837 if (save_buf
|| x1
!= 0 || x2
!= dwWinSize
.X
- 1 || y1
!= b
.srWindow
.Top
1838 || y2
!= b
.srWindow
.Bottom
|| b
.dwSize
.Y
<= dwWinSize
.Y
)
1842 int toscroll
= dwEnd
.Y
- b
.srWindow
.Top
+ 1;
1843 sr
.Left
= sr
.Right
= dwEnd
.X
= 0;
1845 acquire_attach_mutex (mutex_timeout
);
1846 if (b
.srWindow
.Bottom
+ toscroll
>= b
.dwSize
.Y
)
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. */
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
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;
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. */
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
);
1897 /* Eventually set cursor to new end position at the top of the window. */
1899 SetConsoleCursorPosition (h
, dwEnd
);
1900 release_attach_mutex ();
1901 /* Fix up console buffer info. */
1907 * Clear the screen context from x1/y1 to x2/y2 cell.
1908 * Negative values represents current screen dimensions
1911 fhandler_console::clear_screen (cltype xc1
, cltype yc1
, cltype xc2
, cltype yc2
)
1913 HANDLE h
= get_output_handle ();
1914 SHORT oldEndY
= con
.dwEnd
.Y
;
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
);
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
)
1928 con
.dwEnd
.Y
= con
.b
.dwCursorPosition
.Y
= oldEndY
;
1929 y1
= con
.b
.srWindow
.Top
;
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
);
1939 dev_console::clear_screen (HANDLE h
, int x1
, int y1
, int x2
, int y2
)
1945 num
= abs (y1
- y2
) * b
.dwSize
.X
+ abs (x1
- x2
) + 1;
1947 if ((y2
* b
.dwSize
.X
+ x2
) > (y1
* b
.dwSize
.X
+ x1
))
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 ();
1964 fhandler_console::cursor_set (bool rel_to_top
, int x
, int y
)
1968 con
.fillin (get_output_handle ());
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
;
1983 else if (rel_to_top
)
1984 y
+= con
.b
.srWindow
.Top
;
1986 if (x
> con
.dwWinSize
.X
)
1987 x
= con
.dwWinSize
.X
- 1;
1993 acquire_attach_mutex (mutex_timeout
);
1994 SetConsoleCursorPosition (get_output_handle (), pos
);
1995 release_attach_mutex ();
1999 fhandler_console::cursor_rel (int x
, int y
)
2001 con
.fillin (get_output_handle ());
2002 x
+= con
.b
.dwCursorPosition
.X
;
2003 y
+= con
.b
.dwCursorPosition
.Y
;
2004 cursor_set (false, x
, y
);
2008 fhandler_console::cursor_get (int *x
, int *y
)
2010 con
.fillin (get_output_handle ());
2011 *y
= con
.b
.dwCursorPosition
.Y
;
2012 *x
= con
.b
.dwCursorPosition
.X
;
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 */
2052 fhandler_console::write_console (PWCHAR buf
, DWORD len
, DWORD
& done
)
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) '`'];
2062 last_char
= buf
[len
-1];
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 ();
2081 /* The following three functions were adapted (i.e., mildly modified) from
2082 http://stackoverflow.com/questions/14699043/replacement-to-systemcolor */
2084 /* Split a rectangular region into two smaller rectangles based on the
2085 largest dimension. */
2087 region_split (PCHAR_INFO
& buf
, COORD
& bufsiz
, SMALL_RECT
& region
,
2088 PCHAR_INFO
& buf_b
, COORD
& bufsiz_b
, SMALL_RECT
& region_b
)
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
;
2100 /* Utility function to figure out the distance between two points. */
2102 delta (SHORT first
, SHORT second
)
2104 return (second
>= first
) ? (second
- first
+ 1) : 0;
2107 /* Subdivide the ReadConsoleInput operation into smaller and smaller chunks as
2108 needed until it succeeds in reading the entire screen buffer. */
2110 ReadConsoleOutputWrapper (HANDLE h
, PCHAR_INFO buf
, COORD bufsiz
,
2114 SHORT width
= delta (region
.Left
, region
.Right
);
2115 SHORT height
= delta (region
.Top
, region
.Bottom
);
2117 if ((width
== 0) || (height
== 0))
2120 acquire_attach_mutex (mutex_timeout
);
2121 BOOL success
= ReadConsoleOutputW (h
, buf
, bufsiz
, coord
, ®ion
);
2122 release_attach_mutex ();
2125 else if (GetLastError () == ERROR_NOT_ENOUGH_MEMORY
&& (width
* height
) > 1)
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
);
2138 dev_console::save_restore (HANDLE h
, char c
)
2140 if (c
== 'h') /* save */
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
;
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
);
2152 save_cursor
= b
.dwCursorPosition
; /* Remember where we were. */
2153 save_top
= b
.srWindow
.Top
;
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");
2161 /* Position at top of buffer */
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 ();
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;
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 ();
2184 debug_printf ("WriteConsoleOutputW failed, %E");
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;
2217 #define TAB 8 /* We should't let the console deal with these */
2223 static const char base_chars
[256] =
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
};
2258 static const char table256
[256] =
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
2279 fhandler_console::char_command (char c
)
2286 if (wincap
.has_con_24bit_colors () && !con_is_legacy
)
2288 /* For xterm compatible mode */
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
2297 if (con
.args
[0] == 0)
2299 cursor_get (&x
, &y
);
2300 cursor_set (false, con
.args
[0]-1, y
);
2303 if (con
.args
[0] == 0)
2305 cursor_rel (con
.args
[0], 0);
2308 if (con
.args
[0] == 0)
2310 cursor_rel (0, con
.args
[0]);
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')
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 ();
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;
2330 /* Just send the sequence */
2331 wpbuf
.send (get_output_handle ());
2334 if (wincap
.has_con_broken_il_dl ())
2336 /* Use "CSI Ps T" instead */
2337 cursor_get (&x
, &y
);
2338 if (y
< srTop
|| y
> srBottom
)
2340 if (y
== con
.b
.srWindow
.Bottom
)
2342 acquire_attach_mutex (mutex_timeout
);
2343 WriteConsoleW (get_output_handle (), L
"\033[2K", 4, 0, 0);
2344 release_attach_mutex ();
2347 acquire_attach_mutex (mutex_timeout
);
2348 if (y
== con
.b
.srWindow
.Top
2349 && srBottom
== con
.b
.srWindow
.Bottom
)
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);
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);
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 ();
2377 /* Just send the sequence */
2378 wpbuf
.send (get_output_handle ());
2382 if (wincap
.has_con_broken_il_dl ())
2384 /* Use "CSI Ps S" instead */
2385 cursor_get (&x
, &y
);
2386 if (y
< srTop
|| y
> srBottom
)
2388 if (y
== con
.b
.srWindow
.Bottom
)
2390 acquire_attach_mutex (mutex_timeout
);
2391 WriteConsoleW (get_output_handle (), L
"\033[2K", 4, 0, 0);
2392 release_attach_mutex ();
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);
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 ();
2414 /* Just send the sequence */
2415 wpbuf
.send (get_output_handle ());
2420 if (con
.args
[0] == 3 && con
.savey
>= 0)
2422 con
.fillin (get_output_handle ());
2423 con
.savey
-= con
.b
.srWindow
.Top
;
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
};
2434 ScrollConsoleScreenBufferA (get_output_handle (),
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 ();
2443 /* Just send the sequence */
2444 wpbuf
.send (get_output_handle ());
2446 case 'h': /* DECSET */
2447 case 'l': /* DECRST */
2449 /* Just send the sequence */
2450 wpbuf
.send (get_output_handle ());
2451 if (con
.saw_question_mark
)
2453 bool need_fix_tab_position
= false;
2454 for (int i
= 0; i
< con
.nargs
; i
++)
2456 if (con
.args
[i
] == 1049)
2458 con
.screen_alternated
= (c
== 'h');
2459 need_fix_tab_position
= wincap
.has_con_broken_tabs ();
2461 if (con
.args
[i
] == 1) /* DECCKM */
2462 con
.cursor_key_app_mode
= (c
== 'h');
2464 /* Call fix_tab_position() if screen has been alternated. */
2465 if (need_fix_tab_position
)
2466 fix_tab_position (get_output_handle ());
2470 if (con
.saw_exclamation_mark
) /* DECSTR Soft reset */
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;
2478 /* Just send the sequence */
2479 wpbuf
.send (get_output_handle ());
2482 if (con
.saw_greater_than_sign
)
2483 break; /* Ignore unsupported CSI > Pm m */
2484 /* Text attribute settings */
2486 /* Just send the sequence */
2487 wpbuf
.send (get_output_handle ());
2490 /* Other escape sequences */
2492 /* Just send the sequence */
2493 wpbuf
.send (get_output_handle ());
2499 /* For legacy cygwin treminal */
2502 case 'm': /* Set Graphics Rendition */
2503 for (int i
= 0; i
< con
.nargs
; i
++)
2504 switch (con
.args
[i
])
2506 case 0: /* normal color */
2507 con
.set_default_attr ();
2510 con
.intensity
= INTENSITY_BOLD
;
2513 con
.intensity
= INTENSITY_DIM
;
2515 case 4: /* underlined */
2518 case 5: /* blink mode */
2521 case 7: /* reverse */
2524 case 8: /* invisible */
2525 con
.intensity
= INTENSITY_INVISIBLE
;
2527 case 10: /* end alternate charset */
2528 con
.alternate_charset_active
= false;
2530 case 11: /* start alternate charset */
2531 con
.alternate_charset_active
= true;
2535 con
.intensity
= INTENSITY_NORMAL
;
2538 con
.underline
= false;
2544 con
.reverse
= false;
2546 case 30: /* BLACK foreground */
2549 case 31: /* RED foreground */
2550 con
.fg
= FOREGROUND_RED
;
2552 case 32: /* GREEN foreground */
2553 con
.fg
= FOREGROUND_GREEN
;
2555 case 33: /* YELLOW foreground */
2556 con
.fg
= FOREGROUND_RED
| FOREGROUND_GREEN
;
2558 case 34: /* BLUE foreground */
2559 con
.fg
= FOREGROUND_BLUE
;
2561 case 35: /* MAGENTA foreground */
2562 con
.fg
= FOREGROUND_RED
| FOREGROUND_BLUE
;
2564 case 36: /* CYAN foreground */
2565 con
.fg
= FOREGROUND_BLUE
| FOREGROUND_GREEN
;
2567 case 37: /* WHITE foreg */
2568 con
.fg
= FOREGROUND_BLUE
| FOREGROUND_GREEN
| FOREGROUND_RED
;
2571 if (con
.nargs
< i
+ 2)
2572 /* Sequence error (abort) */
2574 switch (con
.args
[i
+ 1])
2577 if (con
.nargs
< i
+ 5)
2578 /* Sequence error (abort) */
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
];
2590 if (con
.nargs
< i
+ 3)
2591 /* Sequence error (abort) */
2594 int idx
= con
.args
[i
+ 2];
2599 con
.fg
= table256
[idx
];
2606 con
.fg
= con
.default_color
& FOREGROUND_ATTR_MASK
;
2608 case 40: /* BLACK background */
2611 case 41: /* RED background */
2612 con
.bg
= BACKGROUND_RED
;
2614 case 42: /* GREEN background */
2615 con
.bg
= BACKGROUND_GREEN
;
2617 case 43: /* YELLOW background */
2618 con
.bg
= BACKGROUND_RED
| BACKGROUND_GREEN
;
2620 case 44: /* BLUE background */
2621 con
.bg
= BACKGROUND_BLUE
;
2623 case 45: /* MAGENTA background */
2624 con
.bg
= BACKGROUND_RED
| BACKGROUND_BLUE
;
2626 case 46: /* CYAN background */
2627 con
.bg
= BACKGROUND_BLUE
| BACKGROUND_GREEN
;
2629 case 47: /* WHITE background */
2630 con
.bg
= BACKGROUND_BLUE
| BACKGROUND_GREEN
| BACKGROUND_RED
;
2633 if (con
.nargs
< i
+ 2)
2634 /* Sequence error (abort) */
2636 switch (con
.args
[i
+ 1])
2639 if (con
.nargs
< i
+ 5)
2640 /* Sequence error (abort) */
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;
2652 if (con
.nargs
< i
+ 3)
2653 /* Sequence error (abort) */
2656 int idx
= con
.args
[i
+ 2];
2661 con
.bg
= table256
[idx
] << 4;
2668 con
.bg
= con
.default_color
& BACKGROUND_ATTR_MASK
;
2671 con
.set_color (get_output_handle ());
2673 case 'q': /* Set cursor style (DECSCUSR) */
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])
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
);
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
);
2694 default: /* use value as percentage */
2695 console_cursor_info
.dwSize
= con
.args
[0];
2696 SetConsoleCursorInfo (get_output_handle (),
2697 &console_cursor_info
);
2700 release_attach_mutex ();
2705 if (!con
.saw_question_mark
)
2707 switch (con
.args
[0])
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");
2717 switch (con
.args
[0])
2719 case 25: /* Show/Hide Cursor (DECTCEM) */
2721 CONSOLE_CURSOR_INFO console_cursor_info
;
2722 acquire_attach_mutex (mutex_timeout
);
2723 GetConsoleCursorInfo (get_output_handle (), & console_cursor_info
);
2725 console_cursor_info
.bVisible
= TRUE
;
2727 console_cursor_info
.bVisible
= FALSE
;
2728 SetConsoleCursorInfo (get_output_handle (), & console_cursor_info
);
2729 release_attach_mutex ();
2732 case 47: /* Save/Restore screen */
2733 con
.save_restore (get_output_handle (), c
);
2736 case 67: /* DECBKM ("DEC Backarrow Key Mode") */
2737 con
.backspace_keycode
= (c
== 'h' ? CTRL('H') : CERASE
);
2740 case 1000: /* Mouse tracking */
2741 con
.use_mouse
= (c
== 'h') ? 1 : 0;
2744 case 1002: /* Mouse button event tracking */
2745 con
.use_mouse
= (c
== 'h') ? 2 : 0;
2748 case 1003: /* Mouse any event tracking */
2749 con
.use_mouse
= (c
== 'h') ? 3 : 0;
2752 case 1004: /* Focus in/out event reporting */
2753 con
.use_focus
= (c
== 'h') ? true : false;
2756 case 1005: /* Extended mouse mode */
2757 con
.ext_mouse_mode5
= c
== 'h';
2760 case 1006: /* SGR extended mouse mode */
2761 con
.ext_mouse_mode6
= c
== 'h';
2764 case 1015: /* Urxvt extended mouse mode */
2765 con
.ext_mouse_mode15
= c
== 'h';
2768 case 2000: /* Raw keyboard mode */
2769 set_raw_win32_keyboard_mode ((c
== 'h') ? true : false);
2772 default: /* Ignore */
2773 syscall_printf ("unknown h/l command: %d", con
.args
[0]);
2778 switch (con
.args
[0])
2780 case 0: /* Clear to end of screen */
2781 clear_screen (cl_curr_pos
, cl_curr_pos
, cl_disp_end
, cl_disp_end
);
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
);
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
);
2797 cursor_rel (0, -(con
.args
[0] ?: 1));
2800 cursor_rel (0, con
.args
[0] ?: 1);
2803 cursor_rel (con
.args
[0] ?: 1, 0);
2806 cursor_rel (-(con
.args
[0] ?: 1),0);
2809 switch (con
.args
[0])
2811 case 0: /* Clear to end of line */
2812 clear_screen (cl_curr_pos
, cl_curr_pos
, cl_disp_end
, cl_curr_pos
);
2814 case 2: /* Clear line */
2815 clear_screen (cl_disp_beg
, cl_curr_pos
, cl_disp_end
, cl_curr_pos
);
2817 case 1: /* Clear from bol to cursor */
2818 clear_screen (cl_disp_beg
, cl_curr_pos
, cl_curr_pos
, cl_curr_pos
);
2826 cursor_set (true, (con
.args
[1] ?: 1) - 1,
2827 (con
.args
[0] ?: 1) - 1);
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
);
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));
2837 case 's': /* Save cursor position */
2838 cursor_get (&con
.savex
, &con
.savey
);
2839 con
.savey
-= con
.b
.srWindow
.Top
;
2841 case 'u': /* Restore cursor position */
2842 cursor_set (true, con
.savex
, con
.savey
);
2845 cursor_get (&x
, &y
);
2846 cursor_set (false, 8 * (x
/ 8 + 1), y
);
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
);
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
);
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
);
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
);
2868 case 'S': /* SF - Scroll forward */
2869 n
= con
.args
[0] ?: 1;
2870 scroll_buffer_screen (0, n
, -1, -1, 0, 0);
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
);
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
);
2883 case 'Z': /* Back tab */
2884 cursor_get (&x
, &y
);
2885 cursor_set (false, ((8 * (x
/ 8 + 1)) - 8), y
);
2887 case 'b': /* Repeat char #1 #2 times */
2888 if (con
.insert_mode
)
2890 cursor_get (&x
, &y
);
2891 scroll_buffer (x
, y
, -1, y
, x
+ con
.args
[1], y
);
2893 while (con
.args
[1]--)
2894 WriteFile (get_output_handle (), &con
.args
[0], 1, (DWORD
*) &x
, 0);
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
);
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);
2919 switch (con
.args
[0])
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);
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);
2944 case 'g': /* TAB set/clear */
2952 #define NUM_REPLACEMENT_CHARS 3
2954 static const wchar_t replacement_char
[NUM_REPLACEMENT_CHARS
] =
2956 0xfffd, /* REPLACEMENT CHARACTER */
2957 0x25a1, /* WHITE SQUARE */
2958 0x2592 /* MEDIUM SHADE */
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
;
2966 enum_proc (const LOGFONTW
*lf
, const TEXTMETRICW
*tm
,
2967 DWORD FontType
, LPARAM lParam
)
2969 int *done
= (int *) lParam
;
2975 check_font (HANDLE hdl
)
2977 CONSOLE_FONT_INFOEX cfi
;
2980 cfi
.cbSize
= sizeof cfi
;
2981 acquire_attach_mutex (mutex_timeout
);
2982 BOOL r
= GetCurrentConsoleFontEx (hdl
, 0, &cfi
);
2983 release_attach_mutex ();
2986 /* Switched font? */
2987 if (wcscmp (cons_facename
, cfi
.FaceName
) == 0)
2989 if (!cdc
&& !(cdc
= GetDC (GetConsoleWindow ())))
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;
2999 EnumFontFamiliesExW (cdc
, &lf
, enum_proc
, (LPARAM
) &done
, 0);
3003 while (!done
&& cp
>= lf
.lfFaceName
);
3004 /* What, really? No recognizable font? */
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
);
3019 HFONT old_f
= (HFONT
) SelectObject(cdc
, f
);
3022 WORD glyph_idx
[NUM_REPLACEMENT_CHARS
];
3024 if (GetGlyphIndicesW (cdc
, replacement_char
,
3025 NUM_REPLACEMENT_CHARS
, glyph_idx
,
3026 GGI_MARK_NONEXISTING_GLYPHS
) != GDI_ERROR
)
3030 for (i
= 0; i
< NUM_REPLACEMENT_CHARS
; ++i
)
3031 if (glyph_idx
[i
] != 0xffff)
3033 if (i
== NUM_REPLACEMENT_CHARS
)
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
3041 wcscpy (cons_facename
, cfi
.FaceName
);
3043 SelectObject (cdc
, old_f
);
3048 /* This gets called when we found an invalid input character.
3049 Print one of the above Unicode chars as replacement char. */
3051 fhandler_console::write_replacement_char ()
3053 check_font (get_output_handle ());
3056 acquire_attach_mutex (mutex_timeout
);
3057 WriteConsoleW (get_output_handle (), &rp_char
, 1, &done
, 0);
3058 release_attach_mutex ();
3061 const unsigned char *
3062 fhandler_console::write_normal (const unsigned char *src
,
3063 const unsigned char *end
)
3065 /* Scan forward to see what a char which needs special treatment */
3068 const unsigned char *found
= src
;
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
;
3078 /* First check if we have cached lead bytes of a former try to write
3079 a truncated multibyte sequence. If so, process it. */
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
))
3090 /* Still truncated multibyte sequence? Keep in trunc_buf. */
3091 trunc_buf
.len
+= cp_len
;
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... */
3099 /* ... and proceed. */
3103 nfound
= trunc_buf
.buf
+ 1;
3106 nfound
= trunc_buf
.buf
+ ret
;
3109 /* Valid multibyte sequence? Process. */
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
))
3117 debug_printf ("multibyte sequence write failed, handle %p",
3118 get_output_handle ());
3121 found
= src
+ (nfound
- trunc_buf
.buf
- trunc_buf
.len
);
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
);
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
))
3140 switch (ret
= f_mbtowc (_REENT
, NULL
, (const char *) found
,
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
);
3147 case -1: /* Invalid multibyte sequence. Handled below. */
3160 /* Print all the base characters out */
3163 DWORD len
= found
- src
;
3164 buf_len
= con
.str_to_con (f_mbtowc
, write_buf
, (const char *) src
, len
);
3167 debug_printf ("conversion error, handle %p",
3168 get_output_handle ());
3173 if (con
.insert_mode
)
3176 cursor_get (&x
, &y
);
3177 scroll_buffer (x
, y
, -1, y
, x
+ buf_len
, y
);
3180 if (!write_console (write_buf
, buf_len
, done
))
3182 debug_printf ("write failed, handle %p", get_output_handle ());
3185 /* Stop here if we reached the conversion limit. */
3186 if (len
>= CONVERT_LIMIT
)
3187 return found
+ trunc_buf
.len
;
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)
3195 switch (base_chars
[*found
])
3197 case SO
: /* Shift Out: Invoke G1 character set (ISO 2022) */
3198 con
.iso_2022_G1
= true;
3200 case SI
: /* Shift In: Invoke G0 character set (ISO 2022) */
3201 con
.iso_2022_G1
= false;
3211 cursor_get (&x
, &y
);
3214 if (y
>= con
.b
.srWindow
.Bottom
&& !con
.scroll_region
.Top
)
3216 acquire_attach_mutex (mutex_timeout
);
3217 WriteConsoleW (get_output_handle (), L
"\n", 1, &done
, 0);
3218 release_attach_mutex ();
3222 scroll_buffer (0, srTop
+ 1, -1, srBottom
, 0, srTop
);
3227 ((get_ttyp ()->ti
.c_oflag
& ONLCR
) ? 0 : x
), y
+ 1);
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. */
3238 cursor_get (&x
, &y
);
3239 cursor_set (false, 0, y
);
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. */
3247 write_replacement_char ();
3248 if (found
+ 1 < end
)
3250 ret
= __utf8_mbtowc (_REENT
, NULL
, (const char *) found
+ 1,
3251 end
- found
- 1, &ps
);
3254 acquire_attach_mutex (mutex_timeout
);
3257 WCHAR w
= *(found
+ 1);
3258 WriteConsoleW (get_output_handle (), &w
, 1, &done
, 0);
3261 release_attach_mutex ();
3267 cursor_get (&x
, &y
);
3268 cursor_set (false, 8 * (x
/ 8 + 1), y
);
3271 write_replacement_char ();
3276 return found
+ trunc_buf
.len
;
3280 fhandler_console::write (const void *vsrc
, size_t len
)
3282 bg_check_types bg
= bg_check (SIGTTOU
);
3284 return (ssize_t
) bg
;
3286 if (get_ttyp ()->ti
.c_lflag
& FLUSHO
)
3287 return len
; /* Discard write data */
3289 if (get_ttyp ()->output_stopped
&& is_nonblocking ())
3294 while (get_ttyp ()->output_stopped
)
3297 push_process_state
process_state (PID_TTYOU
);
3299 acquire_output_mutex (mutex_timeout
);
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. */
3308 write_buf
= tp
.w_get ();
3310 debug_printf ("%p, %ld", vsrc
, len
);
3314 paranoid_printf ("char %0c state is %d", *src
, con
.state
);
3318 src
= write_normal (src
, end
);
3319 if (!src
) /* write_normal failed */
3321 release_output_mutex ();
3326 if (*src
== '[') /* CSI Control Sequence Introducer */
3329 con
.state
= gotsquare
;
3330 memset (con
.args
, 0, sizeof con
.args
);
3332 con
.saw_question_mark
= false;
3333 con
.saw_greater_than_sign
= false;
3334 con
.saw_space
= false;
3335 con
.saw_exclamation_mark
= false;
3337 else if (*src
== '8') /* DECRC Restore cursor position */
3339 if (con
.screen_alternated
)
3341 /* For xterm mode only */
3342 /* Just send the sequence */
3344 wpbuf
.send (get_output_handle ());
3346 else if (con
.savex
>= 0 && con
.savey
>= 0)
3347 cursor_set (false, con
.savex
, con
.savey
);
3351 else if (*src
== '7') /* DECSC Save cursor position */
3353 if (con
.screen_alternated
)
3355 /* For xterm mode only */
3356 /* Just send the sequence */
3358 wpbuf
.send (get_output_handle ());
3361 cursor_get (&con
.savex
, &con
.savey
);
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) */
3369 cursor_get (&x
, &y
);
3372 if (y
== con
.b
.srWindow
.Top
3373 && srBottom
== con
.b
.srWindow
.Bottom
)
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 ();
3385 /* Substitute "CSI Ps T" */
3391 wpbuf
.send (get_output_handle ());
3395 else if (*src
== ']') /* OSC Operating System Command */
3399 con
.my_title_buf
[0] = '\0';
3400 con
.state
= gotrsquare
;
3402 else if (wincap
.has_con_24bit_colors () && !con_is_legacy
)
3404 if (*src
== 'c') /* RIS Full reset */
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;
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. */
3415 /* Just send the sequence */
3416 wpbuf
.send (get_output_handle ());
3420 else if (*src
== '(') /* Designate G0 character set */
3423 con
.state
= gotparen
;
3425 else if (*src
== ')') /* Designate G1 character set */
3428 con
.state
= gotrparen
;
3430 else if (*src
== 'M') /* Reverse Index (scroll down) */
3432 con
.fillin (get_output_handle ());
3433 scroll_buffer_screen (0, 0, -1, -1, 0, 1);
3437 else if (*src
== 'c') /* RIS Full Reset */
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
);
3448 else if (*src
== 'R') /* ? */
3463 if (con
.nargs
< MAXARGS
)
3464 con
.args
[con
.nargs
] = con
.args
[con
.nargs
] * 10 + *src
- '0';
3468 else if (*src
== ';')
3472 if (con
.nargs
< MAXARGS
)
3475 else if (*src
== ' ')
3479 con
.saw_space
= true;
3480 con
.state
= gotcommand
;
3483 con
.state
= gotcommand
;
3486 if (con
.nargs
< MAXARGS
)
3488 char_command (*src
++);
3494 con
.rarg
= con
.rarg
* 10 + (*src
- '0');
3495 else if (*src
== ';')
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
;
3505 con
.state
= eattitle
;
3507 else if (*src
== '\033')
3508 con
.state
= endpalette
;
3509 else if (*src
== '\007')
3512 if (wincap
.has_con_24bit_colors () && !con_is_legacy
)
3513 wpbuf
.send (get_output_handle ());
3526 int n
= strlen (con
.my_title_buf
);
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
);
3536 else if (n
< TITLESIZE
)
3538 con
.my_title_buf
[n
++] = *src
;
3539 con
.my_title_buf
[n
] = '\0';
3547 con
.saw_question_mark
= true;
3548 else if (*src
== '\033')
3549 con
.state
= endpalette
;
3550 else if (*src
== '\a')
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 ());
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 ());
3572 /* Sequence error (abort) */
3580 con
.state
= gotarg1
;
3582 if (con
.nargs
< MAXARGS
)
3586 else if (isalpha (*src
))
3587 con
.state
= gotcommand
;
3588 else if (*src
!= '@' && !isalpha (*src
) && !isdigit (*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;
3597 /* ignore any extra chars between [ and first arg or command */
3601 con
.state
= gotarg1
;
3603 case gotparen
: /* Designate G0 Character Set (ISO 2022) */
3605 con
.vt100_graphics_mode_G0
= true;
3607 con
.vt100_graphics_mode_G0
= false;
3612 case gotrparen
: /* Designate G1 Character Set (ISO 2022) */
3614 con
.vt100_graphics_mode_G1
= true;
3616 con
.vt100_graphics_mode_G1
= false;
3623 release_output_mutex ();
3625 syscall_printf ("%ld = fhandler_console::write(...)", len
);
3631 fhandler_console::doecho (const void *str
, DWORD len
)
3633 bool stopped
= get_ttyp ()->output_stopped
;
3634 get_ttyp ()->output_stopped
= false;
3636 get_ttyp ()->output_stopped
= stopped
;
3639 static const struct {
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 */
3679 fhandler_console::get_nonascii_key (INPUT_RECORD
& input_rec
, char *tmp
)
3684 /*#define CONTROLSHIFT 3*/
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
;
3692 for (int i
= 0; keytable
[i
].vk
; i
++)
3693 if (input_rec
.Event
.KeyEvent
.wVirtualKeyCode
== keytable
[i
].vk
)
3695 if ((input_rec
.Event
.KeyEvent
.dwControlKeyState
& ALT_PRESSED
)
3696 && keytable
[i
].val
[modifier_index
] != NULL
)
3697 { /* Generic ESC prefixing if Alt is pressed */
3699 strcpy (tmp
+ 1, keytable
[i
].val
[modifier_index
]);
3703 return keytable
[i
].val
[modifier_index
];
3706 if (input_rec
.Event
.KeyEvent
.uChar
.AsciiChar
)
3708 tmp
[0] = input_rec
.Event
.KeyEvent
.uChar
.AsciiChar
;
3716 fhandler_console::init (HANDLE h
, DWORD a
, mode_t bin
)
3718 // this->fhandler_termios::init (h, mode, bin);
3719 /* Ensure both input and output console handles are open */
3722 a
&= GENERIC_READ
| GENERIC_WRITE
;
3723 if (a
== GENERIC_READ
)
3725 if (a
== GENERIC_WRITE
)
3727 if (a
== (GENERIC_READ
| GENERIC_WRITE
))
3729 open_with_arch (flags
| O_BINARY
| (h
? 0 : O_NOCTTY
));
3731 return !tcsetattr (0, &get_ttyp ()->ti
);
3735 fhandler_console::igncr_enabled ()
3737 return get_ttyp ()->ti
.c_iflag
& IGNCR
;
3741 fhandler_console::set_close_on_exec (bool val
)
3743 close_on_exec (val
);
3747 set_console_title (char *title
)
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
);
3758 static bool NO_COPY gdb_inferior_noncygwin
= false;
3761 fhandler_console::set_console_mode_to_native ()
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
)
3769 fhandler_console
*cons
= (fhandler_console
*) (fhandler_base
*) cfd
;
3770 if (cons
->get_device () == cons
->tc ()->getntty ())
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;
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
);
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
)
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
);
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
)
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
);
3818 ContinueDebugEvent_Hooked
3819 (DWORD p
, DWORD t
, DWORD s
)
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
);
3828 fhandler_console::fixup_after_fork_exec (bool execing
)
3836 #define DO_HOOK(module, name) \
3839 void *api = hook_api (module, #name, (void *) name##_Hooked); \
3840 name##_Orig = (__typeof__ (name) *) api; \
3841 /*if (api) system_printf (#name " hooked.");*/ \
3843 /* CreateProcess() is hooked for GDB etc. */
3844 DO_HOOK (NULL
, CreateProcessA
);
3845 DO_HOOK (NULL
, CreateProcessW
);
3846 DO_HOOK (NULL
, ContinueDebugEvent
);
3849 /* Ugly workaround to create invisible console required since Windows 7.
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".
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.
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
3866 fhandler_console::create_invisible_console_workaround (bool force
)
3868 /* If force is set, avoid to reattach to existing console. */
3869 if (force
|| !AttachConsole (-1))
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
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
)))
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
3894 STARTUPINFOW si
= {};
3895 PROCESS_INFORMATION pi
;
3896 size_t len
= helper
.get_wide_win32_path_len ();
3898 WCHAR args
[len
+ 1 + (2 * sizeof (" 0xffffffffffffffff")) + 1];
3899 WCHAR title
[] = L
"invisible cygwin console";
3901 /* Create a new hidden process. Use the two event handles as
3902 argv[1] and argv[2]. */
3904 helper
.get_wide_win32_path (cmd
);
3905 __small_swprintf (args
, L
"\"%W\" %p %p", cmd
, hello
, goodbye
);
3907 si
.cb
= sizeof (si
);
3908 si
.dwFlags
= STARTF_USESHOWWINDOW
;
3909 si
.wShowWindow
= SW_HIDE
;
3912 BOOL x
= CreateProcessW (cmd
, args
,
3913 &sec_none_nih
, &sec_none_nih
, true,
3914 CREATE_NEW_CONSOLE
, NULL
, NULL
, &si
, &pi
);
3917 CloseHandle (pi
.hProcess
); /* Don't need */
3918 CloseHandle (pi
.hThread
); /* these. */
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. */
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
);
3938 CloseHandle (hello
);
3941 SetEvent (goodbye
); /* Tell helper process it's ok to exit. */
3942 CloseHandle (goodbye
);
3945 return invisible_console
= true;
3949 fhandler_console::free_console ()
3951 BOOL res
= FreeConsole ();
3952 debug_printf ("freed console, res %d", res
);
3953 init_console_handler (false);
3957 fhandler_console::need_invisible (bool force
)
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;
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.
3979 Things to watch out for if you make changes in this code:
3981 - Flashing, black consoles showing up when you start, e.g., ssh in
3983 - Non-displaying of characters in rxvt or xemacs if you start a
3984 process using setsid: bash -lc "setsid rxvt". */
3986 h
= GetProcessWindowStation ();
3991 || !GetUserObjectInformationW (h
, UOI_FLAGS
, &oi
, sizeof (oi
), &len
)
3992 || !(oi
.dwFlags
& WSF_VISIBLE
))
3995 debug_printf ("window station is not visible");
3997 invisible_console
= true;
3999 b
= create_invisible_console_workaround (force
);
4002 debug_printf ("invisible_console %d", invisible_console
);
4007 fhandler_console::__acquire_input_mutex (const char *fn
, int ln
, DWORD ms
)
4010 strace
.prntf (_STRACE_TERMIOS
, fn
, "(%d): trying to get input_mutex", ln
);
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 ());
4019 strace
.prntf (_STRACE_TERMIOS
, fn
, "(%d): got input_mutex", ln
);
4025 fhandler_console::__release_input_mutex (const char *fn
, int ln
)
4027 ReleaseMutex (input_mutex
);
4029 strace
.prntf (_STRACE_TERMIOS
, fn
, "(%d): release input_mutex", ln
);
4034 fhandler_console::__acquire_output_mutex (const char *fn
, int ln
, DWORD ms
)
4037 strace
.prntf (_STRACE_TERMIOS
, fn
, "(%d): trying to get output_mutex", ln
);
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 ());
4046 strace
.prntf (_STRACE_TERMIOS
, fn
, "(%d): got output_mutex", ln
);
4052 fhandler_console::__release_output_mutex (const char *fn
, int ln
)
4054 ReleaseMutex (output_mutex
);
4056 strace
.prntf (_STRACE_TERMIOS
, fn
, "(%d): release output_mutex", ln
);
4061 fhandler_console::get_duplicated_handle_set (handle_set_t
*p
)
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
);
4077 /* The function close_handle_set() should be static so that they can
4078 be called even after the fhandler_console instance is deleted. */
4080 fhandler_console::close_handle_set (handle_set_t
*p
)
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
;
4093 fhandler_console::need_console_handler ()
4095 return con
.disable_master_thread
;