Package virtual_keyboard :: Module virtual_keyboard
[hide private]
[frames] | no frames]

Source Code for Module virtual_keyboard.virtual_keyboard

  1  #!/usr/bin/env python 
  2   
  3  import subprocess 
  4  from subprocess import PIPE 
  5  import re 
  6   
  7  # Requires xdotools (available via apt in Ubuntu) 
  8   
9 -class VirtualKeyboard(object):
10
11 - def __init__(self):
12 super(VirtualKeyboard,self).__init__(); 13 # Regex that extracts two named groups from a str 14 # like this: "x:1893 y:720 screen:0 window:12595888". 15 # The first group will be called 'x', and will contain 16 # the number following x (here: 1893). The second group 17 # will be called 'y'. See getMouseGlobalPos() for use: 18 self.screenPosPattern = re.compile(r'x:(?P<x>[\d]+).*y:(?P<y>[\d]+)'); 19 # Same for extracting the window ID only: 20 self.windowIDPattern = re.compile(r'.*window:(?P<winID>[\d]+)'); 21 22 # Pattern to extract x,y,width,height from the xdotool getwindowgeometry command: 23 # Window 46137569 24 # Position: 2765,628 (screen: 0) 25 # Geometry: 1244x848 26 # The '.DOTALL' ensures that '.*' matches the newlines: 27 self.winGeometryPattern = re.compile('.*Position: (?P<x>[\d]+)[,]+(?P<y>[\d]+).*Geometry: (?P<width>[\d]+)x(?P<height>[\d]+)', re.DOTALL); 28 29 # Place for users to have window ids stored: 30 self.windowIDs = {}; 31 32 self.saveActiveWindowID('__startup_win_id__');
33
34 - def typeControlCharToActiveWindow(self, theStr):
35 ''' 36 Type a given keystroke. Examples being "alt+r", "Control_L+J", 37 "ctrl+alt+n", "BackSpace", "Linefeed". 38 Generally, any valid X Keysym string will work. Composited control keys are 39 separated by '+'. Aliases exist for "alt", "ctrl", "shift", "super", 40 and "meta" which all map to Foo_L, such as Alt_L and Control_L, etc. 41 @param theStr: control-char string to send to active window 42 @type theStr: string 43 ''' 44 resCode = subprocess.call(['xdotool', 'key', str(theStr)]);
45
46 - def typeTextToActiveWindow(self, theStr):
47 ''' 48 Type a given string, which must not contain control chars. 49 For sending individual control chars, use typeControlCharToActiveWindow(). 50 @param theStr: control-char string to send to active window 51 @type theStr: string 52 ''' 53 resCode = subprocess.call(['xdotool', 'type', str(theStr)]);
54
55 - def mouseClick(self, buttonNum):
56 ''' 57 Buttons map this way: Left mouse is 1, middle is 2, right is 3, 58 wheel up is 4, wheel down is 5. 59 @param buttonNum: which mouse button to click 60 @type buttonNum: int 61 ''' 62 if buttonNum < 1 or buttonNum > 5: 63 raise ValueError("Mouse buttons are 1-3; mouse wheel up is 4; mouse wheel down is 5. Called with %d" % buttonNum) 64 resCode = subprocess.call(['xdotool', 'getactivewindow', 'click', str(buttonNum)]);
65
66 - def mouseDown(self, buttonNum):
67 ''' 68 Buttons map this way: Left mouse is 1, middle is 2, right is 3, 69 wheel up is 4, wheel down is 5. 70 @param buttonNum: which mouse button to hold down 71 @type buttonNum: int 72 ''' 73 if buttonNum < 1 or buttonNum > 5: 74 raise ValueError("Mouse buttons are 1-3; mouse wheel up is 4; mouse wheel down is 5. Called with %d" % buttonNum) 75 resCode = subprocess.call(['xdotool', 'getactivewindow', 'mousedown', str(buttonNum)]);
76
77 - def mouseUp(self, buttonNum):
78 ''' 79 Buttons map this way: Left mouse is 1, middle is 2, right is 3, 80 wheel up is 4, wheel down is 5. 81 @param buttonNum: which mouse button to hold down 82 @type buttonNum: int 83 ''' 84 if buttonNum < 1 or buttonNum > 5: 85 raise ValueError("Mouse buttons are 1-3; mouse wheel up is 4; mouse wheel down is 5. Called with %d" % buttonNum) 86 proc = subprocess.Popen(['xdotool', 'getactivewindow', 'mouseup', str(buttonNum)]);
87
88 - def getMouseGlobalPos(self):
89 ''' 90 Return a dictionary that provides 'x' and 'y' keys, 91 whose values are integer x and y coordinates of the 92 mouse cursor. The position is relative to the upper left 93 corner of the display. 94 ''' 95 proc = subprocess.Popen(['xdotool', 'getmouselocation'], stdout=PIPE, stderr=file("/dev/null", 'w')); 96 # Example return in stdoutData: "x:1893 y:720 screen:0 window:12595888" 97 (stdoutData, stdinData) = proc.communicate(); 98 # Turn output into a Python dictionary: 99 xdotoolOutputMatch = re.match(self.screenPosPattern, stdoutData); 100 if xdotoolOutputMatch is None: 101 raise ValueError("Call to xdotool for obtaining mouse cursor position did not return output of expected format. It returned '%s'" % stdoutData) 102 # Get at dict from the groups: {x:int1, y:int2}, where int1 and int2 are of string type: 103 resDict = xdotoolOutputMatch.groupdict(); 104 # Turn x and y coords into ints: 105 resDict['x'] = int(resDict['x']); 106 resDict['y'] = int(resDict['y']); 107 return resDict
108
109 - def moveMouseAbsolute(self, x, y):
110 ''' 111 Move mouse cursor to absolute position relative to upper left 112 corner of display. 113 @param x: horizontal coordinate 114 @type x: int 115 @param y: vertical coordinate 116 @type y: int 117 ''' 118 resCode = subprocess.call(['xdotool', 'mousemove', '--sync', str(x), str(y)]);
119
120 - def moveMouseRelative(self, x, y):
121 ''' 122 Move mouse cursor new position relative to where 123 it is currently located. It is legal to use negative 124 offsets for x and/or y. 125 @param x: horizontal coordinate 126 @type x: int 127 @param y: vertical coordinate 128 @type y: int 129 ''' 130 resCode = subprocess.call(['xdotool', 'mousemove_relative', '--sync', '--', str(x), str(y)]);
131
132 - def saveActiveWindowID(self,retrievalKey):
133 ''' 134 Internally saves the currently active X11 window's ID. 135 Use getRecentWindow() to retrieve the ID for use with 136 later xdotool commands. 137 @param retrievalKey: key under which caller will ask for the ID later on. 138 @type retrievalKey: string 139 ''' 140 #*********8 141 winid = subprocess.check_output(['xdotool', 'getactivewindow']).strip(); 142 #*********8 143 self.windowIDs[retrievalKey] = subprocess.check_output(['xdotool', 'getactivewindow']).strip();
144
145 - def windowsEqual(self, retrievalKey1, retrievalKey2):
146 return self._getWinIDSafely_(retrievalKey1) == self._getWinIDSafely_(retrievalKey2);
147
148 - def activateWindow(self, retrievalKey=None, windowTitle=None):
149 ''' 150 Activates the X11 window with the given window ID. 151 If windowID is omitted, the most recently active 152 window is activated (see getRecentWindow()). 153 @param retrievalKey: key under which caller asked to associate with window in earlier call to saveActiveWindowID 154 @type retrievalKey: string 155 ''' 156 if ((retrievalKey is None) and (windowTitle is None)) or\ 157 ((retrievalKey is not None) and (windowTitle is not None)): 158 raise ValueError('Either retrievalKey or windowTitle must be provided (but not both)'); 159 160 if retrievalKey is not None: 161 #*********** 162 winKey = str(self._getWinIDSafely_(retrievalKey)); 163 #*********** 164 resCode = subprocess.call(['xdotool', 'windowactivate', '--sync', str(self._getWinIDSafely_(retrievalKey))]); 165 else: 166 resCode = subprocess.call(['xdotool', 'search', '--name', '--sync', windowTitle, 'windowactivate']);
167
168 - def getWindowGeometry(self, retrievalKey=None):
169 if retrievalKey is None: 170 geo = subprocess.check_output(['xdotool', 'getactivewindow', 'getwindowgeometry']); 171 else: 172 geo = subprocess.check_output(['xdotool', 'getwindowgeometry', str(self._getWinIDSafely_(retrievalKey))]); 173 theMatch = self.winGeometryPattern.match(geo); 174 if theMatch is None: 175 return None; 176 resDict = theMatch.groupdict(); 177 return resDict;
178
179 - def raiseWindow(self, retrievalKey=None):
180 ''' 181 Flakey; don't rely on it. 182 @param retrievalKey: 183 @type retrievalKey: 184 ''' 185 if retrievalKey is None: 186 rescode = subprocess.call(['xdotool', 'getactivewindow', 'windowraise']); 187 else: 188 rescode = subprocess.call(['xdotool', 'windowraise', str(self._getWinIDSafely_(retrievalKey))]);
189 190 # def activateWindowUnderMouse(self): 191 # winID = self.windowUnderMouseCursor(); 192 # self.activateWindow(windowID=winID); 193 194 # def windowUnderMouseCursor(self): 195 # ''' 196 # Returns the ID of the X11 window under the mouse cursor. 197 # ''' 198 # stdoutData = subprocess.check_output(['xdotool', 'getmouselocation'], stderr=file("/dev/null", 'w')); 199 # xdotoolOutputMatch = re.match(self.windowIDPattern, stdoutData); 200 # if xdotoolOutputMatch is None: 201 # raise ValueError("Call to xdotool for obtaining mouse cursor position did not return output of expected format. It returned '%s'" % stdoutData) 202 # # Get at dict from the group: {winID:winID}, where winID is of string type: 203 # resDict = xdotoolOutputMatch.groupdict(); 204 # return resDict['winID']; 205 206 207 #---------------------------------------- Private Methods ---------------------------- 208
209 - def _getWinIDSafely_(self, retrievalKey):
210 try: 211 return self.windowIDs[retrievalKey]; 212 except KeyError as e: 213 raise ValueError("No retrieval key %s is known to virtual keyboard." % `e`);
214 215 216 if __name__ == '__main__': 217 218 import time 219 vBoard = VirtualKeyboard() 220 # Time for human user to switch to another window for this test: 221 # time.sleep(4) 222 # vBoard.typeToActiveWindow("This is my test.") 223 224 # time.sleep(4) 225 # vBoard.mouseClick(1) 226 227 # locDict = vBoard.getMouseGlobalPos(); 228 # print("X: %d; Y: %d" % (locDict['x'], locDict['y'])) 229 230 # vBoard.moveMouseAbsolute(100, 100); 231 232 # vBoard.moveMouseRelative(-100, 100); 233 234 # currentWinID = vBoard.windowUnderMouseCursor(); 235 # print("Move cursor to a different window; it will be activated."); 236 # while True: 237 # winIDNow = vBoard.windowUnderMouseCursor() 238 # if winIDNow != currentWinID: 239 # break; 240 # time.sleep(1); 241 # vBoard.activateWindow(winIDNow); 242 # print("Done.") 243 244 # geometryDict = vBoard.getWindowGeometry(); 245 # print(str(geometryDict)); 246 # 247 # vBoard.saveActiveWindowID('testWindow'); 248 # geometryDict = vBoard.getWindowGeometry(retrievalKey='testWindow'); 249 # print(str(geometryDict)); 250 251 vBoard.windowIDs['test'] = str(46137569); 252 vBoard.raiseWindow(retrievalKey='test') 253 254 255 # The following test doesn't work, because 256 # 'xdotool selectwindow' doesn't select the clicked-on window 257 # in the X11 sense. It only indicates to xdotools which window 258 # will be talked about in the remainder of a command chain: 259 # print("Select a window to save."); 260 # winID = subprocess.check_output(['xdotool', 'selectwindow']) 261 # print("winid was: %s" % winID); 262 # vBoard.saveActiveWindowID(); 263 # print("Click another window and then wait 3 seconds for original window to regain focus."); 264 # newWinID = subprocess.check_output(['xdotool', 'selectwindow']) 265 # time.sleep(3); 266 # vBoard.activateWindow(); 267