Alt + numpad combination does not work anymore #21780

Closed
opened 2026-01-31 07:54:42 +00:00 by claunia · 8 comments
Owner

Originally created by @raksati on GitHub (May 28, 2024).

Windows Terminal version

1.21.1272.0

Windows build number

10.0.22631.0

Other Software

wsl terminal, powershell, etc.

Steps to reproduce

Sequential numpad keys combinations with the ALT key pressed to insert special characters ( for example the most common ones such as { with ALT+123, ~ with ALT+126, or the tilde ~ with ALT+126, etc. ) do not work anymore.

The latest working version is Microsoft.WindowsTerminal.Preview 1.20.11215.0, the next current one (1.21.1272.0) broke this functionality.

Expected Behavior

On previous versions the combination works

raksati@wsl:$ showkey -a

Press any keys - Ctrl-D will terminate this program

` 96 0140 0x60

^D 4 0004 0x04
raksati@wsl:~$

Actual Behavior

nothing is detected:

raksati@wsl:$ showkey -a

Press any keys - Ctrl-D will terminate this program

^D 4 0004 0x04
raksati@wsl:~$

Originally created by @raksati on GitHub (May 28, 2024). ### Windows Terminal version 1.21.1272.0 ### Windows build number 10.0.22631.0 ### Other Software wsl terminal, powershell, etc. ### Steps to reproduce Sequential numpad keys combinations with the ALT key pressed to insert special characters ( for example the most common ones such as `{` with ALT+123, `~` with ALT+126, or the tilde ~ with ALT+126, etc. ) do not work anymore. The latest working version is Microsoft.WindowsTerminal.Preview 1.20.11215.0, the next current one (1.21.1272.0) broke this functionality. ### Expected Behavior On previous versions the combination works raksati@wsl:$ showkey -a Press any keys - Ctrl-D will terminate this program ` 96 0140 0x60 ^D 4 0004 0x04 raksati@wsl:~$ ### Actual Behavior nothing is detected: raksati@wsl:$ showkey -a Press any keys - Ctrl-D will terminate this program ^D 4 0004 0x04 raksati@wsl:~$
Author
Owner

@j4james commented on GitHub (May 28, 2024):

Thanks for the report!

I suspect this might be related to PR #17067. There are a couple of places in the code that mention Alt-Numpad being handled in TSFInputControl. For example see here:

5d1cf1a704/src/cascadia/TerminalApp/TerminalPage.cpp (L1439-L1442)

But TSFInputControl has now been replaced, so perhaps that functionality was lost in the rewrite.

/cc @lhecker

@j4james commented on GitHub (May 28, 2024): Thanks for the report! I suspect this might be related to PR #17067. There are a couple of places in the code that mention Alt-Numpad being handled in `TSFInputControl`. For example see here: https://github.com/microsoft/terminal/blob/5d1cf1a704986808d09ae7729aaed39ce5d5f909/src/cascadia/TerminalApp/TerminalPage.cpp#L1439-L1442 But `TSFInputControl` has now been replaced, so perhaps that functionality was lost in the rewrite. /cc @lhecker
Author
Owner

@lhecker commented on GitHub (May 28, 2024):

It works in conhost because that one doesn't rely on the WinRT TSF stack via TextInputHost, and so it'll receive the Alt+Numpad key regularly via WM_CHAR. The weirdest thing is that even if I remove the new TSF implementation entirely (doesn't get instantiated) it'll not fix the issue. Does that mean the only way to get Alt+Numpad keys in a WinUI app is by using CoreTextServicesManager? That'd be quite surprising and kind of sad, because we fundamentally can't use that class.

@lhecker commented on GitHub (May 28, 2024): It works in conhost because that one doesn't rely on the WinRT TSF stack via TextInputHost, and so it'll receive the Alt+Numpad key regularly via `WM_CHAR`. The weirdest thing is that even if I remove the new TSF implementation entirely (doesn't get instantiated) it'll not fix the issue. Does that mean the only way to get Alt+Numpad keys in a WinUI app is by using `CoreTextServicesManager`? That'd be quite surprising and kind of sad, because we fundamentally can't use that class.
Author
Owner

@lhecker commented on GitHub (May 28, 2024):

Wow. Just after I wrote that I found that switching my keyboard layout to Korean or any other "complex" language fixes the issue. What in the heck!

@lhecker commented on GitHub (May 28, 2024): Wow. Just after I wrote that I found that switching my keyboard layout to Korean or any other "complex" language fixes the issue. What in the heck!
Author
Owner

@lhecker commented on GitHub (May 29, 2024):

So, with all that said, I think it'd be way easier to just write our own Alt+Numpad support in TermControl from scratch. ConEmu got all the info we need: https://conemu.github.io/en/AltNumpad.html

If anyone wants to give it a shot, please do so!


Windows internals for the curious follow below:

I spent an unfathomable amount of time debugging this. 😄
This happens basically because the UWP implementation of TSF does not like its own non-UWP sibling very much and switches off when it sees it.

I tried solving this issue by adding this to TermControl:

// The remaining callbacks must be implemented because SendInput should only be called if there's no active composition.
const auto manager = Windows::UI::Text::Core::CoreTextServicesManager::GetForCurrentView();
_editContext = manager.CreateEditContext();
_editContext.TextUpdating([this](auto&&, auto&& args) { SendInput(args.Text()); });
_editContext.TextRequested([](auto&&, auto&&) {});
_editContext.SelectionRequested([](auto&&, auto&&) {});

(And NotifyFocusEnter() / NotifyFocusLeave() calls must be added of course.)
The problem is that for some mysterious reason these callbacks won't get called if our new TSF implementation got focus simultaneously (which it needs because otherwise we have no IME).

We call ITfDocumentMgr::CreateContext here:
13de7c6685/src/tsf/Implementation.cpp (L42)

It's implemented by CDocumentInputManager::CreateContext which calls QueryInterface on our ITfContextOwnerCompositionSink to check if it implements ITextInputClientOwner (86b4403f-c187-4a8b-b13e-c93c4ad1078c) and passes that as a bool to the CInputContext instance that's returned as the "context".

(The TSF context is mostly an adapter between the public msctf.h interfaces and the private implementation. We need our own context because we need TS_SS_TRANSITORY. See Implementation.cpp for lots of details about this.)

CInputContext::GetStatus then adapts our Implementation::GetStatus somewhat like this:

HRESULT CInputContext::GetStatus(TS_STATUS* status)
{
    YOUR_IMPLEMENTATION->GetStatus(status);
    if (YOU_IMPLEMENTED_THE_ITextInputClientOwner_INTERFACE?)
        status->dwStaticFlags |= TS_SS_UWPCONTROL;
    else
        status->dwStaticFlags &= ~TS_SS_UWPCONTROL;
}

The existence of this TS_SS_UWPCONTROL is what makes and breaks not just the Alt+Numpad support, but seemingly any support for any input whatsoever. If that flag is missing (= no TSF3), then somewhere something between TextInputHost and CoreTextEditContext breaks down. I couldn't quite figure out where and what exactly.

I think that's a bug in the TSF implementation. The lack of Alt+Numpad support is likely a fallout from this, because when that flag is missing and TextInputHost is not sending any input anymore, then it should also re-enable sending Alt+Numpad via WM_CHAR, just like how it works for regular Win32 applications.

In any case, if I pass any IUnknown instance as the ITextInputClientOwner in our Implementation, then that will fix the issue with the context that WinUI holds, because TextInputHost will then send messages again.

The only problem with all that is that it doesn't solve the focus problem: To make this work we need to give both WinUI's context (for Alt+Numpad) and our context (= our IME) focus at the same, which means that both will receive the same input at the same time. That's really really bad, because TSF3 doesn't use TS_SS_TRANSITORY and I don't want to deal with the terminal-specific issues that this results in.

@lhecker commented on GitHub (May 29, 2024): So, with all that said, I think it'd be _way_ easier to just write our own Alt+Numpad support in `TermControl` from scratch. ConEmu got all the info we need: https://conemu.github.io/en/AltNumpad.html If anyone wants to give it a shot, please do so! --- Windows internals for the curious follow below: I spent an unfathomable amount of time debugging this. 😄 This happens basically because the UWP implementation of TSF does not like its own non-UWP sibling very much and switches off when it sees it. I tried solving this issue by adding this to `TermControl`: ```cpp // The remaining callbacks must be implemented because SendInput should only be called if there's no active composition. const auto manager = Windows::UI::Text::Core::CoreTextServicesManager::GetForCurrentView(); _editContext = manager.CreateEditContext(); _editContext.TextUpdating([this](auto&&, auto&& args) { SendInput(args.Text()); }); _editContext.TextRequested([](auto&&, auto&&) {}); _editContext.SelectionRequested([](auto&&, auto&&) {}); ``` (And `NotifyFocusEnter()` / `NotifyFocusLeave()` calls must be added of course.) The problem is that for some mysterious reason these callbacks won't get called if our new TSF implementation got focus simultaneously (which it needs because otherwise we have no IME). We call `ITfDocumentMgr::CreateContext` here: https://github.com/microsoft/terminal/blob/13de7c668577ae270ce9528d6a895fd169ece7cc/src/tsf/Implementation.cpp#L42 It's implemented by `CDocumentInputManager::CreateContext` which calls `QueryInterface` on our `ITfContextOwnerCompositionSink` to check if it implements `ITextInputClientOwner` (`86b4403f-c187-4a8b-b13e-c93c4ad1078c`) and passes that as a `bool` to the `CInputContext` instance that's returned as the "context". (The TSF context is mostly an adapter between the public msctf.h interfaces and the private implementation. We need our own context because we need `TS_SS_TRANSITORY`. See `Implementation.cpp` for lots of details about this.) `CInputContext::GetStatus` then adapts our `Implementation::GetStatus` somewhat like this: ```cpp HRESULT CInputContext::GetStatus(TS_STATUS* status) { YOUR_IMPLEMENTATION->GetStatus(status); if (YOU_IMPLEMENTED_THE_ITextInputClientOwner_INTERFACE?) status->dwStaticFlags |= TS_SS_UWPCONTROL; else status->dwStaticFlags &= ~TS_SS_UWPCONTROL; } ``` The existence of this `TS_SS_UWPCONTROL` is what makes and breaks not just the Alt+Numpad support, but seemingly any support for any input whatsoever. If that flag is missing (= no TSF3), then somewhere something between `TextInputHost` and `CoreTextEditContext` breaks down. I couldn't quite figure out where and what exactly. I think that's a bug in the TSF implementation. The lack of Alt+Numpad support is likely a fallout from this, because when that flag is missing and `TextInputHost` is not sending any input anymore, then it should also re-enable sending Alt+Numpad via `WM_CHAR`, just like how it works for regular Win32 applications. In any case, if I pass any `IUnknown` instance as the `ITextInputClientOwner` in **our** `Implementation`, then that will fix the issue with the context that WinUI holds, because `TextInputHost` will then send messages again. The only problem with all that is that it doesn't solve the focus problem: To make this work we need to give both WinUI's context (for Alt+Numpad) and our context (= our IME) focus at the same, which means that both will receive the same input at the same time. That's really really bad, because TSF3 doesn't use `TS_SS_TRANSITORY` and I don't want to deal with the terminal-specific issues that this results in.
Author
Owner

@inoperable commented on GitHub (Jul 4, 2024):

If this is not a joke, can i quote your rationale?

@inoperable commented on GitHub (Jul 4, 2024): If this is _not_ a joke, can i quote your rationale?
Author
Owner

@inoperable commented on GitHub (Jul 4, 2024):

   ╔═════╗
   ║ Duh ║
o.O╜═════╝

So, whats the best way to achieve something more complex, say √² or https://brltty.app/details.html

...

@inoperable commented on GitHub (Jul 4, 2024): ``` ╔═════╗ ║ Duh ║ o.O╜═════╝ ``` So, whats the best way to achieve something more complex, say √² or https://brltty.app/details.html ...
Author
Owner

@lhecker commented on GitHub (Jul 8, 2024):

Rationale for what? And what exactly did you feel like was a joke?

The way you're phrasing it, it sounds like you believe I have no intention of seeing this fixed, but literally the very first paragraph of what I wrote explains how we should do it. 😅

@lhecker commented on GitHub (Jul 8, 2024): Rationale for what? And what exactly did you feel like was a joke? The way you're phrasing it, it sounds like you believe I have no intention of seeing this fixed, but literally the very first paragraph of what I wrote explains how we should do it. 😅
Author
Owner

@inoperable commented on GitHub (Jul 8, 2024):

Right. Hast je ein terminal verwendet in deinem Leben der weniger mühsam
ist als er?

On Mon, Jul 8, 2024, 19:38 Leonard Hecker @.***> wrote:

Rationale for what? And what exactly did you feel like was a joke?

The way you're phrasing it, it sounds like you believe I have no intention
of seeing this fixed, but literally the very first paragraph of what I
wrote explains how we should do it. 😅


Reply to this email directly, view it on GitHub
https://github.com/microsoft/terminal/issues/17327#issuecomment-2214796236,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAL7SAW5DGMZ3H2N643JBXLZLLFBBAVCNFSM6AAAAABIMKLFJKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMJUG44TMMRTGY
.
You are receiving this because you commented.Message ID:
@.***>

@inoperable commented on GitHub (Jul 8, 2024): Right. Hast je ein terminal verwendet in deinem Leben der weniger mühsam ist als er? On Mon, Jul 8, 2024, 19:38 Leonard Hecker ***@***.***> wrote: > Rationale for what? And what exactly did you feel like was a joke? > > The way you're phrasing it, it sounds like you believe I have no intention > of seeing this fixed, but literally the very first paragraph of what I > wrote explains how we should do it. 😅 > > — > Reply to this email directly, view it on GitHub > <https://github.com/microsoft/terminal/issues/17327#issuecomment-2214796236>, > or unsubscribe > <https://github.com/notifications/unsubscribe-auth/AAL7SAW5DGMZ3H2N643JBXLZLLFBBAVCNFSM6AAAAABIMKLFJKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMJUG44TMMRTGY> > . > You are receiving this because you commented.Message ID: > ***@***.***> >
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#21780