~ubuntu-branches/ubuntu/saucy/autopilot/saucy-proposed

« back to all changes in this revision

Viewing changes to autopilot/input/_X11.py

  • Committer: Package Import Robot
  • Author(s): Ubuntu daily release, Łukasz 'sil2100' Zemczak, Ubuntu daily release
  • Date: 2013-07-12 00:02:34 UTC
  • mfrom: (52.5.1) (58.1.9 saucy-proposed)
  • Revision ID: package-import@ubuntu.com-20130712000234-gsrwbjke7oi0yfxt
Tags: 1.3.1+13.10.20130712-0ubuntu1
[ Łukasz 'sil2100' Zemczak ]
* I don't like it, but it's the easiest way - make autopilot-touch
  Arch: any and make the python-ubuntu-platform-api [armhf] dependent.

[ Ubuntu daily release ]
* Automatic snapshot from revision 265

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
from autopilot.input import (
35
35
    Keyboard as KeyboardBase,
36
36
    Mouse as MouseBase,
37
 
    )
 
37
)
38
38
from Xlib import X, XK
39
39
from Xlib.display import Display
40
40
from Xlib.ext.xtest import fake_input
47
47
 
48
48
 
49
49
def get_display():
50
 
    """Get the Xlib display object, creating it (silently) if it doesn't exist."""
 
50
    """Return the Xlib display object.
 
51
 
 
52
    It is created (silently) if it doesn't exist.
 
53
 
 
54
    """
51
55
    global _DISPLAY
52
56
    if _DISPLAY is None:
53
57
        with Silence():
64
68
    """Wrapper around xlib to make faking keyboard input possible."""
65
69
 
66
70
    _special_X_keysyms = {
67
 
        ' ' : "space",
68
 
        '\t' : "Tab",
69
 
        '\n' : "Return",  # for some reason this needs to be cr, not lf
70
 
        '\r' : "Return",
71
 
        '\e' : "Escape",
72
 
        '!' : "exclam",
73
 
        '#' : "numbersign",
74
 
        '%' : "percent",
75
 
        '$' : "dollar",
76
 
        '&' : "ampersand",
77
 
        '"' : "quotedbl",
78
 
        '\'' : "apostrophe",
79
 
        '(' : "parenleft",
80
 
        ')' : "parenright",
81
 
        '*' : "asterisk",
82
 
        '=' : "equal",
83
 
        '+' : "plus",
84
 
        ',' : "comma",
85
 
        '-' : "minus",
86
 
        '.' : "period",
87
 
        '/' : "slash",
88
 
        ':' : "colon",
89
 
        ';' : "semicolon",
90
 
        '<' : "less",
91
 
        '>' : "greater",
92
 
        '?' : "question",
93
 
        '@' : "at",
94
 
        '[' : "bracketleft",
95
 
        ']' : "bracketright",
96
 
        '\\' : "backslash",
97
 
        '^' : "asciicircum",
98
 
        '_' : "underscore",
99
 
        '`' : "grave",
100
 
        '{' : "braceleft",
101
 
        '|' : "bar",
102
 
        '}' : "braceright",
103
 
        '~' : "asciitilde"
104
 
        }
 
71
        ' ': "space",
 
72
        '\t': "Tab",
 
73
        '\n': "Return",  # for some reason this needs to be cr, not lf
 
74
        '\r': "Return",
 
75
        '\e': "Escape",
 
76
        '!': "exclam",
 
77
        '#': "numbersign",
 
78
        '%': "percent",
 
79
        '$': "dollar",
 
80
        '&': "ampersand",
 
81
        '"': "quotedbl",
 
82
        '\'': "apostrophe",
 
83
        '(': "parenleft",
 
84
        ')': "parenright",
 
85
        '*': "asterisk",
 
86
        '=': "equal",
 
87
        '+': "plus",
 
88
        ',': "comma",
 
89
        '-': "minus",
 
90
        '.': "period",
 
91
        '/': "slash",
 
92
        ':': "colon",
 
93
        ';': "semicolon",
 
94
        '<': "less",
 
95
        '>': "greater",
 
96
        '?': "question",
 
97
        '@': "at",
 
98
        '[': "bracketleft",
 
99
        ']': "bracketright",
 
100
        '\\': "backslash",
 
101
        '^': "asciicircum",
 
102
        '_': "underscore",
 
103
        '`': "grave",
 
104
        '{': "braceleft",
 
105
        '|': "bar",
 
106
        '}': "braceright",
 
107
        '~': "asciitilde"
 
108
    }
105
109
 
