~stomato463/+junk/nvdajp

« back to all changes in this revision

Viewing changes to source/keyboardHandler.py

  • Committer: Masataka Shinke
  • Date: 2011-10-25 12:35:26 UTC
  • mfrom: (4185 jpmain)
  • mto: This revision was merged to the branch mainline in revision 4211.
  • Revision ID: mshinke@users.sourceforge.jp-20111025123526-ze527a2rl3z0g2ky
lp:~nishimotz/nvdajp/main : 4185 をマージ

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#keyboardHandler.py
 
1
#keyboardHandler.py
2
2
#A part of NonVisual Desktop Access (NVDA)
3
 
#Copyright (C) 2006-2009 NVDA Contributors <http://www.nvda-project.org/>
4
3
#This file is covered by the GNU General Public License.
5
4
#See the file COPYING for more details.
 
5
#Copyright (C) 2006-2010 Michael Curran <mick@kulgan.net>, James Teh <jamie@jantrid.net>, Peter Vágner <peter.v@datagate.sk>, Aleksey Sadovoy <lex@onm.su>
6
6
 
7
7
"""Keyboard support"""
8
8
 
9
 
import locale
 
9
import time
 
10
import wx
10
11
import winUser
11
 
import ctypes
12
 
import time
13
12
import vkCodes
14
13
import speech
15
 
from keyUtils import key, keyName, sendKey, localizedKeyLabels
16
 
import scriptHandler
17
 
import globalVars
 
14
import ui
 
15
from keyLabels import localizedKeyLabels
18
16
from logHandler import log
19
17
import queueHandler
20
18
import config
21
19
import api
22
20
import winInputHook
23
 
import watchdog
24
 
import nvdajp_keyEvents # Masataka.Shinke
25
 
 
26
 
keyUpIgnoreSet=set()
27
 
passKeyThroughCount=-1 #If 0 or higher then key downs and key ups will be passed straight through
28
 
lastPassThroughKeyDown=None
29
 
NVDAModifierKey=None
30
 
usedNVDAModifierKey=False
31
 
lastNVDAModifierKey=None
32
 
lastNVDAModifierKeyTime=None
33
 
unpauseByShiftUp=False
 
21
import inputCore
 
22
 
 
23
# Fake vk codes.
 
24
# These constants should be assigned to the name that NVDA will use for the key.
 
25
VK_WIN = "windows"
 
26
 
 
27
#: Keys which have been trapped by NVDA and should not be passed to the OS.
 
28
trappedKeys=set()
 
29
#: Tracks the number of keys passed through by request of the user.
 
30
#: If -1, pass through is disabled.
 
31
#: If 0 or higher then key downs and key ups will be passed straight through.
 
32
passKeyThroughCount=-1
 
33
#: The last key down passed through by request of the user.
 
34
lastPassThroughKeyDown = None
 
35
#: The last NVDA modifier key that was pressed with no subsequent key presses.
 
36
lastNVDAModifier = None
 
37
#: When the last NVDA modifier key was released.
 
38
lastNVDAModifierReleaseTime = None
 
39
#: Indicates that the NVDA modifier's special functionality should be bypassed until a key is next released.
 
40
bypassNVDAModifier = False
 
41
#: The modifiers currently being pressed.
 
42
currentModifiers = set()
 
43
#: A counter which is incremented each time a key is pressed.
 
44
#: Note that this may be removed in future, so reliance on it should generally be avoided.
 
45
#: @type: int
 
46
keyCounter = 0
34
47
 
35
48
def passNextKeyThrough():
36
 
        global passKeyThroughCount, lastPassThroughKeyDown
 
49
        global passKeyThroughCount
37
50
        if passKeyThroughCount==-1:
38
51
                passKeyThroughCount=0
39
 
                lastPassThroughKeyDown=None
40
52
 
41
53
def isNVDAModifierKey(vkCode,extended):
42
54
        if config.conf["keyboard"]["useNumpadInsertAsNVDAModifierKey"] and vkCode==winUser.VK_INSERT and not extended:
50
62
        else:
51
63
                return False
52
64
 
53
 
def speakToggleKey(vkCode):
54
 
        toggleState=bool(not winUser.getKeyState(vkCode)&1)
55
 
        if vkCode==winUser.VK_CAPITAL:
56
 
                        queueHandler.queueFunction(queueHandler.eventQueue,speech.speakMessage,_("caps lock %s")%(_("on") if toggleState else _("off")))
57
 
        elif vkCode==winUser.VK_NUMLOCK:
58
 
                        queueHandler.queueFunction(queueHandler.eventQueue,speech.speakMessage,_("num lock %s")%(_("on") if toggleState else _("off")))
59
 
        elif vkCode==winUser.VK_SCROLL:
60
 
                        queueHandler.queueFunction(queueHandler.eventQueue,speech.speakMessage,_("scroll lock %s")%(_("on") if toggleState else _("off")))
61
 
 
62
65
def internal_keyDownEvent(vkCode,scanCode,extended,injected):
63
 
        """Event called by keyHook when it receives a keyDown. It sees if there is a script tied to this key and if so executes it. It also handles the speaking of characters, words and command keys.
64
 
"""
 
66
        """Event called by winInputHook when it receives a keyDown.
 
67
        """
65
68
        try:
66
 
                global NVDAModifierKey, usedNVDAModifierKey, lastNVDAModifierKey, lastNVDAModifierKeyTime, passKeyThroughCount, lastPassThroughKeyDown, unpauseByShiftUp
 
69
                global lastNVDAModifier, lastNVDAModifierReleaseTime, bypassNVDAModifier, passKeyThroughCount, lastPassThroughKeyDown, currentModifiers, keyCounter
67
70
                #Injected keys should be ignored
68
71
                if injected:
69
72
                        return True
70
 
                # IF we're passing keys through, increment the pass key through count,
71
 
                # but only if this isn't a repeat of the previous key down, as we don't receive key ups for repeated key downs.
72
 
                if passKeyThroughCount>=0 and lastPassThroughKeyDown!=(vkCode,extended):
73
 
                        passKeyThroughCount+=1
74
 
                        lastPassThroughKeyDown=(vkCode,extended)
75
 
                        return True
76
 
                if watchdog.isAttemptingRecovery:
77
 
                        # The core is dead, so let keys pass through unhindered.
78
 
                        return True
79
 
                focusObject=api.getFocusObject()
80
 
                focusAppModule=focusObject.appModule
81
 
                if focusAppModule and focusAppModule.selfVoicing:
82
 
                        return True
83
 
                #pass the volume controlling keys
84
 
                if extended and vkCode >= winUser.VK_VOLUME_MUTE and vkCode <= winUser.VK_VOLUME_UP: return True
85
 
                vkName=vkCodes.byCode.get(vkCode,"").lower()
86
 
                vkChar=ctypes.windll.user32.MapVirtualKeyW(vkCode,winUser.MAPVK_VK_TO_CHAR)
87
 
                if vkName.startswith('oem') or not vkName:
88
 
                        if 32<vkCode<128:
89
 
                                vkName=unichr(vkCode).lower()
90
 
                        elif 32<vkChar<128:
91
 
                                vkName=unichr(vkChar).lower()
92
 
                if vkCode in (winUser.VK_SHIFT,winUser.VK_LSHIFT,winUser.VK_RSHIFT):
93
 
                        if speech.isPaused:
94
 
                                unpauseByShiftUp=True
95
 
                        else:
96
 
                                queueHandler.queueFunction(queueHandler.eventQueue,speech.pauseSpeech,True)
 
73
 
 
74
                keyCode = (vkCode, extended)
 
75
 
 
76
                if passKeyThroughCount >= 0:
 
77
                        # We're passing keys through.
 
78
                        if lastPassThroughKeyDown != keyCode:
 
79
                                # Increment the pass key through count.
 
80
                                # We only do this if this isn't a repeat of the previous key down, as we don't receive key ups for repeated key downs.
 
