mouseHandler.py 4.59 KB
#mouseHandler.py
#A part of NonVisual Desktop Access (NVDA)
#Copyright (C) 2006-2007 NVDA Contributors <http://www.nvda-project.org/>
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.

import time
import wx
import tones
import ctypes
import winUser
import queueHandler
import api
import screenBitmap
import speech
import globalVars
import eventHandler
from logHandler import log
import config
import winInputHook
import core
import ui

WM_MOUSEMOVE=0x0200
WM_LBUTTONDOWN=0x0201
WM_LBUTTONUP=0x0202
WM_LBUTTONDBLCLK=0x0203
WM_RBUTTONDOWN=0x0204
WM_RBUTTONUP=0x0205
WM_RBUTTONDBLCLK=0x0206

curMousePos=(0,0)
mouseMoved=False
curMouseShape=""
_shapeTimer=None
scrBmpObj=None
#: The time (in seconds) at which the last mouse event occurred.
#: @type: float
lastMouseEventTime=0

SHAPE_REPORT_DELAY = 100
def updateMouseShape(name):
	global curMouseShape
	if not name or name==curMouseShape:
		return
	curMouseShape=name
	if config.conf["mouse"]["reportMouseShapeChanges"]:
		# Delay reporting to avoid unnecessary/excessive verbosity.
		_shapeTimer.Stop()
		_shapeTimer.Start(SHAPE_REPORT_DELAY, True)

def playAudioCoordinates(x, y, screenWidth, screenHeight, detectBrightness=True,blurFactor=0):
	minPitch=config.conf['mouse']['audioCoordinates_minPitch']
	maxPitch=config.conf['mouse']['audioCoordinates_maxPitch']
	curPitch=minPitch+((maxPitch-minPitch)*((screenHeight-y)/float(screenHeight)))
	if detectBrightness:
		startX=min(max(x-blurFactor,0),screenWidth)
		width=min((x+blurFactor+1)-startX,screenWidth)
		startY=min(max(y-blurFactor,0),screenHeight)
		height=min((y+blurFactor+1)-startY,screenHeight)
		grey=screenBitmap.rgbPixelBrightness(scrBmpObj.captureImage(startX,startY,width,height)[0][0])
		brightness=grey/255.0
		minBrightness=config.conf['mouse']['audioCoordinates_minVolume']
		maxBrightness=config.conf['mouse']['audioCoordinates_maxVolume']
		brightness=(brightness*(maxBrightness-minBrightness))+minBrightness
	else:
		brightness=config.conf['mouse']['audioCoordinates_maxVolume']
	leftVolume=int((85*((screenWidth-float(x))/screenWidth))*brightness)
	rightVolume=int((85*(float(x)/screenWidth))*brightness)
	tones.beep(curPitch,40,left=leftVolume,right=rightVolume)

#Internal mouse event

def internal_mouseEvent(msg,x,y,injected):
	global mouseMoved, curMousePos, lastMouseEventTime
	lastMouseEventTime=time.time()
	if injected:
		return True
	if not config.conf['mouse']['enableMouseTracking']:
		return True
	try:
		curMousePos=(x,y)
		if msg==WM_MOUSEMOVE: 
			mouseMoved=True
			core.requestPump()
		elif msg in (WM_LBUTTONDOWN,WM_RBUTTONDOWN):
			queueHandler.queueFunction(queueHandler.eventQueue,speech.cancelSpeech)
	except:
		log.error("", exc_info=True)
	return True

def executeMouseMoveEvent(x,y):
	global currentMouseWindow
	desktopObject=api.getDesktopObject()
	screenLeft,screenTop,screenWidth,screenHeight=desktopObject.location
	x=min(max(screenLeft,x),(screenLeft+screenWidth)-1)
	y=min(max(screenTop,y),(screenTop+screenHeight)-1)
	if config.conf["mouse"]["audioCoordinatesOnMouseMove"]:
		playAudioCoordinates(x,y,screenWidth,screenHeight,config.conf['mouse']['audioCoordinates_detectBrightness'],config.conf['mouse']['audioCoordinates_blurFactor'])
	oldMouseObject=api.getMouseObject()
	mouseObject=desktopObject.objectFromPoint(x,y)
	while mouseObject and mouseObject.beTransparentToMouse:
		mouseObject=mouseObject.parent
	if not mouseObject:
		return
	if oldMouseObject==mouseObject:
		mouseObject=oldMouseObject
	else:
		api.setMouseObject(mouseObject)
	try:
		eventHandler.executeEvent("mouseMove",mouseObject,x=x,y=y)
		oldMouseObject=mouseObject
	except:
		log.error("api.notifyMouseMoved", exc_info=True)

#Register internal mouse event

def initialize():
	global curMousePos, scrBmpObj, _shapeTimer
	scrBmpObj=screenBitmap.ScreenBitmap(1,1)
	(x,y)=winUser.getCursorPos()
	desktopObject=api.getDesktopObject()
	try:
		mouseObject=desktopObject.objectFromPoint(x,y)
	except:
		log.exception("Error retrieving initial mouse object")
		mouseObject=None
	if not mouseObject:
		mouseObject=api.getDesktopObject()
	api.setMouseObject(mouseObject)
	curMousePos=(x,y)
	winInputHook.initialize()
	winInputHook.setCallbacks(mouse=internal_mouseEvent)
	_shapeTimer = wx.PyTimer(_reportShape)

def _reportShape():
	# Translators: Reported when mouse cursor shape changes (example output: edit cursor).
	ui.message(_("%s cursor")%curMouseShape)

def pumpAll():
	global mouseMoved, curMousePos
	if mouseMoved:
		mouseMoved=False
		(x,y)=curMousePos
		executeMouseMoveEvent(x,y)

def terminate():
	global scrBmpObj, _shapeTimer
	scrBmpObj=None
	winInputHook.terminate()
	_shapeTimer.Stop()
	_shapeTimer = None