public inbox for cygwin@cygwin.com
 help / color / mirror / Atom feed
* Ctrl+C is sometimes ignored on Windows Terminal
@ 2021-11-01  8:33 Naoto Aoki
  2021-11-01  8:50 ` Russell VT
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Naoto Aoki @ 2021-11-01  8:33 UTC (permalink / raw)
  To: cygwin

[-- Attachment #1: Type: text/plain, Size: 1148 bytes --]

Hi,

When I'm using some programs such as bash and python from cygwin under
Windows Terminal, Ctrl+C is sometimes ignored.
https://github.com/microsoft/terminal

Normally holding 'Ctrl' and pressing 'C' will make new line.
But, sometimes it does not and unholding 'Ctrl' makes new line under
Windows Terminal.
bash from msys2 does also reproduce this issue.

I dug into this issue and found that this is related to
readline and Windows 10's pseudo console (ConPTY).
I made simple programs to reproduce this issue.

 - EchoCon.cpp
   - modification of ConPty sample code provided by Microsoft.
     This program execute bash on pseudo console.
     to be compiled with MSVC.
 - getkey.cpp
   - simple program to check Ctrl+C is passed to Cygwin program.
     to be compiled with Cygwin gcc.
 - rltest.cpp
   - simple program to check SIGINT handling.
     This program reproduces the issue.
     If you replace readline("> ") with gets(buf),
     then the issue does not happen.
     to be compiled with Cygwin gcc.

- The machine and OS that it is running on
  - OS: Windows 10 Pro 19043.1288
  - Windows Terminal: 1.11.2921.0

Regards,
Naoto Aoki

[-- Attachment #2: getkey.cpp --]
[-- Type: text/plain, Size: 2003 bytes --]

// original: https://blog.goo.ne.jp/lm324/e/16629a8aadaa0de77fc05611390cf15b
// modified by aont 2021/10/15

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>

int getkey(void)
{
    struct termios oldt, newt;
    int ch0,ch1,ch2,ch3,ch4;
    int ret;

    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_iflag = ~( BRKINT | ISTRIP | IXON  );
    newt.c_lflag = ~( ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHONL );
    newt.c_cc[VTIME]    = 0;
    newt.c_cc[VMIN]     = 1;
    newt.c_cc[VINTR]    = 1;
    if(tcsetattr(STDIN_FILENO, TCSANOW, &newt)==-1) {
        fprintf(stderr,"error tcsetattr\n");
        exit(EXIT_FAILURE);
    }
    
    ch0 = getchar();
    if(ch0==0x1B) {
        ch1 = getchar();
        ch2 = getchar();
        if(ch2==0x32) {
            ch3 = getchar();
            if(ch3==0x7e) {
                ret = (ch0<<24) | (ch1<<16) | (ch2<<8) | ch3;
            } else {
                ch4 = getchar();
                ret = (ch1<<24) | (ch2<<16) | (ch3<<8) | ch4;
            }
        } else if(ch2==0x31) {
            ch3 = getchar();
            ch4 = getchar();
            ret = (ch1<<24) | (ch2<<16) | (ch3<<8) | ch4;
        } else if((ch2==0x33)||(ch2==0x35)||(ch2==0x36)) {
            ch3 = getchar();
            ret = (ch0<<24) | (ch1<<16) | (ch2<<8) | ch3;
        } else {
            ret = (ch0<<16) | (ch1<<8) | ch2;
        }
    } else if(ch0 != EOF) {
        ret = ch0;
    } else {
        ret = 0;
    }

    if(tcsetattr(STDIN_FILENO, TCSANOW, &oldt)==-1) {
        fprintf(stderr,"error tcsetattr\n");
        exit(EXIT_FAILURE);
    }

    return ret;
}

int main(int argc,char *argv[])
{
    int key;

    while (1) {
        key = getkey();
        
        if (key!=0) {
            printf("key code 0x%x\n",key);
        }else{
            ;
            usleep(1000);
        }
        if(key==0x71 /* q */ ) {
            break;
        }

    }

    return 0;
}


[-- Attachment #3: rltest.cpp --]
[-- Type: text/plain, Size: 577 bytes --]

#include <setjmp.h>
#include <stdio.h>
#include <signal.h>
#include <readline/readline.h>

sigjmp_buf ctrlc_buf;

void handle_signals(int signo) {
  if (signo == SIGINT) {
    siglongjmp(ctrlc_buf, 1);
  }
}

int main(int argc, char **argv)
{
  char * input;

  if (signal(SIGINT, handle_signals) == SIG_ERR) {
    fprintf(stderr, "installing signal handler failed\n");
  }

    char buf[128];
  while (1) 
  {
    while ( sigsetjmp( ctrlc_buf, 1 ) != 0 ) {
      fprintf(stderr, "Ctrl+C\n");
    }

    input = readline("> ");
    if (!input)
      break;

  }  
  return 0;
}

[-- Attachment #4: EchoCon.cpp --]
[-- Type: text/plain, Size: 7856 bytes --]

// EchoCon.cpp : Entry point for the EchoCon Pseudo-Console sample application.
// Copyright © 2018, Microsoft
// Modified by aont 2021/10/15

// #include "stdafx.h"
#include <Windows.h>
#include <process.h>
#include <cstdio>

// Forward declarations
HRESULT CreatePseudoConsoleAndPipes(HPCON*, HANDLE*, HANDLE*);
HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX*, HPCON);
void __cdecl PipeListener(LPVOID);

int main()
{
    wchar_t szCommand[]{ L"C:\\cygwin64\\bin\\bash.exe --login -i" };
    HRESULT hr{ E_UNEXPECTED };
    HANDLE hConsole = { GetStdHandle(STD_OUTPUT_HANDLE) };

    HANDLE hin = GetStdHandle(STD_INPUT_HANDLE);

    // Enable Console VT Processing
    DWORD consoleMode{};

    GetConsoleMode(hin, &consoleMode);
    hr = SetConsoleMode(hin, consoleMode ^ ENABLE_PROCESSED_INPUT ^ ENABLE_LINE_INPUT)
        ? S_OK
        : GetLastError();

    GetConsoleMode(hConsole, &consoleMode);
    hr = SetConsoleMode(hConsole, consoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
        ? S_OK
        : GetLastError();
    if (S_OK == hr)
    {
        HPCON hPC{ INVALID_HANDLE_VALUE };

        //  Create the Pseudo Console and pipes to it
        HANDLE hPipeIn{ INVALID_HANDLE_VALUE };
        HANDLE hPipeOut{ INVALID_HANDLE_VALUE };
        hr = CreatePseudoConsoleAndPipes(&hPC, &hPipeIn, &hPipeOut);
        if (S_OK == hr)
        {
            // Create & start thread to listen to the incoming pipe
            // Note: Using CRT-safe _beginthread() rather than CreateThread()
            HANDLE hPipeListenerThread{ reinterpret_cast<HANDLE>(_beginthread(PipeListener, 0, hPipeIn)) };

            // Initialize the necessary startup info struct        
            STARTUPINFOEX startupInfo{};
            if (S_OK == InitializeStartupInfoAttachedToPseudoConsole(&startupInfo, hPC))
            {
                // Launch ping to emit some text back via the pipe
                PROCESS_INFORMATION piClient{};
                hr = CreateProcess(
                    NULL,                           // No module name - use Command Line
                    szCommand,                      // Command Line
                    NULL,                           // Process handle not inheritable
                    NULL,                           // Thread handle not inheritable
                    FALSE,                          // Inherit handles
                    EXTENDED_STARTUPINFO_PRESENT,   // Creation flags
                    NULL,                           // Use parent's environment block
                    NULL,                           // Use parent's starting directory 
                    &startupInfo.StartupInfo,       // Pointer to STARTUPINFO
                    &piClient)                      // Pointer to PROCESS_INFORMATION
                    ? S_OK
                    : GetLastError();

                char input_key;
                DWORD num_events;
                while (true) {
                    bool ret;
                    ret = ReadFile(hin, &input_key, 1, &num_events, NULL);
                    if (ret) {
                        fprintf(stderr, "input: 0x%x\n", input_key);
                        WriteFile(hPipeOut, &input_key, 1, &num_events, NULL);
                    }
                    
                };


                if (S_OK == hr)
                {
                    // Wait up to 10s for ping process to complete
                    WaitForSingleObject(piClient.hThread, 10 * 1000);

                    // Allow listening thread to catch-up with final output!
                    Sleep(500);
                }

                // --- CLOSEDOWN ---

                // Now safe to clean-up client app's process-info & thread
                CloseHandle(piClient.hThread);
                CloseHandle(piClient.hProcess);

                // Cleanup attribute list
                DeleteProcThreadAttributeList(startupInfo.lpAttributeList);
                free(startupInfo.lpAttributeList);
            }

            // Close ConPTY - this will terminate client process if running
            ClosePseudoConsole(hPC);

            // Clean-up the pipes
            if (INVALID_HANDLE_VALUE != hPipeOut) CloseHandle(hPipeOut);
            if (INVALID_HANDLE_VALUE != hPipeIn) CloseHandle(hPipeIn);
        }
    }

    return S_OK == hr ? EXIT_SUCCESS : EXIT_FAILURE;
}

HRESULT CreatePseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut)
{
    HRESULT hr{ E_UNEXPECTED };
    HANDLE hPipePTYIn{ INVALID_HANDLE_VALUE };
    HANDLE hPipePTYOut{ INVALID_HANDLE_VALUE };

    // Create the pipes to which the ConPTY will connect
    if (CreatePipe(&hPipePTYIn, phPipeOut, NULL, 0) &&
        CreatePipe(phPipeIn, &hPipePTYOut, NULL, 0))
    {
        // Determine required size of Pseudo Console
        COORD consoleSize{};
        CONSOLE_SCREEN_BUFFER_INFO csbi{};
        HANDLE hConsole{ GetStdHandle(STD_OUTPUT_HANDLE) };
        if (GetConsoleScreenBufferInfo(hConsole, &csbi))
        {
            consoleSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1;
            consoleSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
        }

        // Create the Pseudo Console of the required size, attached to the PTY-end of the pipes
        hr = CreatePseudoConsole(consoleSize, hPipePTYIn, hPipePTYOut, 0, phPC);

        // Note: We can close the handles to the PTY-end of the pipes here
        // because the handles are dup'ed into the ConHost and will be released
        // when the ConPTY is destroyed.
        if (INVALID_HANDLE_VALUE != hPipePTYOut) CloseHandle(hPipePTYOut);
        if (INVALID_HANDLE_VALUE != hPipePTYIn) CloseHandle(hPipePTYIn);
    }

    return hr;
}

// Initializes the specified startup info struct with the required properties and
// updates its thread attribute list with the specified ConPTY handle
HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC)
{
    HRESULT hr{ E_UNEXPECTED };

    if (pStartupInfo)
    {
        SIZE_T attrListSize{};

        pStartupInfo->StartupInfo.cb = sizeof(STARTUPINFOEX);

        // Get the size of the thread attribute list.
        InitializeProcThreadAttributeList(NULL, 1, 0, &attrListSize);

        // Allocate a thread attribute list of the correct size
        pStartupInfo->lpAttributeList =
            reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(attrListSize));

        // Initialize thread attribute list
        if (pStartupInfo->lpAttributeList
            && InitializeProcThreadAttributeList(pStartupInfo->lpAttributeList, 1, 0, &attrListSize))
        {
            // Set Pseudo Console attribute
            hr = UpdateProcThreadAttribute(
                pStartupInfo->lpAttributeList,
                0,
                PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
                hPC,
                sizeof(HPCON),
                NULL,
                NULL)
                ? S_OK
                : HRESULT_FROM_WIN32(GetLastError());
        }
        else
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
        }
    }
    return hr;
}

void __cdecl PipeListener(LPVOID pipe)
{
    HANDLE hPipe{ pipe };
    HANDLE hConsole{ GetStdHandle(STD_OUTPUT_HANDLE) };

    const DWORD BUFF_SIZE{ 512 };
    char szBuffer[BUFF_SIZE]{};

    DWORD dwBytesWritten{};
    DWORD dwBytesRead{};
    BOOL fRead{ FALSE };
    do
    {
        // Read from the pipe
        fRead = ReadFile(hPipe, szBuffer, BUFF_SIZE, &dwBytesRead, NULL);

        // Write received text to the Console
        // Note: Write to the Console using WriteFile(hConsole...), not printf()/puts() to
        // prevent partially-read VT sequences from corrupting output
        WriteFile(hConsole, szBuffer, dwBytesRead, &dwBytesWritten, NULL);

    } while (fRead && dwBytesRead >= 0);
}

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Ctrl+C is sometimes ignored on Windows Terminal
  2021-11-01  8:33 Ctrl+C is sometimes ignored on Windows Terminal Naoto Aoki
@ 2021-11-01  8:50 ` Russell VT
  2021-11-01 14:07 ` Doug Henderson
  2021-11-02  3:34 ` Takashi Yano
  2 siblings, 0 replies; 4+ messages in thread
From: Russell VT @ 2021-11-01  8:50 UTC (permalink / raw)
  To: Naoto Aoki; +Cc: cygwin

If you're running things from under Windows Terminal, rather than the
Cygwin terminal, the behavior is likely going to be inconsistent or
unpredictable (and things like interrupts may be handled at a lower level
within the terminal, first, before passing to a higher level and received
by Cygwin).

I'd work on trying to reproduce this in any of the terminals provided by
Cygwin... otherwise, chances are you might need to dig deeper in
MSTerminal, or even WIndows, first.

Regards,
Russell VT


On Mon, Nov 1, 2021 at 1:34 AM Naoto Aoki via Cygwin <cygwin@cygwin.com>
wrote:

> Hi,
>
> When I'm using some programs such as bash and python from cygwin under
> Windows Terminal, Ctrl+C is sometimes ignored.
> https://github.com/microsoft/terminal
>
> Normally holding 'Ctrl' and pressing 'C' will make new line.
> But, sometimes it does not and unholding 'Ctrl' makes new line under
> Windows Terminal.
> bash from msys2 does also reproduce this issue.
>
> I dug into this issue and found that this is related to
> readline and Windows 10's pseudo console (ConPTY).
> I made simple programs to reproduce this issue.
>
>  - EchoCon.cpp
>    - modification of ConPty sample code provided by Microsoft.
>      This program execute bash on pseudo console.
>      to be compiled with MSVC.
>  - getkey.cpp
>    - simple program to check Ctrl+C is passed to Cygwin program.
>      to be compiled with Cygwin gcc.
>  - rltest.cpp
>    - simple program to check SIGINT handling.
>      This program reproduces the issue.
>      If you replace readline("> ") with gets(buf),
>      then the issue does not happen.
>      to be compiled with Cygwin gcc.
>
> - The machine and OS that it is running on
>   - OS: Windows 10 Pro 19043.1288
>   - Windows Terminal: 1.11.2921.0
>
> Regards,
> Naoto Aoki
>
> --
> Problem reports:      https://cygwin.com/problems.html
> FAQ:                  https://cygwin.com/faq/
> Documentation:        https://cygwin.com/docs.html
> Unsubscribe info:     https://cygwin.com/ml/#unsubscribe-simple
>


