Skip to content

Conversation

@dkwingsmt
Copy link
Contributor

@dkwingsmt dkwingsmt commented Mar 2, 2021

This PR fixes a problem where IME events cause crash in Win32 apps.

This fix that was once landed in flutter/engine#23853 had a flaw: because on Windows, only down events of IME operations has VK_PROCESSKEY as keyCode, while the accompanying up event uses a normal keycode, filtering events in engine using VK_PROCESSKEY does not exclude the up events.

This PR instead filters these events in the framework, since the RawKeyboard API keeps states in the framework.

(For the new HardwareKeyboard API, the filtering will be done in the embedding converter.)

Fixes #74819

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I signed the CLA.
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@flutter-dashboard flutter-dashboard bot added the framework flutter/packages/flutter repository. See also f: labels. label Mar 2, 2021
@google-cla google-cla bot added the cla: yes label Mar 2, 2021
@dkwingsmt dkwingsmt requested a review from cbracken March 2, 2021 06:10
Copy link
Member

@cbracken cbracken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm. Thank you!

@wxw-9527
Copy link

wxw-9527 commented Jun 29, 2022

On Windows platform, I want to listen to the down event of any key use RawKeyboard.instance.addListener(_handleKey), including when the input method is displayed, but the following code skips the callback function.what should I do? @dkwingsmt Thank You.

/// Process a new [RawKeyEvent] by recording the state changes and
  /// dispatching to listeners.
  bool handleRawKeyEvent(RawKeyEvent event) {
    bool shouldDispatch = true;
    if (event is RawKeyDownEvent) {
      if (event.data.shouldDispatchEvent()) {
        _keysPressed[event.physicalKey] = event.logicalKey;
      } else {
        shouldDispatch = false;
        _hiddenKeysPressed.add(event.physicalKey);
      }
    } else if (event is RawKeyUpEvent) {
      if (!_hiddenKeysPressed.contains(event.physicalKey)) {
        // Use the physical key in the key up event to find the physical key from
        // the corresponding key down event and remove it, even if the logical
        // keys don't match.
        _keysPressed.remove(event.physicalKey);
      } else {
        _hiddenKeysPressed.remove(event.physicalKey);
        shouldDispatch = false;
      }
    }
    if (!shouldDispatch) {
      return true;
    }
    // Make sure that the modifiers reflect reality, in case a modifier key was
    // pressed/released while the app didn't have focus.
    _synchronizeModifiers(event);
    assert(
      event is! RawKeyDownEvent || _keysPressed.isNotEmpty,
      'Attempted to send a key down event when no keys are in keysPressed. '
      "This state can occur if the key event being sent doesn't properly "
      'set its modifier flags. This was the event: $event and its data: '
      '${event.data}',
    );
    // Send the event to passive listeners.
    for (final ValueChanged<RawKeyEvent> listener in List<ValueChanged<RawKeyEvent>>.of(_listeners)) {
      try {
        if (_listeners.contains(listener)) {
          listener(event);
        }
      } catch (exception, stack) {
        InformationCollector? collector;
        assert(() {
          collector = () => <DiagnosticsNode>[
            DiagnosticsProperty<RawKeyEvent>('Event', event),
          ];
          return true;
        }());
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: ErrorDescription('while processing a raw key listener'),
          informationCollector: collector,
        ));
      }
    }

    return false;
  }

@dkwingsmt dkwingsmt deleted the framework-win-ime-check branch June 30, 2022 03:22
@dkwingsmt
Copy link
Contributor Author

dkwingsmt commented Jun 30, 2022

@wxw-9527 This is a tricky problem, because even if we somehow hack into RawKeyboardWindows, our new keyboard system (accessible via HardwareKeyboard) also skips these events and does it at in the engine, which you can not hack into. And since other platforms use different ways to ensure keyboard events work well with IMEs, any solution that specifically targets at VK_PROCESSKEY will not be portable.
Can you tell me what you want to achieve with this?

@wxw-9527
Copy link

wxw-9527 commented Jun 30, 2022

Thank you for your reply.
I need to develop a windows program to parse the data provided by the Scanning Gun.But when starting the Chinese input method,if (!shouldDispatch) { return true; } will jump out of this method.
Now I solved my needs in other ways,It was cumbersome, but solved my problem.
http://t.csdn.cn/QDUNO

@dkwingsmt
Copy link
Contributor Author

I see what you're doing here. (By the way, your current short link is broken.)

If you can hack into the engine you might want to try ImmDisableIME, which must be called before the window is created and disables IME for the entire window. Not sure if it's too strong for you.

@wxw-9527
Copy link

You're great.....(给大哥跪了)
I use Focus() do what I want, but I just discovered, you have fixed it..... #104244
Now I have to think about using ImmDisableIME.
Can you help me, how to use ImmDisableIME method to disable IME? Thank you very much.

(Google Translate)

@dkwingsmt
Copy link
Contributor Author

总的来说,你需要让flutter引擎在程序的窗口创建之前运行win32的ImmDisableIME函数(不需要参数)。我一时能想到有三种方案:

  1. 自己修改引擎,然后找到创建窗口前的位置插这么一行就行。优点是100%理论可行,缺点是需要自己编译引擎并且以后需要自己rebase。
  2. 写一个插件(flutter plugin),插件不需要开放任何接口,只需要调用这个函数。我猜测这个函数应该是在创建窗口前调用的,但不确定。优点是漂亮,甚至可以拿到pub.dev上发成库。缺点是我不确定是不是真的可行。
  3. 修改应用里的runner,然后在调用flutter的任何函数前调用这个函数。优点是简单(应该可行吧……),缺点是……没有2漂亮(?)。

具体怎么做我现在在手机上看不到。如果你摸索不出来我们可以再讨论。

@wxw-9527
Copy link

非常感谢您的回复!我试一试。
(题外话,你可真是个让人又爱又恨的男人。。。把路堵的死死的。)

@wxw-9527
Copy link

wxw-9527 commented Sep 23, 2022

I found the documentation for the ImmDisableIME function, but I didn't find the right place to call this function, because it requires parameters I didn't find in any file under the windows/runner path.

@wxw-9527
Copy link

I'm very sorry to trouble you again.
Because I don't know anything about Windows native development. I tried many times, Including asking questions on stackoverflow, But after adding ImmDisableIME(-1);, it will report an error when compiling.

@dkwingsmt
Copy link
Contributor Author

What's the compiling error?

@wxw-9527
Copy link

use ImmDisableIME(-1);:
mian.cpp
win32_window.cpp

Exception when using DWORD dwParam = (DWORD)(-1); ImmDisableIME(dwParam);:
win32_window.obj : error LNK2019: �޷��������ⲿ���� ImmDisableIME������ "public: bool __cdecl Win32Window::CreateAndShow(class std::basic_string<wchar_t,struct std::char_traits<wchar_t>,class std::allocator<wchar_t> > const &,struct Win32Window::Point const &,struct Win32Window::Size const &)" (?CreateAndShow@Win32Window@@QEAA_NAEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@AEBUPoint@1@AEBUSize@1@@Z) �������˸÷��� [W:\WorkSpace\RouXinPai\workpad\build\windows\runner\workpad.vcxproj] W:\WorkSpace\RouXinPai\workpad\build\windows\runner\Debug\workpad.exe : fatal error LNK1120: 1 ���޷��������ⲿ���� [W:\WorkSpace\RouXinPai\workpad\build\windows\runner\workpad.vcxproj] Exception: Build process failed.

@dkwingsmt
Copy link
Contributor Author

I think it's because you didn't provide the correct lib file to link ImmDisableIME. Maybe try adding

#pragma comment(lib, "imm32.lib")

https://blog.csdn.net/sleep_0/article/details/50452483

@wxw-9527
Copy link

Thanks for your help.
After adding #pragma comment(lib, "imm32.lib") to the file header of win32_window.cpp, add ImmDisableIME(GetCurrentThreadId()) in CreateAndShow method, can disable the IME.
Thanks again.

@dkwingsmt
Copy link
Contributor Author

Awesome! Glad to know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: text input Entering text in a text field or keyboard related problems framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[windows desktop]Korean IME In hanja(한자) key problem

4 participants