OSDN Git Service

Merge 2013.1.
[nvdajp/nvdajp.git] / source / touchHandler.py
1 #touchHandler.py\r
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
6 \r
7 import threading\r
8 from ctypes import *\r
9 from ctypes.wintypes import *\r
10 import sys\r
11 import globalPluginHandler\r
12 import config\r
13 import winUser\r
14 import speech\r
15 import api\r
16 import ui\r
17 import queueHandler\r
18 import inputCore\r
19 import screenExplorer\r
20 from logHandler import log\r
21 import touchTracker\r
22 import gui\r
23 \r
24 availableTouchModes=['text','object']\r
25 \r
26 HWND_MESSAGE=-3\r
27 \r
28 WM_QUIT=18\r
29 \r
30 PT_TOUCH=0x02\r
31 \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
38 WM_POINTERUP=0x0247\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
47 \r
48 POINTER_FLAG_CANCELED=0x400\r
49 POINTER_FLAG_UP=0x40000\r
50 \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
58 \r
59 class POINTER_INFO(Structure):\r
60         _fields_=[\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
71                 ('dwTime',DWORD),\r
72                 ('historyCount',c_uint32),\r
73                 ('inputData',c_int),\r
74                 ('dwKeyStates',DWORD),\r
75                 ('PerformanceCount',c_uint64),\r
76         ]\r
77 \r
78 class POINTER_TOUCH_INFO(Structure):\r
79         _fields_=[\r
80                 ('pointerInfo',POINTER_INFO),\r
81                 ('touchFlags',c_uint32),\r
82                 ('touchMask',c_uint32),\r
83                 ('rcContact',RECT),\r
84                 ('rcContactRaw',RECT),\r
85                 ('orientation',c_uint32),\r
86                 ('pressure',c_uint32),\r
87         ]\r
88 \r
89 ANRUS_TOUCH_MODIFICATION_ACTIVE=2\r
90 \r
91 touchWindow=None\r
92 touchThread=None\r
93 \r
94 class TouchInputGesture(inputCore.InputGesture):\r
95 \r
96         counterNames=["single","double","tripple","quodruple"]\r
97 \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
101 \r
102         def __init__(self,tracker,mode):\r
103                 super(TouchInputGesture,self).__init__()\r
104                 self.tracker=tracker\r
105                 self.mode=mode\r
106 \r
107         def _get__rawIdentifiers(self):\r
108                 ID=""\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
116                 IDs=[]\r
117                 IDs.append("TS(%s):%s"%(self.mode,ID))\r
118                 IDs.append("ts:%s"%ID)\r
119                 return IDs\r
120 \r
121         def _get_logIdentifier(self):\r
122                 return self._rawIdentifiers[0]\r
123 \r
124         def _get_identifiers(self):\r
125                 return [x.lower() for x in self._rawIdentifiers] \r
126 \r
127         def _get_displayName(self):\r
128                 return " ".join(self._rawIdentifiers[1][3:].split('_'))\r
129 \r
130 class TouchHandler(threading.Thread):\r
131 \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
137                 self.start()\r
138                 self.initializedEvent.wait()\r
139                 if self.threadExc:\r
140                         raise self.threadExc\r
141 \r
142         def terminate(self):\r
143                 windll.user32.PostThreadMessageW(self.ident,WM_QUIT,0,0)\r
144                 self.join()\r
145 \r
146         def run(self):\r
147                 try:\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
161                         self.threadExc=e\r
162                 finally:\r
163                         self.initializedEvent.set()\r
164                 msg=MSG()\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
173 \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
181                         if touching:\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
185                         return 0\r
186                 return windll.user32.DefWindowProcW(hwnd,msg,wParam,lParam)\r
187 \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
192 \r
193         def gesturePumpFunc(self):\r
194                 while True:\r
195                         for tracker in self.trackerManager.emitTrackers():\r
196                                 gesture=TouchInputGesture(tracker,self._curTouchMode)\r
197                                 try:\r
198                                         inputCore.manager.executeGesture(gesture)\r
199                                 except inputCore.NoInputGestureAction:\r
200                                         pass\r
201                         yield\r
202 \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
208                 """\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
212 \r
213 handler=None\r
214 \r
215 def initialize():\r
216         global handler\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
225         if maxTouches<=0:\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
230 \r
231 def terminate():\r
232         global handler\r
233         if handler:\r
234                 handler.terminate()\r
235                 handler=None\r