106
110
    _keysym_translations = {
107
 
        'Control' : 'Control_L',
108
 
        'Ctrl' : 'Control_L',
109
 
        'Alt' : 'Alt_L',
 
111
        'Control': 'Control_L',
 
112
        'Ctrl': 'Control_L',
 
113
        'Alt': 'Alt_L',
110
114
        'AltR': 'Alt_R',
111
 
        'Super' : 'Super_L',
112
 
        'Shift' : 'Shift_L',
113
 
        'Enter' : 'Return',
114
 
        'Space' : ' ',
 
115
        'Super': 'Super_L',
 
116
        'Shift': 'Shift_L',
 
117
        'Enter': 'Return',
 
118
        'Space': ' ',
115
119
    }
116
120
 
117
121
    def __init__(self):
189
193
            raise TypeError("'keys' argument must be a string.")
190
194
        logger.debug("Typing text %r", string)
191
195
        for key in string:
192
 
            # Don't call press or release here, as they translate keys to keysyms.
 
196
            # Don't call press or release here, as they translate keys to
 
197
            # keysyms.
193
198
            self.__perform_on_key(key, X.KeyPress)
194
199
            sleep(delay)
195
200
            self.__perform_on_key(key, X.KeyRelease)
199
204
    def on_test_end(cls, test_instance):
200
205
        """Generate KeyRelease events for any un-released keys.
201
206
 
202
 
        .. important:: Ensure you call this at the end of any test to release any
203
 
         keys that were pressed and not released.
 
207
        .. important:: Ensure you call this at the end of any test to release
 
208
         any keys that were pressed and not released.
204
209
 
205
210
        """
206
211
        global _PRESSED_KEYS
207
212
        for keycode in _PRESSED_KEYS:
208
 
            logger.warning("Releasing key %r as part of cleanup call.", keycode)
 
213
            logger.warning(
 
214
                "Releasing key %r as part of cleanup call.", keycode)
209
215
            fake_input(get_display(), X.KeyRelease, keycode)
210
216
        _PRESSED_KEYS = []
211
217
 
229
235
            if keycode in _PRESSED_KEYS:
230
236
                _PRESSED_KEYS.remove(keycode)
231
237
            else:
232
 
                logger.warning("Generating release event for keycode %d that was not pressed.", keycode)
 
238
                logger.warning(
 
239
                    "Generating release event for keycode %d that was not "
 
240
                    "pressed.", keycode)
233
241
 
234
242
        fake_input(get_display(), event, keycode)
235
243
        get_display().sync()
246
254
    def __is_shifted(self, key):
247
255
        return len(key) == 1 and ord(key) in self.shifted_keys and key != '<'
248
256
 
249
 
    def __char_to_keycode(self, key) :
 
257
    def __char_to_keycode(self, key):
250
258
        keysym = self.__get_keysym(key)
251
259
        keycode = get_display().keysym_to_keycode(keysym)
252
 
        if keycode == 0 :
 
260
        if keycode == 0:
253
261
            logger.warning("Sorry, can't map '%s'", key)
254
262
 
255
 
        if (self.__is_shifted(key)) :
 
263
        if (self.__is_shifted(key)):
256
264
            shift_mask = X.ShiftMask
257
 
        else :
 
265
        else:
258
266
            shift_mask = 0
259
267
 
260
268
        return keycode, shift_mask
261
269
 
262
270
    def __translate_keys(self, key_string):
263
271
        if len(key_string) > 1:
264
 
            return [self._keysym_translations.get(k, k) for k in key_string.split('+')]
 
272
            return [self._keysym_translations.get(k, k)
 
273
                    for k in key_string.split('+')]
265
274
        else:
266
275
            # workaround that lets us press_and_release '+' by itself.
267
276
            return [self._keysym_translations.get(key_string, key_string)]
298
307
        if button in _PRESSED_MOUSE_BUTTONS:
299
308
            _PRESSED_MOUSE_BUTTONS.remove(button)
300
309
        else:
301
 
            logger.warning("Generating button release event or button %d that was not pressed.", button)
 
310
            logger.warning(
 
311
                "Generating button release event or button %d that was not "
 
312
                "pressed.", button)
302
313
        fake_input(get_display(), X.ButtonRelease, button)
303
314
        get_display().sync()
304
315
 
315
326
        parameters unless they need a specific rate of movement.
316
327
 
