Module winConsoleHandler
[hide private]
[frames] | no frames]

Source Code for Module winConsoleHandler

  1  #winConsoleHandler.py 
  2  #A part of NonVisual Desktop Access (NVDA) 
  3  #This file is covered by the GNU General Public License. 
  4  #See the file COPYING for more details. 
  5  #Copyright (C) 2009-2010 Michael Curran <mick@kulgan.net>, James Teh <jamie@jantrid.net> 
  6   
  7  import wx 
  8  import winUser 
  9  import winKernel 
 10  import wincon 
 11  from colors import RGB 
 12  import eventHandler 
 13  from logHandler import log 
 14  import speech 
 15  import textInfos 
 16  import api 
 17  import config 
 18   
 19  #: How often to check whether the console is dead (in ms). 
 20  CHECK_DEAD_INTERVAL = 100 
 21   
 22  consoleObject=None #:The console window that is currently in the foreground. 
 23  consoleWinEventHookHandles=[] #:a list of currently registered console win events. 
 24  consoleOutputHandle=None 
 25  checkDeadTimer=None 
 26   
 27  CONSOLE_COLORS_TO_RGB=( #http://en.wikipedia.org/wiki/Color_Graphics_Adapter 
 28          RGB(0x00,0x00,0x00), #black 
 29          RGB(0x00,0x00,0xAA), #blue 
 30          RGB(0x00,0xAA,0x00), #green 
 31          RGB(0x00,0xAA,0xAA), #cyan 
 32          RGB(0xAA,0x00,0x00), #red 
 33          RGB(0xAA,0x00,0xAA), #magenta 
 34          RGB(0xAA,0x55, 0x00), #brown 
 35          RGB(0xAA,0xAA,0xAA), #white 
 36          RGB(0x55,0x55,0x55), #gray 
 37          RGB(0x55,0x55,0xFF), #light blue 
 38          RGB(0x55,0xFF,0x55), #light green 
 39          RGB(0x55,0xFF,0xFF), #light cyan 
 40          RGB(0xFF,0x55,0x55), #light red 
 41          RGB(0xFF,0x55,0xFF), #light magenta 
 42          RGB(0xFF,0xFF,0x55), #yellow 
 43          RGB(0xFF,0xFF,0xFF), #white (high intensity) 
 44  ) 
 45   
 46  COMMON_LVB_UNDERSCORE=0x8000 