81
                                passKeyThroughCount += 1
 
82
                                lastPassThroughKeyDown = keyCode
 
83
                        return True
 
84
 
 
85
                keyCounter += 1
 
86
                gesture = KeyboardInputGesture(currentModifiers, vkCode, scanCode, extended)
 
87
                if bypassNVDAModifier or (keyCode == lastNVDAModifier and lastNVDAModifierReleaseTime and time.time() - lastNVDAModifierReleaseTime < 0.5):
 
88
                        # The user wants the key to serve its normal function instead of acting as an NVDA modifier key.
 
89
                        # There may be key repeats, so ensure we do this until they stop.
 
90
                        bypassNVDAModifier = True
 
91
                        gesture.isNVDAModifierKey = False
 
92
                lastNVDAModifierReleaseTime = None
 
93
                if gesture.isNVDAModifierKey:
 
94
                        lastNVDAModifier = keyCode
97
95
                else:
98
 
                        unpauseByShiftUp=False
99
 
                        globalVars.keyCounter+=1
100
 
                        queueHandler.queueFunction(queueHandler.eventQueue,speech.cancelSpeech)
101
 
                if lastNVDAModifierKey and (vkCode,extended)==lastNVDAModifierKey:
102
 
                        lastNVDAModifierKey=None
103
 
                        if (time.time()-lastNVDAModifierKeyTime)<0.5:
104
 
                                speakToggleKey(vkCode)
 
96
                        # Another key was pressed after the last NVDA modifier key, so it should not be passed through on the next press.
 
97
                        lastNVDAModifier = None
 
98
                if gesture.isModifier:
 
99
                        if gesture.speechEffectWhenExecuted in (gesture.SPEECHEFFECT_PAUSE, gesture.SPEECHEFFECT_RESUME) and keyCode in currentModifiers:
 
100
                                # Ignore key repeats for the pause speech key to avoid speech stuttering as it continually pauses and resumes.
105
101
                                return True
106
 
                lastNVDAModifierKey=None
107
 
                if isNVDAModifierKey(vkCode,extended):
108
 
                        NVDAModifierKey=(vkCode,extended)
109
 
                        if not globalVars.keyboardHelp:
 
102
                        currentModifiers.add(keyCode)
 
103
 
 
104
                try:
 
105
                        inputCore.manager.executeGesture(gesture)
 
106
                        trappedKeys.add(keyCode)
 
107
                        return False
 
108
                except inputCore.NoInputGestureAction:
 
109
                        if gesture.isNVDAModifierKey:
 
110
                                # Never pass the NVDA modifier key to the OS.
 
111
                                trappedKeys.add(keyCode)
110
112
                                return False
111
 
                if not globalVars.keyboardHelp and vkCode in [winUser.VK_CONTROL,winUser.VK_LCONTROL,winUser.VK_RCONTROL,winUser.VK_SHIFT,winUser.VK_LSHIFT,winUser.VK_RSHIFT,winUser.VK_MENU,winUser.VK_LMENU,winUser.VK_RMENU,winUser.VK_LWIN,winUser.VK_RWIN]:
112
 
                        return True
113
 
                modifierList=[]
114
 
                if NVDAModifierKey:
115
 
                        modifierList.append("nvda")
116
 
                if not vkCode in [winUser.VK_CONTROL,winUser.VK_LCONTROL,winUser.VK_RCONTROL] and winUser.getKeyState(winUser.VK_CONTROL)&32768:
117
 
                        modifierList.append("control")
118
 
                if not vkCode in [winUser.VK_SHIFT,winUser.VK_LSHIFT,winUser.VK_RSHIFT] and winUser.getKeyState(winUser.VK_SHIFT)&32768:
119
 
                        modifierList.append("shift")
120
 
                if not vkCode in [winUser.VK_MENU,winUser.VK_LMENU,winUser.VK_RMENU] and winUser.getKeyState(winUser.VK_MENU)&32768:
121
 
                        modifierList.append("alt")
122
 
                if not vkCode in [winUser.VK_LWIN,winUser.VK_RWIN] and winUser.getKeyState(winUser.VK_LWIN)&32768:
123
 
                        modifierList.append("win")
124
 
                if not vkCode in [winUser.VK_LWIN,winUser.VK_RWIN] and winUser.getKeyState(winUser.VK_RWIN)&32768:
125
 
                        modifierList.append("win")
126
 
                if len(modifierList) > 0:
127
 
                        modifiers=frozenset(modifierList)
128
 
                else:
129
 
                        modifiers=None
130
 
                mainKey=vkName
131
 
                if not mainKey:
132
 
                        mainKey=winUser.getKeyNameText(scanCode,extended)
133
 
                if extended==1:
134
 
                        mainKey="extended%s"%mainKey
135
 
                keyPress=(modifiers,mainKey)
136
 
                if log.isEnabledFor(log.IO): log.io("key press: %s"%keyName(keyPress))
137
 
                speakCommandKeys=config.conf["keyboard"]["speakCommandKeys"]
138
 
                if globalVars.keyboardHelp or speakCommandKeys:
139
 
                        labelList = []
140
 
                        if modifiers:
141
 
                                for mod in modifiers:
142
 
                                        if localizedKeyLabels.has_key(mod):
143
 
                                                labelList.append(localizedKeyLabels[mod])
144
 
                                        else:
145
 
                                                labelList.append(mod)
146
 
                        if not isNVDAModifierKey(vkCode,extended):
147
 
                                ch=ctypes.windll.user32.MapVirtualKeyW(vkCode,winUser.MAPVK_VK_TO_CHAR)
148
 
                                if localizedKeyLabels.has_key(keyPress[1]):
149
 
                                        labelList.append(localizedKeyLabels[keyPress[1]])
150
 
                                elif ch>=32 and not mainKey.startswith('numpad') and not mainKey in ('extendeddivide', 'multiply', 'subtract', 'add', 'extendedreturn', 'decimal'):
151
 
                                        labelList.append(unichr(ch))
152
 
                                else:
153
 
                                        labelList.append(keyPress[1])
154
 
                        if not speakCommandKeys or (speakCommandKeys and (
155
 
                                # An alphanumeric key has a label of only 1 character.
156
 
                                # Therefore, a command key either has a label longer than 1 character (except space)...
157
 
                                (labelList[-1]!="space" and len(labelList[-1])>1)
158
 
                                # or it has modifiers other than shift; e.g. control+f is a command key, but shift+f is not.
159
 
                                or (modifiers and modifiers!=frozenset(("shift",)))
160
 
                        )):
161
 
                                queueHandler.queueFunction(queueHandler.eventQueue,speech.speakMessage,"+".join(labelList))
162
 
                if not globalVars.keyboardHelp and (mainKey in ('extendeddivide', 'multiply', 'subtract', 'add', 'extendedreturn')) and (bool(winUser.getKeyState(winUser.VK_NUMLOCK)&1)):
163
 
                        return True
164
 
                script=scriptHandler.findScript(keyPress)
165
 
                if script:
166
 
                        scriptName=scriptHandler.getScriptName(script)
167
 
                        if globalVars.keyboardHelp and scriptName!="keyboardHelp":
168
 
                                brailleTextList=[]
169
 
                                brailleTextList.append("+".join(labelList))
170
 
                                scriptDescription = scriptHandler.getScriptDescription(script)
171
 
                                if scriptDescription:
172
 
                                        brailleTextList.append(scriptDescription)
173
 
                                        queueHandler.queueFunction(queueHandler.eventQueue,speech.speakMessage,_("Description: %s")%scriptDescription)
174
 
                                scriptLocation=scriptHandler.getScriptLocation(script)
175
 
                                brailleTextList.append(scriptLocation)
176
 
                                queueHandler.queueFunction(queueHandler.eventQueue,speech.speakMessage,_("Location: %s")%scriptLocation)
177
 
                                import braille
178
 
                                braille.handler.message("\t\t".join(brailleTextList))
179
 
                        else:
180
 
                                scriptHandler.queueScript(script,keyPress)
181
 
                if script or globalVars.keyboardHelp:
182
 
                        keyUpIgnoreSet.add((vkCode,extended))
183
 
                        if NVDAModifierKey:
184
 
                                usedNVDAModifierKey=True
185
 
                        return False
186
 
                else:
187
 
                        speakToggleKey(vkCode)
188
 
                        return True
189
113
        except:
190
114
                log.error("internal_keyDownEvent", exc_info=True)
191
 
                speech.speakMessage(_("Error in keyboardHandler.internal_keyDownEvent"))
192
 
                return True
 
115
        return True
193
116
 
194
117
def internal_keyUpEvent(vkCode,scanCode,extended,injected):
195
 
        """Event that pyHook calls when it receives keyUps"""
 
118
        """Event called by winInputHook when it receives a keyUp.
 
119
        """
196
120
        try:
197
 
                global NVDAModifierKey, usedNVDAModifierKey, lastNVDAModifierKey, lastNVDAModifierKeyTime, passKeyThroughCount, unpauseByShiftUp
 
121
                global lastNVDAModifier, lastNVDAModifierReleaseTime, bypassNVDAModifier, passKeyThroughCount, lastPassThroughKeyDown, currentModifiers
198
122
                if injected:
199
123
                        return True
200
 
                if passKeyThroughCount>=1:
201
 
                        passKeyThroughCount-=1
202
 
                        if passKeyThroughCount==0:
203
 
                                passKeyThroughCount=-1
204
 
                        return True
205
 
                if watchdog.isAttemptingRecovery:
206
 
                        # The core is dead, so let keys pass through unhindered.
207
 
                        return True
208
 
                focusObject=api.getFocusObject()
209
 
                focusAppModule=focusObject.appModule
210
 
                if focusAppModule and focusAppModule.selfVoicing:
211
 
                        return True
212
 
                if unpauseByShiftUp and vkCode in (winUser.VK_SHIFT,winUser.VK_LSHIFT,winUser.VK_RSHIFT):
213
 
                        queueHandler.queueFunction(queueHandler.eventQueue,speech.pauseSpeech,False)
214
 
                        unpauseByShiftUp=False
215
 
                if NVDAModifierKey and (vkCode,extended)==NVDAModifierKey:
216
 
                        if not usedNVDAModifierKey:
217
 
                                lastNVDAModifierKey=NVDAModifierKey
218
 
                                lastNVDAModifierKeyTime=time.time()
219
 
                        NVDAModifierKey=None
220
 
                        usedNVDAModifierKey=False
221
 
                        return False
222
 
                elif (vkCode,extended) in keyUpIgnoreSet:
223
 
                        keyUpIgnoreSet.remove((vkCode,extended))
224
 
                        return False
225
 
                elif vkCode in [winUser.VK_CONTROL,winUser.VK_LCONTROL,winUser.VK_RCONTROL,winUser.VK_SHIFT,winUser.VK_LSHIFT,winUser.VK_RSHIFT,winUser.VK_MENU,winUser.VK_LMENU,winUser.VK_RMENU,winUser.VK_LWIN,winUser.VK_RWIN]:
226
 
                        return True
 
124
 
 
125
                keyCode = (vkCode, extended)
 
126
 
 
127
                if passKeyThroughCount >= 1:
 
128
                        if lastPassThroughKeyDown == keyCode:
 
129
                                # This key has been released.
 
130
                                lastPassThroughKeyDown = None
 
131
                        passKeyThroughCount -= 1
 
132
                        if passKeyThroughCount == 0:
 
133
                                passKeyThroughCount = -1
 
134
                        return True
 
135
 
 
136
                if lastNVDAModifier and keyCode == lastNVDAModifier:
 
137
                        # The last pressed NVDA modifier key is being released and there were no key presses in between.
 
138
                        # The user may want to press it again quickly to pass it through.
 
139
                        lastNVDAModifierReleaseTime = time.time()
 
140
                # If we were bypassing the NVDA modifier, stop doing so now, as there will be no more repeats.
 
141
                bypassNVDAModifier = False
 
142
 
 
143
                currentModifiers.discard(keyCode)
 
144
 
 
145
                if keyCode in trappedKeys:
 
146
                        trappedKeys.remove(keyCode)
 
147
                        return False
227
148
        except:
228
 
                log.error("", exc=True)
229
 
                speech.speakMessage(_("Error in keyboardHandler.internal_keyUpEvent"))
 
149
                log.error("", exc_info=True)
230
150
        return True
231
151
 
232
152
#Register internal key press event with  operating system
235
155
        """Initialises keyboard support."""
236
156
        winInputHook.initialize()
237
157
        winInputHook.setCallbacks(keyDown=internal_keyDownEvent,keyUp=internal_keyUpEvent)
238
 
        nvdajp_keyEvents.initialize()   # Masataka.Shinke
239
158
 
240
159
def terminate():
241
 
        nvdajp_keyEvents.terminate()    # Masataka.Shinke
242
160
        winInputHook.terminate()
 
161
 
 
162
class KeyboardInputGesture(inputCore.InputGesture):
 
163
        """A key pressed on the traditional system keyboard.
 
164
        """
 
165
 
 
166
        #: All normal modifier keys, where modifier vk codes are mapped to a more general modifier vk code or C{None} if not applicable.
 
167
        #: @type: dict
 
168
        NORMAL_MODIFIER_KEYS = {
 
169
                winUser.VK_LCONTROL: winUser.VK_CONTROL,
 
170
                winUser.VK_RCONTROL: winUser.VK_CONTROL,
 
171
                winUser.VK_LSHIFT: winUser.VK_SHIFT,
 
172
                winUser.VK_RSHIFT: winUser.VK_SHIFT,
 
173
                winUser.VK_LMENU: winUser.VK_MENU,
 
174
                winUser.VK_RMENU: winUser.VK_MENU,
 
175
                winUser.VK_LWIN: VK_WIN,
 
176
                winUser.VK_RWIN: VK_WIN,
 
177
        }
 
178
 
 
179
        #: All possible toggle key vk codes.
 
180
        #: @type: frozenset
 
181
        TOGGLE_KEYS = frozenset((winUser.VK_CAPITAL, winUser.VK_NUMLOCK, winUser.VK_SCROLL))
 
182
 
 
183
        #: All possible keyboard layouts, where layout names are mapped to localised layout names.
 
184
        #: @type: dict
 
185
        LAYOUTS = {
 
186
                "desktop": _("desktop"),
 
187
                "laptop": _("laptop"),
 
188
        }
 
189
 
 
190
        @classmethod
 
191
        def getVkName(cls, vkCode, isExtended):
 
192
                if isinstance(vkCode, str):
 
193
                        return vkCode
 
194
                name = vkCodes.byCode.get((vkCode, isExtended))
 
195
                if not name and isExtended is not None:
 
196
                        # Whether the key is extended doesn't matter for many keys, so try None.
 
197
                        name = vkCodes.byCode.get((vkCode, None))
 
198
                return name if name else ""
 
199
 
 
200
        def __init__(self, modifiers, vkCode, scanCode, isExtended):
 
201
                #: The keyboard layout in which this gesture was created.
 
202
                #: @type: str
 
203
                self.layout = config.conf["keyboard"]["keyboardLayout"]
 
204
                self.modifiers = modifiers = set(modifiers)
 
205
                # Don't double up if this is a modifier key repeat.
 
206
                modifiers.discard((vkCode, isExtended))
 
207
                if vkCode in (winUser.VK_DIVIDE, winUser.VK_MULTIPLY, winUser.VK_SUBTRACT, winUser.VK_ADD) and winUser.getKeyState(winUser.VK_NUMLOCK) & 1:
 
208
                        # Some numpad keys have the same vkCode regardless of numlock.
 