317
328
        """
318
 
        logger.debug("Moving mouse to position %d,%d %s animation.", x, y,
319
 
            "with" if animate else "without")
320
 
 
321
329
        def perform_move(x, y, sync):
322
 
            fake_input(get_display(), X.MotionNotify, sync, X.CurrentTime, X.NONE, x=x, y=y)
 
330
            fake_input(
 
331
                get_display(),
 
332
                X.MotionNotify,
 
333
                sync,
 
334
                X.CurrentTime,
 
335
                X.NONE,
 
336
                x=x,
 
337
                y=y)
323
338
            get_display().sync()
324
339
            sleep(time_between_events)
325
340
 
 
341
        dest_x, dest_y = int(x), int(y)
 
342
        logger.debug(
 
343
            "Moving mouse to position %d,%d %s animation.", dest_x, dest_y,
 
344
            "with" if animate else "without")
 
345
 
326
346
        if not animate:
327
 
            perform_move(x, y, False)
 
347
            perform_move(dest_x, dest_y, False)
328
348
            return
329
349
 
330
 
        dest_x, dest_y = x, y
331
350
        curr_x, curr_y = self.position()
332
 
        coordinate_valid = is_point_on_any_screen((x,y))
 
351
        coordinate_valid = is_point_on_any_screen((dest_x, dest_y))
333
352
        if x < -1000 or y < -1000:
334
 
            raise ValueError("Invalid mouse coordinates: %d, %d" % (x, y))
 
353
            raise ValueError(
 
354
                "Invalid mouse coordinates: %d, %d" % (dest_x, dest_y))
335
355
 
336
356
        while curr_x != dest_x or curr_y != dest_y:
337
357
            dx = abs(dest_x - curr_x)
370
390
 
371
391
        """
372
392
        try:
373
 
            x,y,w,h = object_proxy.globalRect
 
393
            x, y, w, h = object_proxy.globalRect
374
394
            logger.debug("Moving to object's globalRect coordinates.")
375
395
            self.move(x+w/2, y+h/2)
376
396
            return
377
397
        except AttributeError:
378
398
            pass
379
399
        except (TypeError, ValueError):
380
 
            raise ValueError("Object '%r' has globalRect attribute, but it is not of the correct type" % object_proxy)
 
400
            raise ValueError(
 
401
                "Object '%r' has globalRect attribute, but it is not of the "
 
402
                "correct type" % object_proxy)
381
403
 
382
404
        try:
383
 
            x,y = object_proxy.center_x, object_proxy.center_y
 
405
            x, y = object_proxy.center_x, object_proxy.center_y
384
406
            logger.debug("Moving to object's center_x, center_y coordinates.")
385
 
            self.move(x,y)
 
407
            self.move(x, y)
386
408
            return
387
409
        except AttributeError:
388
410
            pass
389
411
        except (TypeError, ValueError):
390
 
            raise ValueError("Object '%r' has center_x, center_y attributes, but they are not of the correct type" % object_proxy)
 
412
            raise ValueError(
 
413
                "Object '%r' has center_x, center_y attributes, but they are "
 
414
                "not of the correct type" % object_proxy)
391
415
 
392
416
        try:
393
 
            x,y,w,h = object_proxy.x, object_proxy.y, object_proxy.w, object_proxy.h
394
 
            logger.debug("Moving to object's center point calculated from x,y,w,h attributes.")
395
 
            self.move(x+w/2,y+h/2)
 
417
            x, y, w, h = (
 
418
                object_proxy.x, object_proxy.y, object_proxy.w, object_proxy.h)
 
419
            logger.debug(
 
420
                "Moving to object's center point calculated from x,y,w,h "
 
421
                "attributes.")
 
422
            self.move(x+w/2, y+h/2)
396
423
            return
397
424
        except AttributeError:
398
 
            raise ValueError("Object '%r' does not have any recognised position attributes" % object_proxy)
 
425
            raise ValueError(
 
426
                "Object '%r' does not have any recognised position "
 
427
                "attributes" % object_proxy)
399
428
        except (TypeError, ValueError):
400
 
            raise ValueError("Object '%r' has x,y attribute, but they are not of the correct type" % object_proxy)
 
429
            raise ValueError(
 
430
                "Object '%r' has x,y attribute, but they are not of the "
 
431
                "correct type" % object_proxy)
401
432
 
402
433
    def position(self):
403
434
        """
411
442
        return x, y
412
443
 
413
444
    def drag(self, x1, y1, x2, y2):
414
 
        """Performs a press, move and release
415
 
        This is to keep a common API between Mouse and Finger as long as possible"""
 
445
        """Performs a press, move and release.
 
446
 
 
447
        This is to keep a common API between Mouse and Finger as long as
 
448
        possible.
 
449
 
 
450
        """
416
451
        self.move(x1, y1)
417
452
        self.press()
418
453
        self.move(x2, y2)
419
454
        self.release()
420
455
 
421
456
    @classmethod
422
 
    def on_test_end():
 
457
    def on_test_end(cls, test_instance):
423
458
        """Put mouse in a known safe state."""
424
459
        global _PRESSED_MOUSE_BUTTONS
425
460
        for btn in _PRESSED_MOUSE_BUTTONS: