Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speak typed characters in UWP apps on Windows 10 RS2 and above #6631

Merged
merged 6 commits into from Jan 23, 2017
22 changes: 22 additions & 0 deletions source/keyboardHandler.py
Expand Up @@ -7,11 +7,15 @@

"""Keyboard support"""

import ctypes
import sys
import time
import re
import wx
import winVersion
import winUser
import vkCodes
import eventHandler
import speech
import ui
from keyLabels import localizedKeyLabels
Expand Down Expand Up @@ -73,6 +77,7 @@ def isNVDAModifierKey(vkCode,extended):
def internal_keyDownEvent(vkCode,scanCode,extended,injected):
"""Event called by winInputHook when it receives a keyDown.
"""
gestureExecuted=False
try:
global lastNVDAModifier, lastNVDAModifierReleaseTime, bypassNVDAModifier, passKeyThroughCount, lastPassThroughKeyDown, currentModifiers, keyCounter, stickyNVDAModifier, stickyNVDAModifierLocked
# Injected keys should be ignored in some cases.
Expand Down Expand Up @@ -146,6 +151,7 @@ def internal_keyDownEvent(vkCode,scanCode,extended,injected):

try:
inputCore.manager.executeGesture(gesture)
gestureExecuted=True
trappedKeys.add(keyCode)
if canModifiersPerformAction(gesture.generalizedModifiers):
# #3472: These modifiers can perform an action if pressed alone
Expand All @@ -162,6 +168,22 @@ def internal_keyDownEvent(vkCode,scanCode,extended,injected):
return False
except:
log.error("internal_keyDownEvent", exc_info=True)
finally:
Copy link
Contributor

Choose a reason for hiding this comment

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

Might be worth commenting why we do this in the finally block; i.e. because we return early in several places but we still want this to be executed.

# #6017: handle typed characters for UWP apps in Win10 RS2 and above where we can't detect typed characters in-process
# This code must be in the 'finally' block as code above returns in several places yet we still want to execute this particular code.
focus=api.getFocusObject()
if winVersion.winVersion.build>=14986 and not gestureExecuted and not isNVDAModifierKey(vkCode,extended) and not vkCode in KeyboardInputGesture.NORMAL_MODIFIER_KEYS and focus.windowClassName.startswith('Windows.UI.Core'):
keyStates=(ctypes.c_byte*256)()
for k in xrange(256):
keyStates[k]=ctypes.windll.user32.GetKeyState(k)
charBuf=ctypes.create_unicode_buffer(5)
hkl=ctypes.windll.user32.GetKeyboardLayout(focus.windowThreadID)
# In previous Windows builds, calling ToUnicodeEx would destroy keyboard buffer state and therefore cause the app to not produce the right WM_CHAR message.
# However, ToUnicodeEx now can take a new flag of 0x4, which stops it from destroying keyboard state, thus allowing us to safely call it here.
res=ctypes.windll.user32.ToUnicodeEx(vkCode,scanCode,keyStates,len(charBuf),0x4,hkl)
if res>0:
for ch in charBuf[:res]:
eventHandler.queueEvent("typedCharacter",focus,ch=ch)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this saying there can be multiple separate characters? I know there are languages where a single key press produces a conjunct Unicode character sequence (e.g. Tamil), but we probably actually want that to be handled as one character. We can't do that with WM_CHAR right now, but is there a reason we shouldn't do this here since we can?

Copy link
Member Author

Choose a reason for hiding this comment

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

I have certainly seen it produce at least 2 characters yes. For instance, if a dead key is hit, followed by a character that is not compatible with a dead key, then you get a char for the dead key (say ^) and also the actual character.

return True

def internal_keyUpEvent(vkCode,scanCode,extended,injected):
Expand Down