209
                        # For these keys, treat numlock as a modifier.
 
210
                        modifiers.add((winUser.VK_NUMLOCK, False))
 
211
                self.generalizedModifiers = set((self.NORMAL_MODIFIER_KEYS.get(mod) or mod, extended) for mod, extended in modifiers)
 
212
                self.vkCode = vkCode
 
213
                self.scanCode = scanCode
 
214
                self.isExtended = isExtended
 
215
                super(KeyboardInputGesture, self).__init__()
 
216
 
 
217
        def _get_isNVDAModifierKey(self):
 
218
                return isNVDAModifierKey(self.vkCode, self.isExtended)
 
219
 
 
220
        def _get_isModifier(self):
 
221
                return self.vkCode in self.NORMAL_MODIFIER_KEYS or self.isNVDAModifierKey
 
222
 
 
223
        def _get_mainKeyName(self):
 
224
                if self.isNVDAModifierKey:
 
225
                        return "NVDA"
 
226
 
 
227
                name = self.getVkName(self.vkCode, self.isExtended)
 
228
                if name:
 
229
                        return name
 
230
 
 
231
                if 32 < self.vkCode < 128:
 
232
                        return unichr(self.vkCode).lower()
 
233
                vkChar = winUser.user32.MapVirtualKeyW(self.vkCode, winUser.MAPVK_VK_TO_CHAR)
 
234
                if vkChar>0:
 
235
                        return unichr(vkChar).lower()
 
236
 
 
237
                return winUser.getKeyNameText(self.scanCode, self.isExtended)
 
238
 
 
239
        def _get_modifierNames(self):
 
240
                modTexts = set()
 
241
                for modVk, modExt in self.generalizedModifiers:
 
242
                        if isNVDAModifierKey(modVk, modExt):
 
243
                                modTexts.add("NVDA")
 
244
                        else:
 
245
                                modTexts.add(self.getVkName(modVk, None))
 
246
 
 
247
                return modTexts
 
248
 
 
249
        def _get__keyNamesInDisplayOrder(self):
 
250
                return tuple(self.modifierNames) + (self.mainKeyName,)
 
251
 
 
252
        def _get_logIdentifier(self):
 
253
                return u"kb({layout}):{key}".format(layout=self.layout,
 
254
                        key="+".join(self._keyNamesInDisplayOrder))
 
255
 
 
256
        def _get_displayName(self):
 
257
                return "+".join(localizedKeyLabels.get(key, key) for key in self._keyNamesInDisplayOrder)
 
258
 
 
259
        def _get_identifiers(self):
 
260
                keyNames = set(self.modifierNames)
 
261
                keyNames.add(self.mainKeyName)
 
262
                keyName = "+".join(keyNames).lower()
 
263
                return (
 
264
                        u"kb({layout}):{key}".format(layout=self.layout, key=keyName),
 
265
                        u"kb:{key}".format(key=keyName)
 
266
                )
 
267
 
 
268
        def _get_shouldReportAsCommand(self):
 
269
                if self.isExtended and winUser.VK_VOLUME_MUTE <= self.vkCode <= winUser.VK_VOLUME_UP:
 
270
                        # Don't report volume controlling keys.
 
271
                        return False
 
272
                # Aside from space, a key name of more than 1 character is a command.
 
273
                if self.vkCode != winUser.VK_SPACE and len(self.mainKeyName) > 1:
 
274
                        return True
 
275
                # If this key has modifiers other than shift, it is a command; e.g. shift+f is text, but control+f is a command.
 
276
                modifiers = self.generalizedModifiers
 
277
                if modifiers and (len(modifiers) > 1 or tuple(modifiers)[0][0] != winUser.VK_SHIFT):
 
278
                        return True
 
279
                return False
 
280
 
 
281
        def _get_speechEffectWhenExecuted(self):
 
282
                if inputCore.manager.isInputHelpActive:
 
283
                        return self.SPEECHEFFECT_CANCEL
 
