#keyboardHandler.py #A part of NonVisual Desktop Access (NVDA) #Copyright (C) 2006-2007 NVDA Contributors #This file is covered by the GNU General Public License. #See the file COPYING for more details. """Keyboard support""" import locale import winUser import ctypes import time import vkCodes import speech from keyUtils import key, keyName, sendKey, localizedKeyLabels import scriptHandler import globalVars from logHandler import log import queueHandler import config import _winreg import api import keyHook keyUpIgnoreSet=set() passKeyThroughCount=-1 #If 0 or higher then key downs and key ups will be passed straight through NVDAModifierKey=None usedNVDAModifierKey=False lastNVDAModifierKey=None lastNVDAModifierKeyTime=None unpauseByShiftUp=False def passNextKeyThrough(): global passKeyThroughCount if passKeyThroughCount==-1: passKeyThroughCount=0 def isNVDAModifierKey(vkCode,extended): if config.conf["keyboard"]["useNumpadInsertAsNVDAModifierKey"] and vkCode==winUser.VK_INSERT and not extended: return True elif config.conf["keyboard"]["useExtendedInsertAsNVDAModifierKey"] and vkCode==winUser.VK_INSERT and extended: return True elif config.conf["keyboard"]["useCapsLockAsNVDAModifierKey"] and vkCode==winUser.VK_CAPITAL: return True else: return False def speakToggleKey(vkCode): toggleState=bool(not winUser.getKeyState(vkCode)&1) if vkCode==winUser.VK_CAPITAL: queueHandler.queueFunction(queueHandler.eventQueue,speech.speakMessage,_("caps lock %s")%(_("on") if toggleState else _("off"))) elif vkCode==winUser.VK_NUMLOCK: queueHandler.queueFunction(queueHandler.eventQueue,speech.speakMessage,_("num lock %s")%(_("on") if toggleState else _("off"))) elif vkCode==winUser.VK_SCROLL: queueHandler.queueFunction(queueHandler.eventQueue,speech.speakMessage,_("scroll lock %s")%(_("on") if toggleState else _("off"))) def speakKeyboardLayout(layout): try: s = hex(winUser.LOWORD(layout))[2:].rjust(8, "0") key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\"+ s) except: s = hex(winUser.HIWORD(layout))[2:].rjust(8, "0") key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\"+ s) try: s = _winreg.QueryValueEx(key, "Layout Display Name")[0] except: s=None if s is not None and isinstance(s,basestring): buf=ctypes.create_unicode_buffer(256) ctypes.windll.shlwapi.SHLoadIndirectString(s,buf,256,None) s=buf.value else: s = _winreg.QueryValueEx(key, "Layout Text")[0] key.Close() queueHandler.queueFunction(queueHandler.eventQueue,speech.speakMessage,_("%s keyboard layout")%s) def internal_keyDownEvent(vkCode,scanCode,extended,injected): """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. """ try: global NVDAModifierKey, usedNVDAModifierKey, lastNVDAModifierKey, lastNVDAModifierKeyTime, passKeyThroughCount, unpauseByShiftUp #Injected keys should be ignored if injected: return True if passKeyThroughCount>=0: passKeyThroughCount+=1 return True vkName=vkCodes.byCode.get(vkCode,"").lower() vkChar=ctypes.windll.user32.MapVirtualKeyW(vkCode,winUser.MAPVK_VK_TO_CHAR) if vkName.startswith('oem') or not vkName: if 32 0: modifiers=frozenset(modifierList) else: modifiers=None mainKey=vkName if not mainKey: mainKey=winUser.getKeyNameText(scanCode,extended) if extended==1: mainKey="extended%s"%mainKey keyPress=(modifiers,mainKey) if log.isEnabledFor(log.IO): log.io("key press: %s"%keyName(keyPress)) if globalVars.keyboardHelp or (config.conf["keyboard"]["speakCommandKeys"] and not ( not keyPress[0] and config.conf["keyboard"]["speakTypedCharacters"])): labelList = [] if modifiers: for mod in modifiers: if localizedKeyLabels.has_key(mod): labelList.append(localizedKeyLabels[mod]) else: labelList.append(mod) if not isNVDAModifierKey(vkCode,extended): ch=ctypes.windll.user32.MapVirtualKeyW(vkCode,winUser.MAPVK_VK_TO_CHAR) if localizedKeyLabels.has_key(keyPress[1]): labelList.append(localizedKeyLabels[keyPress[1]]) elif ch>=32 and not mainKey.startswith('numpad') and not mainKey in ('extendeddivide', 'multiply', 'subtract', 'add', 'extendedreturn', 'decimal'): labelList.append(unichr(ch)) else: labelList.append(keyPress[1]) queueHandler.queueFunction(queueHandler.eventQueue,speech.speakMessage,"+".join(labelList)) if not globalVars.keyboardHelp and (mainKey in ('extendeddivide', 'multiply', 'subtract', 'add', 'extendedreturn')) and (bool(winUser.getKeyState(winUser.VK_NUMLOCK)&1)): return True script=scriptHandler.findScript(keyPress) if script: scriptName=scriptHandler.getScriptName(script) if globalVars.keyboardHelp and scriptName!="keyboardHelp": brailleTextList=[] brailleTextList.append("+".join(labelList)) scriptDescription = scriptHandler.getScriptDescription(script) if scriptDescription: brailleTextList.append(scriptDescription) queueHandler.queueFunction(queueHandler.eventQueue,speech.speakMessage,_("Description: %s")%scriptDescription) scriptLocation=scriptHandler.getScriptLocation(script) brailleTextList.append(scriptLocation) queueHandler.queueFunction(queueHandler.eventQueue,speech.speakMessage,_("Location: %s")%scriptLocation) import braille braille.handler.message("\t\t".join(brailleTextList)) else: scriptHandler.queueScript(script,keyPress) if script or globalVars.keyboardHelp: keyUpIgnoreSet.add((vkCode,extended)) if NVDAModifierKey: usedNVDAModifierKey=True return False else: speakToggleKey(vkCode) return True except: log.error("internal_keyDownEvent", exc_info=True) speech.speakMessage(_("Error in keyboardHandler.internal_keyDownEvent")) return True def internal_keyUpEvent(vkCode,scanCode,extended,injected): """Event that pyHook calls when it receives keyUps""" try: global NVDAModifierKey, usedNVDAModifierKey, lastNVDAModifierKey, lastNVDAModifierKeyTime, passKeyThroughCount, unpauseByShiftUp lastPressedKeyTime=time.time() if injected: return True elif passKeyThroughCount>=1: passKeyThroughCount-=1 if passKeyThroughCount==0: passKeyThroughCount=-1 return True if unpauseByShiftUp and vkCode in (winUser.VK_SHIFT,winUser.VK_LSHIFT,winUser.VK_RSHIFT): queueHandler.queueFunction(queueHandler.eventQueue,speech.pauseSpeech,False) unpauseByShiftUp=False if NVDAModifierKey and (vkCode,extended)==NVDAModifierKey: if not usedNVDAModifierKey: lastNVDAModifierKey=NVDAModifierKey lastNVDAModifierKeyTime=time.time() NVDAModifierKey=None usedNVDAModifierKey=False return False elif (vkCode,extended) in keyUpIgnoreSet: keyUpIgnoreSet.remove((vkCode,extended)) return False 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]: return True except: log.error("", exc=True) speech.speakMessage(_("Error in keyboardHandler.internal_keyUpEvent")) return True #Register internal key press event with operating system def initialize(): """Initialises keyboard support.""" keyHook.initialize(internal_keyDownEvent,internal_keyUpEvent) def terminate(): keyHook.terminate()