-- 
Russell M. Van Tassell <russellvt@gmail.com>

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Ctrl+C is sometimes ignored on Windows Terminal
  2021-11-01  8:33 Ctrl+C is sometimes ignored on Windows Terminal Naoto Aoki
  2021-11-01  8:50 ` Russell VT
@ 2021-11-01 14:07 ` Doug Henderson
  2021-11-02  3:34 ` Takashi Yano
  2 siblings, 0 replies; 4+ messages in thread
From: Doug Henderson @ 2021-11-01 14:07 UTC (permalink / raw)
  To: cygwin

On Mon, 1 Nov 2021 at 02:35, Naoto Aoki via Cygwin <cygwin@cygwin.com> wrote:
>
> Hi,
>
> When I'm using some programs such as bash and python from cygwin under
> Windows Terminal, Ctrl+C is sometimes ignored.
> https://github.com/microsoft/terminal
>
snip


What do you expect to happen when you press Ctrl+C.
- break action aka keyboard interrupt?
- copy selection to clipboard?

Do you expect different behavior determined by whether you have some
text selected?

Is your cygwin installation fully updated via the cygwin setup.exe app?

You should specify the WT version, e.g. Version: 1.11.2921.0. By
default WT updates automatically.

Also, what is in the Action tab of WT Settings, and the Actions
section of the JSON settings at

"%LOCALAPPDATA%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json"

Also note that https://aka.ms/terminal-profiles-schema is imported at
the top of the settings file, so ...

IIRC, there is also a system wide settings file that is read before
your personal settings file, but I forget where it is. Or maybe there
are some builtin settings, eg Ctrl+Shift+C

HTH
Doug

-- 
Doug Henderson, Calgary, Alberta, Canada - from gmail.com

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Ctrl+C is sometimes ignored on Windows Terminal
  2021-11-01  8:33 Ctrl+C is sometimes ignored on Windows Terminal Naoto Aoki
  2021-11-01  8:50 ` Russell VT
  2021-11-01 14:07 ` Doug Henderson
@ 2021-11-02  3:34 ` Takashi Yano
  2 siblings, 0 replies; 4+ messages in thread
From: Takashi Yano @ 2021-11-02  3:34 UTC (permalink / raw)
  To: cygwin; +Cc: Naoto Aoki

On Mon, 1 Nov 2021 17:33:02 +0900
Naoto Aoki wrote:
> When I'm using some programs such as bash and python from cygwin under
> Windows Terminal, Ctrl+C is sometimes ignored.
> https://github.com/microsoft/terminal
> 
> Normally holding 'Ctrl' and pressing 'C' will make new line.
> But, sometimes it does not and unholding 'Ctrl' makes new line under
> Windows Terminal.
> bash from msys2 does also reproduce this issue.
> 
> I dug into this issue and found that this is related to
> readline and Windows 10's pseudo console (ConPTY).
> I made simple programs to reproduce this issue.

Thanks for the report.

I could reproduce your problem and identified the cause.
I will submit a patch to fix this issue shortly.

-- 
Takashi Yano <takashi.yano@nifty.ne.jp>

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-11-02  3:35 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-01  8:33 Ctrl+C is sometimes ignored on Windows Terminal Naoto Aoki
2021-11-01  8:50 ` Russell VT
2021-11-01 14:07 ` Doug Henderson
2021-11-02  3:34 ` Takashi Yano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).