2 #A part of NonVisual Desktop Access (NVDA)
\r
3 #This file is covered by the GNU General Public License.
\r
4 #See the file COPYING for more details.
\r
5 #Copyright (C) 2012 NV Access Limited
\r
9 from ctypes.wintypes import *
\r
11 import globalPluginHandler
\r
19 import screenExplorer
\r
20 from logHandler import log
\r
24 availableTouchModes=['text','object']
\r
32 _WM_POINTER_FIRST=WM_NCPOINTERUPDATE=0x0241
\r
33 WM_NCPOINTERDOWN=0x0242
\r
34 WM_NCPOINTERUP=0x0243
\r
35 WM_NCPOINTERCANCEL=0x0244
\r
36 WM_POINTERUPDATE=0x0245
\r
37 WM_POINTERDOWN=0x0246
\r
39 WM_POINTERCANCEL=0x0248
\r
40 WM_POINTERENTER=0x0249
\r
41 WM_POINTERLEAVE=0x024A
\r
42 WM_POINTERACTIVATE=0x024B
\r
43 WM_POINTERCAPTURECHANGED=0x024C
\r
44 WM_TOUCHHITTESTING=0x024D
\r
45 WM_POINTERWHEEL=0x024E
\r
46 _WM_POINTER_LAST=WM_POINTERHWHEEL=0x024F
\r
48 POINTER_FLAG_CANCELED=0x400
\r
49 POINTER_FLAG_UP=0x40000
\r
51 POINTER_MESSAGE_FLAG_NEW=0x1
\r
52 POINTER_MESSAGE_FLAG_INRANGE=0x2
\r
53 POINTER_MESSAGE_FLAG_INCONTACT=0x4
\r
54 POINTER_MESSAGE_FLAG_FIRSTBUTTON=0x10
\r
55 POINTER_MESSAGE_FLAG_PRIMARY=0x100
\r
56 POINTER_MESSAGE_FLAG_CONFIDENCE=0x200
\r
57 POINTER_MESSAGE_FLAG_CANCELED=0x400
\r
59 class POINTER_INFO(Structure):
\r
61 ('pointerType',DWORD),
\r
62 ('pointerId',c_uint32),
\r
63 ('frameId',c_uint32),
\r
64 ('pointerFlags',c_uint32),
\r
65 ('sourceDevice',HANDLE),
\r
66 ('hwndTarget',HWND),
\r
67 ('ptPixelLocation',POINT),
\r
68 ('ptHimetricLocation',POINT),
\r
69 ('ptPixelLocationRaw',POINT),
\r
70 ('ptHimetricLocationRaw',POINT),
\r
72 ('historyCount',c_uint32),
\r
73 ('inputData',c_int),
\r
74 ('dwKeyStates',DWORD),
\r
75 ('PerformanceCount',c_uint64),
\r
78 class POINTER_TOUCH_INFO(Structure):
\r
80 ('pointerInfo',POINTER_INFO),
\r
81 ('touchFlags',c_uint32),
\r
82 ('touchMask',c_uint32),
\r
84 ('rcContactRaw',RECT),
\r
85 ('orientation',c_uint32),
\r
86 ('pressure',c_uint32),
\r
89 ANRUS_TOUCH_MODIFICATION_ACTIVE=2
\r
94 class TouchInputGesture(inputCore.InputGesture):
\r
96 counterNames=["single","double","tripple","quodruple"]
\r
98 def _get_speechEffectWhenExecuted(self):
\r
99 if self.tracker.action in (touchTracker.action_hover,touchTracker.action_hoverUp): return None
\r
100 return super(TouchInputGesture,self).speechEffectWhenExecuted
\r
102 def __init__(self,tracker,mode):
\r
103 super(TouchInputGesture,self).__init__()
\r
104 self.tracker=tracker
\r
107 def _get__rawIdentifiers(self):
\r
109 if self.tracker.numHeldFingers>0:
\r
110 ID+="%dfinger_hold+"%self.tracker.numHeldFingers
\r
111 if self.tracker.numFingers>1:
\r
112 ID+="%dfinger_"%self.tracker.numFingers
\r
113 if self.tracker.actionCount>1:
\r
114 ID+="%s_"%self.counterNames[min(self.tracker.actionCount,4)-1]
\r
115 ID+=self.tracker.action
\r
117 IDs.append("TS(%s):%s"%(self.mode,ID))
\r
118 IDs.append("ts:%s"%ID)
\r
121 def _get_logIdentifier(self):
\r
122 return self._rawIdentifiers[0]
\r
124 def _get_identifiers(self):
\r
125 return [x.lower() for x in self._rawIdentifiers]
\r
127 def _get_displayName(self):
\r
128 return " ".join(self._rawIdentifiers[1][3:].split('_'))
\r
130 class TouchHandler(threading.Thread):
\r
132 def __init__(self):
\r
133 super(TouchHandler,self).__init__()
\r
134 self._curTouchMode='object'
\r
135 self.initializedEvent=threading.Event()
\r
136 self.threadExc=None
\r
138 self.initializedEvent.wait()
\r
140 raise self.threadExc
\r
142 def terminate(self):
\r
143 windll.user32.PostThreadMessageW(self.ident,WM_QUIT,0,0)
\r
148 self._appInstance=windll.kernel32.GetModuleHandleW(None)
\r
149 self._cInputTouchWindowProc=winUser.WNDPROC(self.inputTouchWndProc)
\r
150 self._wc=winUser.WNDCLASSEXW(cbSize=sizeof(winUser.WNDCLASSEXW),lpfnWndProc=self._cInputTouchWindowProc,hInstance=self._appInstance,lpszClassName="inputTouchWindowClass")
\r
151 self._wca=windll.user32.RegisterClassExW(byref(self._wc))
\r
152 self._touchWindow=windll.user32.CreateWindowExW(0,self._wca,u"NVDA touch input",0,0,0,0,0,HWND_MESSAGE,None,self._appInstance,None)
\r
153 windll.user32.RegisterPointerInputTarget(self._touchWindow,PT_TOUCH)
\r
154 oledll.oleacc.AccSetRunningUtilityState(self._touchWindow,ANRUS_TOUCH_MODIFICATION_ACTIVE,ANRUS_TOUCH_MODIFICATION_ACTIVE)
\r
155 self.trackerManager=touchTracker.TrackerManager()
\r
156 self.screenExplorer=screenExplorer.ScreenExplorer()
\r
157 self.screenExplorer.updateReview=True
\r
158 self.gesturePump=self.gesturePumpFunc()
\r
159 queueHandler.registerGeneratorObject(self.gesturePump)
\r
160 except Exception as e:
\r
163 self.initializedEvent.set()
\r
165 while windll.user32.GetMessageW(byref(msg),None,0,0):
\r
166 windll.user32.TranslateMessage(byref(msg))
\r
167 windll.user32.DispatchMessageW(byref(msg))
\r
168 self.gesturePump.close()
\r
169 oledll.oleacc.AccSetRunningUtilityState(self._touchWindow,ANRUS_TOUCH_MODIFICATION_ACTIVE,0)
\r
170 windll.user32.UnregisterPointerInputTarget(self._touchWindow,PT_TOUCH)
\r
171 windll.user32.DestroyWindow(self._touchWindow)
\r
172 windll.user32.UnregisterClassW(self._wca,self._appInstance)
\r
174 def inputTouchWndProc(self,hwnd,msg,wParam,lParam):
\r
175 if msg>=_WM_POINTER_FIRST and msg<=_WM_POINTER_LAST:
\r
176 flags=winUser.HIWORD(wParam)
\r
177 touching=(flags&POINTER_MESSAGE_FLAG_INRANGE) and (flags&POINTER_MESSAGE_FLAG_FIRSTBUTTON)
\r
178 x=winUser.LOWORD(lParam)
\r
179 y=winUser.HIWORD(lParam)
\r
180 ID=winUser.LOWORD(wParam)
\r
182 self.trackerManager.update(ID,x,y,False)
\r
183 elif not flags&POINTER_MESSAGE_FLAG_FIRSTBUTTON:
\r
184 self.trackerManager.update(ID,x,y,True)
\r
186 return windll.user32.DefWindowProcW(hwnd,msg,wParam,lParam)
\r
188 def setMode(self,mode):
\r
189 if mode not in availableTouchModes:
\r
190 raise ValueError("Unknown mode %s"%mode)
\r
191 self._curTouchMode=mode
\r
193 def gesturePumpFunc(self):
\r
195 for tracker in self.trackerManager.emitTrackers():
\r
196 gesture=TouchInputGesture(tracker,self._curTouchMode)
\r
198 inputCore.manager.executeGesture(gesture)
\r
199 except inputCore.NoInputGestureAction:
\r
203 def notifyInteraction(self, obj):
\r
204 """Notify the system that UI interaction is occurring via touch.
\r
205 This should be called when performing an action on an object.
\r
206 @param obj: The NVDAObject with which the user is interacting.
\r
207 @type obj: L{NVDAObjects.NVDAObject}
\r
209 l, t, w, h = obj.location
\r
210 oledll.oleacc.AccNotifyTouchInteraction(gui.mainFrame.Handle, obj.windowHandle,
\r
211 POINT(l + (w / 2), t + (h / 2)))
\r
217 if not config.isInstalledCopy():
\r
218 log.debugWarning("Touch only supported on installed copies")
\r
219 raise NotImplementedError
\r
220 version=sys.getwindowsversion()
\r
221 if (version.major*10+version.minor)<62:
\r
222 log.debugWarning("Touch only supported on Windows 8 and higher")
\r
223 raise NotImplementedError
\r
224 maxTouches=windll.user32.GetSystemMetrics(95) #maximum touches
\r
226 log.debugWarning("No touch devices found")
\r
227 raise NotImplementedError
\r
228 handler=TouchHandler()
\r
229 log.debug("Touch support initialized. maximum touch inputs: %d"%maxTouches)
\r
234 handler.terminate()
\r