47 48 49 50 @wincon.PHANDLER_ROUTINE 51 -def _consoleCtrlHandler(event):
52 if event in (wincon.CTRL_C_EVENT,wincon.CTRL_BREAK_EVENT): 53 return True 54 return False
55
56 -def connectConsole(obj):
57 global consoleObject, consoleOutputHandle, checkDeadTimer 58 #Get the process ID of the console this NVDAObject is fore 59 processID,threadID=winUser.getWindowThreadProcessID(obj.windowHandle) 60 #Attach NVDA to this console so we can access its text etc 61 try: 62 wincon.AttachConsole(processID) 63 except WindowsError as e: 64 log.debugWarning("Could not attach console: %s"%e) 65 return False 66 wincon.SetConsoleCtrlHandler(_consoleCtrlHandler,True) 67 consoleOutputHandle=winKernel.CreateFile(u"CONOUT$",winKernel.GENERIC_READ|winKernel.GENERIC_WRITE,winKernel.FILE_SHARE_READ|winKernel.FILE_SHARE_WRITE,None,winKernel.OPEN_EXISTING,0,None) 68 #Register this callback with all the win events we need, storing the given handles for removal later 69 for eventID in (winUser.EVENT_CONSOLE_CARET,winUser.EVENT_CONSOLE_UPDATE_REGION,winUser.EVENT_CONSOLE_UPDATE_SIMPLE,winUser.EVENT_CONSOLE_UPDATE_SCROLL,winUser.EVENT_CONSOLE_LAYOUT): 70 handle=winUser.setWinEventHook(eventID,eventID,0,consoleWinEventHook,0,0,0) 71 if not handle: 72 raise OSError("could not register eventID %s"%eventID) 73 consoleWinEventHookHandles.append(handle) 74 consoleObject=obj 75 checkDeadTimer=wx.PyTimer(_checkDead) 76 checkDeadTimer.Start(CHECK_DEAD_INTERVAL) 77 return True
78
79 -def disconnectConsole():
80 global consoleObject, consoleOutputHandle, consoleWinEventHookHandles, checkDeadTimer 81 if not consoleObject: 82 log.debugWarning("console was not connected") 83 return False 84 checkDeadTimer.Stop() 85 checkDeadTimer=None 86 #Unregister any win events we are using 87 for handle in consoleWinEventHookHandles: 88 winUser.unhookWinEvent(handle) 89 consoleEventHookHandles=[] 90 consoleObject.stopMonitoring() 91 winKernel.closeHandle(consoleOutputHandle) 92 consoleOutputHandle=None 93 consoleObject=None 94 try: 95 wincon.SetConsoleCtrlHandler(_consoleCtrlHandler,False) 96 except WindowsError: 97 pass 98 #Try freeing NVDA from this console 99 try: 100 wincon.FreeConsole() 101 except WindowsError: 102 pass 103 return True
104
105 -def isConsoleDead():
106 # Every console should have at least one process associated with it. 107 # This console should have two if NVDA is also connected. 108 # If there is only one, it must be NVDA alone, so it is dead. 109 processList=wincon.GetConsoleProcessList(2) 110 return len(processList) < 2
111
112 -def _checkDead():
113 try: 114 if isConsoleDead(): 115 # We must disconnect NVDA from this console so it can close. 116 disconnectConsole() 117 except: 118 log.exception()
119
120 -def getConsoleVisibleLines():
121 consoleScreenBufferInfo=wincon.GetConsoleScreenBufferInfo(consoleOutputHandle) 122 topLine=consoleScreenBufferInfo.srWindow.Top 123 lineCount=(consoleScreenBufferInfo.srWindow.Bottom-topLine)+1 124 lineLength=consoleScreenBufferInfo.dwSize.x 125 text=wincon.ReadConsoleOutputCharacter(consoleOutputHandle,lineCount*lineLength,0,topLine) 126 newLines=[text[x:x+lineLength] for x in xrange(0,len(text),lineLength)] 127 return newLines
128
129 @winUser.WINEVENTPROC 130 -def consoleWinEventHook(handle,eventID,window,objectID,childID,threadID,timestamp):
131 #We don't want to do anything with the event if the event is not for the window this console is in 132 if window!=consoleObject.windowHandle: 133 return 134 if eventID==winUser.EVENT_CONSOLE_CARET and not eventHandler.isPendingEvents("caret",consoleObject): 135 eventHandler.queueEvent("caret",consoleObject) 136 # It is safe to call this event from this callback. 137 # This avoids an extra core cycle. 138 consoleObject.event_textChange() 139 if eventID==winUser.EVENT_CONSOLE_UPDATE_SIMPLE: 140 x=winUser.LOWORD(objectID) 141 y=winUser.HIWORD(objectID) 142 consoleScreenBufferInfo=wincon.GetConsoleScreenBufferInfo(consoleOutputHandle) 143 if x<consoleScreenBufferInfo.dwCursorPosition.x and (y==consoleScreenBufferInfo.dwCursorPosition.y or y==consoleScreenBufferInfo.dwCursorPosition.y+1): 144 eventHandler.queueEvent("typedCharacter",consoleObject,ch=unichr(winUser.LOWORD(childID)))
145
146 -def initialize():
147 pass
148
149 -def terminate():
150 if consoleObject: 151 disconnectConsole()
152
153 -class WinConsoleTextInfo(textInfos.offsets.OffsetsTextInfo):
154 155 _cache_consoleScreenBufferInfo=True 158
159 - def _offsetFromConsoleCoord(self,x,y):
160 consoleScreenBufferInfo=self.consoleScreenBufferInfo 161 val=y-consoleScreenBufferInfo.srWindow.Top 162 val*=consoleScreenBufferInfo.dwSize.x 163 val+=x 164 return val
165
166 - def _consoleCoordFromOffset(self,offset):
167 consoleScreenBufferInfo=self.consoleScreenBufferInfo 168 x=offset%consoleScreenBufferInfo.dwSize.x 169 y=offset-x 170 y/=consoleScreenBufferInfo.dwSize.x 171 y+=consoleScreenBufferInfo.srWindow.Top 172 return x,y
173
174 - def _getOffsetFromPoint(self,x,y):
175 consoleScreenBufferInfo=self.consoleScreenBufferInfo 176 screenLeft,screenTop,screenWidth,screenHeight=self.obj.location 177 relativeX=x-screenLeft 178 relativeY=y-screenTop 179 lineLength=(consoleScreenBufferInfo.srWindow.Right+1)-consoleScreenBufferInfo.srWindow.Left 180 numLines=(consoleScreenBufferInfo.srWindow.Bottom+1)-consoleScreenBufferInfo.srWindow.Top 181 characterWidth=screenWidth/lineLength 182 characterHeight=screenHeight/numLines 183 characterX=(relativeX/characterWidth)+consoleScreenBufferInfo.srWindow.Left 184 characterY=(relativeY/characterHeight)+consoleScreenBufferInfo.srWindow.Top 185 return self._offsetFromConsoleCoord(characterX,characterY)
186
187 - def _getPointFromOffset(self,offset):
188 consoleScreenBufferInfo=self.consoleScreenBufferInfo 189 characterX,characterY=self._consoleCoordFromOffset(offset) 190 screenLeft,screenTop,screenWidth,screenHeight=self.obj.location 191 lineLength=(consoleScreenBufferInfo.srWindow.Right+1)-consoleScreenBufferInfo.srWindow.Left 192 numLines=(consoleScreenBufferInfo.srWindow.Bottom+1)-consoleScreenBufferInfo.srWindow.Top 193 characterWidth=screenWidth/lineLength 194 characterHeight=screenHeight/numLines 195 relativeX=(characterX-consoleScreenBufferInfo.srWindow.Left)*characterWidth 196 relativeY=(characterY-consoleScreenBufferInfo.srWindow.Top)*characterHeight 197 x=relativeX+screenLeft 198 y=relativeY+screenTop 199 return textInfos.Point(x,y)
200
201 - def _getCaretOffset(self):
202 consoleScreenBufferInfo=self.consoleScreenBufferInfo 203 return self._offsetFromConsoleCoord(consoleScreenBufferInfo.dwCursorPosition.x,consoleScreenBufferInfo.dwCursorPosition.y)
204
205 - def _getSelectionOffsets(self):
206 selInfo=wincon.GetConsoleSelectionInfo() 207 if selInfo.dwFlags&wincon.CONSOLE_SELECTION_NOT_EMPTY: 208 start=self._offsetFromConsoleCoord(selInfo.srSelection.Left,selInfo.srSelection.Top) 209 end=self._offsetFromConsoleCoord(selInfo.srSelection.Right,selInfo.srSelection.Bottom) 210 else: 211 start=end=self._getCaretOffset() 212 return start,end
213
214 - def getTextWithFields(self,formatConfig=None):
215 commands=[] 216 if self.isCollapsed: 217 return commands 218 if not formatConfig: 219 formatConfig=config.conf["documentFormatting"] 220 left,top=self._consoleCoordFromOffset(self._startOffset) 221 right,bottom=self._consoleCoordFromOffset(self._endOffset-1) 222 rect=wincon.SMALL_RECT(left,top,right,bottom) 223 if bottom-top>0: #offsets span multiple lines 224 rect.Left=0 225 rect.Right=self.consoleScreenBufferInfo.dwSize.x-1 226 length=self.consoleScreenBufferInfo.dwSize.x*(bottom-top+1) 227 else: 228 length=self._endOffset-self._startOffset 229 buf=wincon.ReadConsoleOutput(consoleOutputHandle, length, rect) 230 if bottom-top>0: 231 buf=buf[left:len(buf)-(self.consoleScreenBufferInfo.dwSize.x-right)+1] 232 lastAttr=None 233 lastText=[] 234 boundEnd=self._startOffset 235 for i,c in enumerate(buf): 236 if self._startOffset+i==boundEnd: 237 field,(boundStart,boundEnd)=self._getFormatFieldAndOffsets(boundEnd,formatConfig) 238 if lastText: 239 commands.append("".join(lastText)) 240 lastText=[] 241 commands.append(textInfos.FieldCommand("formatChange",field)) 242 if not c.Attributes==lastAttr: 243 formatField=textInfos.FormatField() 244 if formatConfig['reportColor']: 245 formatField["color"]=CONSOLE_COLORS_TO_RGB[c.Attributes&0x0f] 246 formatField["background-color"]=CONSOLE_COLORS_TO_RGB[(c.Attributes>>4)&0x0f] 247 if formatConfig['reportFontAttributes'] and c.Attributes&COMMON_LVB_UNDERSCORE: 248 formatField['underline']=True 249 if formatField: 250 if lastText: 251 commands.append("".join(lastText)) 252 lastText=[] 253 command=textInfos.FieldCommand("formatChange", formatField) 254 commands.append(command) 255 lastAttr=c.Attributes 256 lastText.append(c.Char) 257 commands.append("".join(lastText)) 258 return commands
259
260 - def _getTextRange(self,start,end):
261 startX,startY=self._consoleCoordFromOffset(start) 262 return wincon.ReadConsoleOutputCharacter(consoleOutputHandle,end-start,startX,startY)
263
264 - def _getLineOffsets(self,offset):
265 consoleScreenBufferInfo=self.consoleScreenBufferInfo 266 x,y=self._consoleCoordFromOffset(offset) 267 x=0 268 start=self._offsetFromConsoleCoord(x,y) 269 end=start+consoleScreenBufferInfo.dwSize.x 270 return start,end
271
272 - def _getLineNumFromOffset(self,offset):
273 consoleScreenBufferInfo=self.consoleScreenBufferInfo 274 x,y=self._consoleCoordFromOffset(offset) 275 return y-consoleScreenBufferInfo.srWindow.Top
276
277 - def _getStoryLength(self):
278 consoleScreenBufferInfo=self.consoleScreenBufferInfo 279 return consoleScreenBufferInfo.dwSize.x*((consoleScreenBufferInfo.srWindow.Bottom+1)-consoleScreenBufferInfo.srWindow.Top)
280
281 - def _get_clipboardText(self):
282 return "\r\n".join(block.rstrip() for block in self.getTextInChunks(textInfos.UNIT_LINE))
283