Bill-Gray / PDCursesMod

Public Domain Curses - a curses library for environments that don't fit the termcap/terminfo model, modified and extended from the 'official' version
https://www.projectpluto.com/win32a.htm
338 stars 73 forks source link

[WinGUI] getch() returns two characters for Ctrl+^ #323

Open rhaberkorn opened 3 weeks ago

rhaberkorn commented 3 weeks ago

When pressing Ctrl+^ on an US layout, ie. Ctrl+Shift+6, wgetch() actually returns two keys. first 54 ("6") and then 30. This does not happen with the Wincon port.

GitMensch commented 3 weeks ago

Thank you for that notice. What are your build commands for PDCursesMod (WIDE/UTF8)?

rhaberkorn commented 3 weeks ago

Thank you for that notice. What are your build commands for PDCursesMod (WIDE/UTF8)?

I was using the MSYS package, ie. v4.4.0. This builds with WIDE=Y and UTF8=Y (see PKGBUILD).

If necessary, I can build from scratch. But I didn't see any commit in the repo that would promise a different behavior.

Bill-Gray commented 3 weeks ago

Interesting. I'm seeing the same thing, using the "Input Test" section of testcurs. It applies to Ctrl-Shift-6 = Ctrl-^, and not to Ctrl-Shift-other numbers; those are okay.

After some testing, including use of a 'keylook' utility from the mid-1990s written by Charles Petzold (remember him? You must be old... like me), it appears there are three Ctrl-Shift combinations that (unlike other Ctrl-Shift combos) emit a WM_CHAR event.

Ctrl-Shift-6 = Ctrl-^ causes a WMCHAR event with a value of 30; Ctrl-Shift-` = Ctrl-~ causes the same thing; Ctr-Shift-- (that's a minus) = Ctrl- causes a WM_CHAR event with a value of 31.

It looks to me as if these three combinations are similar to Shift-Tab, in that we get the key reported twice and have to suppress one of them. The logical place to do it is to add it to the existing code to handle Shift-Tab. That looks like a one-line modification:

diff --git a/wingui/pdcscrn.c b/wingui/pdcscrn.c
index 159f2c62..6c1f4172 100644
--- a/wingui/pdcscrn.c
+++ b/wingui/pdcscrn.c
@@ -1921,7 +1921,7 @@ static LRESULT ALIGN_STACK CALLBACK WndProc (const HWND hwnd,
     case WM_CHAR:       /* _Don't_ add Shift-Tab;  it's handled elsewhere */
         if( wParam == 3 && !SP->raw_inp)     /* Ctrl-C hit */
             exit( 0);
-        if( wParam != 9 || !(GetKeyState( VK_SHIFT) & 0x8000))
+        if( (wParam != 9 && wParam != 30 && wParam != 31) || !(GetKeyState( VK_SHIFT) & 0x8000))
             if( !key_already_handled)
                add_key_to_queue( (int)wParam );
         key_already_handled = FALSE;

With the caveat that this has been tested and works on Wine. If you tell me it works on "real" Windows, I'll commit and push.

rhaberkorn commented 3 weeks ago

No, unfortunately it doesn't work. Here is Ctrl+Shift+6 at the current HEAD:

testcurs-wo-patch

And here is it with your patch:

testcurs-with-patch

In other words, only "6" is returned but no code 30.

rhaberkorn commented 3 weeks ago

In other words, only "6" is returned but no code 30.

On Wine I can observe exactly the same.

Bill-Gray commented 2 weeks ago

Actually, that's what I would have expected to see. I gather you were wanting things the other way around (suppress Ctrl-Shift-6, but keep the Ctrl-^)?

The problem I see with that is consistency. Ctrl-Shift-0 through 5, and Ctrl-Shift-7 to 9, return those values. I'd want Ctrl-Shift-6 to do likewise.

rhaberkorn commented 2 weeks ago

IMHO you should look how UNIX terminal emulators behave. Curses is originally a UNIX thing.

I would expect that it converts those symbols to control codes, where a control code exists. So Ctrl+A becomes 1 and Ctrl+^ becomes 30 (ASCII & ~0x40). It shouldn't play any role that some keyboard layouts (like the US layout) need an additional shift. The German layout for instance doesn't. Similarily Ctrl+@ should generate code 0. On UNIX not all terminal emulators behave consistently in this regard. They don't insert 0 on Ctrl+@, but generally insert 30 on Ctrl+^ (even with an US keyboard layout). Ctrl+@ might be a Curses limitation.

Where no control code exists, getch() should not deliver any key press at all.

Bill-Gray commented 1 week ago

Um. Looks as if we have a whole slew of problems involving Ctrl-Shift-numbers. It's not just Ctrl-Shift-6, and it's not just WinGUI. Just tried Ctrl-Shift-0 through 9 in testcurs on a variety of ports :

With WinGUI, Ctrl-Shift-Number gets you 'number' with the Shift and Ctrl flags set. (And, as you note, Ctrl-Shift-6 also gets you Ctrl-^ = 0x1e, also with those flags set.)

With ncurses and VT, you get the shifted character (! for 1, # for 3, etc.), except that Ctrl-Shift-2 gets ^@ = 0 and Ctrl-Shift-6 gets you Ctrl-^ = 0x1e. Ditto for X11, except that Ctrl-Shift-2 gets no response and Ctrl-Shift-6 gets ^ = 0x5e.

WinCon and SDL1 respond with the shifted character, except that Ctrl-Shift-2 gets no response and Ctrl-Shift-6 gets Ctrl-^ = 0x1e.

SDL2 responds with both the number and the shifted character. (Except that for 2 and 6, only the number.) SDL2 key handling has long been a source of weird bugs...

Tested thus far only with a US (well, Dvorak) layout. With, say, a UK layout, Shift-2 ought to get you £, not @, and Ctrl-Shift-2 should get your Ctrl-£ (not sure what that would be), but haven't checked that yet. Nor have I tried out the DOS, DOSVGA, OS/2, framebuffer, or DRM ports (though the last two use essentially the same code for keyboard input as VT and almost certainly do what it does.)

I'm inclined to regard ncurses as "normative" here, which would line up with your thought that (getting back to the title issue) Ctrl-Shift-6 should return Ctrl-^ = 0x1e in WinGUI (and elsewhere).

rhaberkorn commented 1 week ago

With WinGUI, Ctrl-Shift-Number gets you 'number' with the Shift and Ctrl flags set.

If PDCursesMod would behave consistently in this regard, I could work around the incompatibility using PDC_get_key_modifiers(). Let's say it would always return the shifted number, then it would be quite easy to check the modifiers as well and calculate the control code.

But if you are planning to behave like on ncurses, this won't be necessary.

I'm inclined to regard ncurses as "normative" here

I guess it's actually the terminal emulator that produces the control codes, but I might be mistaken.