284
                if self.isExtended and winUser.VK_VOLUME_MUTE <= self.vkCode <= winUser.VK_VOLUME_UP:
 
285
                        return None
 
286
                if self.vkCode in (winUser.VK_SHIFT, winUser.VK_LSHIFT, winUser.VK_RSHIFT):
 
287
                        return self.SPEECHEFFECT_RESUME if speech.isPaused else self.SPEECHEFFECT_PAUSE
 
288
                return self.SPEECHEFFECT_CANCEL
 
289
 
 
290
        def reportExtra(self):
 
291
                if self.vkCode in self.TOGGLE_KEYS:
 
292
                        wx.CallLater(30, self._reportToggleKey)
 
293
 
 
294
        def _reportToggleKey(self):
 
295
                toggleState = winUser.getKeyState(self.vkCode) & 1
 
296
                key = self.mainKeyName
 
297
                ui.message(u"{key} {state}".format(
 
298
                        key=localizedKeyLabels.get(key, key),
 
299
                        state=_("on") if toggleState else _("off")))
 
300
 
 
301
        def send(self):
 
302
                keys = []
 
303
                for vk, ext in self.generalizedModifiers:
 
304
                        if vk == VK_WIN:
 
305
                                if winUser.getKeyState(winUser.VK_LWIN) & 32768 or winUser.getKeyState(winUser.VK_RWIN) & 32768:
 
306
                                        # Already down.
 
307
                                        continue
 
308
                                vk = winUser.VK_LWIN
 
309
                        elif winUser.getKeyState(vk) & 32768:
 
310
                                # Already down.
 
311
                                continue
 
312
                        keys.append((vk, 0, ext))
 
313
                keys.append((self.vkCode, self.scanCode, self.isExtended))
 
314
 
 
315
                if winUser.getKeyState(self.vkCode) & 32768:
 
316
                        # This key is already down, so send a key up for it first.
 
317
                        winUser.keybd_event(self.vkCode, self.scanCode, self.isExtended + 2, 0)
 
318
 
 
319
                # Send key down events for these keys.
 
320
                for vk, scan, ext in keys:
 
321
                        winUser.keybd_event(vk, scan, ext, 0)
 
322
                # Send key up events for the keys in reverse order.
 
323
                for vk, scan, ext in reversed(keys):
 
324
                        winUser.keybd_event(vk, scan, ext + 2, 0)
 
325
 
 
326
                if not queueHandler.isPendingItems(queueHandler.eventQueue):
 
327
                        time.sleep(0.01)
 
328
                        wx.Yield()
 
329
 
 
330
        @classmethod
 
331
        def fromName(cls, name):
 
332
                """Create an instance given a key name.
 
333
                @param name: The key name.
 
334
                @type name: str
 
335
                @return: A gesture for the specified key.
 
336
                @rtype: L{KeyboardInputGesture}
 
337
                """
 
338
                keyNames = name.split("+")
 
339
                keys = []
 
340
                for keyName in keyNames:
 
341
                        if keyName == VK_WIN:
 
342
                                vk = winUser.VK_LWIN
 
343
                                ext = False
 
344
                        elif len(keyName) == 1:
 
345
                                ext = False
 
346
                                requiredMods, vk = winUser.VkKeyScan(keyName)
 
347
                                if requiredMods & 1:
 
348
                                        keys.append((winUser.VK_SHIFT, False))
 
349
                                if requiredMods & 2:
 
350
                                        keys.append((winUser.VK_CONTROL, False))
 
351
                                if requiredMods & 4:
 
352
                                        keys.append((winUser.VK_MENU, False))
 
353
                                # Not sure whether we need to support the Hankaku modifier (& 8).
 
354
                        else:
 
355
                                vk, ext = vkCodes.byName[keyName.lower()]
 
356
                                if ext is None:
 
357
                                        ext = False
 
358
                        keys.append((vk, ext))
 
359
 
 
360
                if not keys:
 
361
                        raise ValueError
 
362
 
 
363
                return cls(keys[:-1], vk, 0, ext)