~azzar1/unity/scale-left-padding

« back to all changes in this revision

Viewing changes to tests/autopilot/autopilot/emulators/X11.py

  • Committer: Andrea Azzarone
  • Date: 2011-12-19 22:18:53 UTC
  • mfrom: (1792 unity)
  • mto: This revision was merged to the branch mainline in revision 1833.
  • Revision ID: azzaronea@gmail.com-20111219221853-wyy8fqwxk78s85ct
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
 
2
# Copyright 2010 Canonical
 
3
# Author: Alex Launi
 
4
#
 
5
# This program is free software: you can redistribute it and/or modify it 
 
6
# under the terms of the GNU General Public License version 3, as published 
 
7
# by the Free Software Foundation.
 
8
#
 
9
# This script is designed to run unity in a test drive manner. It will drive 
 
10
# X and test the GL calls that Unity makes, so that we can easily find out if
 
11
# we are triggering graphics driver/X bugs.
 
12
 
 
13
"""
 
14
A collection of emulators for X11 - namely keyboards and mice. In the future we may 
 
15
also need other devices.
 
16
"""
 
17
 
 
18
 
 
19
from time import sleep
 
20
 
 
21
from Xlib import X
 
22
from Xlib import XK
 
23
from Xlib.display import Display
 
24
from Xlib.ext.xtest import fake_input
 
25
 
 
26
class Keyboard(object):
 
27
    '''Wrapper around xlib to make faking keyboard input possible'''
 
28
    _lame_hardcoded_keycodes = {
 
29
        'A' : 64, 
 
30
        'C' : 37,
 
31
        'S' : 50,
 
32
        'T' : 23,
 
33
        'W' : 133,
 
34
        'U' : 111
 
35
        }
 
36
 
 
37
    _special_X_keysyms = {
 
38
        ' ' : "space",
 
39
        '\t' : "Tab",
 
40
        '\n' : "Return",  # for some reason this needs to be cr, not lf
 
41
        '\r' : "Return",
 
42
        '\e' : "Escape",
 
43
        '!' : "exclam",
 
44
        '#' : "numbersign",
 
45
        '%' : "percent",
 
46
        '$' : "dollar",
 
47
        '&' : "ampersand",
 
48
        '"' : "quotedbl",
 
49
        '\'' : "apostrophe",
 
50
        '(' : "parenleft",
 
51
        ')' : "parenright",
 
52
        '*' : "asterisk",
 
53
        '=' : "equal",
 
54
        '+' : "plus",
 
55
        ',' : "comma",
 
56
        '-' : "minus",
 
57
        '.' : "period",
 
58
        '/' : "slash",
 
59
        ':' : "colon",
 
60
        ';' : "semicolon",
 
61
        '<' : "less",
 
62
        '>' : "greater",
 
63
        '?' : "question",
 
64
        '@' : "at",
 
65
        '[' : "bracketleft",
 
66
        ']' : "bracketright",
 
67
        '\\' : "backslash",
 
68
        '^' : "asciicircum",
 
69
        '_' : "underscore",
 
70
        '`' : "grave",
 
71
        '{' : "braceleft",
 
72
        '|' : "bar",
 
73
        '}' : "braceright",
 
74
        '~' : "asciitilde"
 
75
        }
 
76
    
 
77
    def __init__(self):
 
78
        self._display = Display()
 
79
 
 
80
    def press(self, keys):
 
81
        """
 
82
        Send key press events for every key in the 'keys' string.
 
83
        """
 
84
        self.__perform_on_keys(keys, X.KeyPress)            
 
85
        sleep(0.2)
 
86
 
 
87
    def release(self, keys):
 
88
        """
 
89
        Send key release events for every key in the 'keys' string.
 
90
        """
 
91
        self.__perform_on_keys(keys, X.KeyRelease)
 
92
        sleep(0.2)
 
93
        
 
94
    def press_and_release(self, keys):
 
95
        """
 
96
        Send key press events for every key in the 'keys' string, then send
 
97
        key release events for every key in the 'keys' string. 
 
98
 
 
99
        This method is not appropriate for simulating a user typing a string
 
100
        of text, since it presses all the keys, and then releases them all. 
 
101
        """
 
102
        self.press(keys)
 
103
        self.release(keys)
 
104
 
 
105
    def type(self, keys):
 
106
        """
 
107
        Simulate a user typing the keys specified in 'keys'. 
 
108
 
 
109
        Each key will be pressed and released before the next key is processed. If
 
110
        you need to simulate multiple keys being pressed at the same time, use the 
 
111
        'press_and_release' method above.
 
112
        """
 
113
        for key in keys:
 
114
            self.press(key)
 
115
            self.release(key)
 
116
 
 
117
    def __perform_on_keys(self, keys, event):
 
118
        control_key = False
 
119
        keycode = 0
 
120
        shift_mask = 0
 
121
 
 
122
        for index in range(len(keys)):
 
123
            key = keys[index]
 
124
            if control_key:
 
125
                keycode = self._lame_hardcoded_keycodes[key]
 
126
                shift_mask = 0
 
127
                control_key = False
 
128
            elif index < len(keys) and key == '^' and keys[index+1] in self._lame_hardcoded_keycodes:
 
129
                control_key = True
 
130
                continue
 
131
            else:
 
132
                keycode, shift_mask = self.__char_to_keycode(key)
 
133
 
 
134
            if shift_mask != 0:
 
135
                fake_input(self._display, event, 50)
 
136
 
 
137
            fake_input(self._display, event, keycode)
 
138
        self._display.sync()
 
139
 
 
140
    def __get_keysym(self, key) :
 
141
        keysym = XK.string_to_keysym(key)
 
142
        if keysym == 0 :
 
143
            # Unfortunately, although this works to get the correct keysym
 
144
            # i.e. keysym for '#' is returned as "numbersign"
 
145
            # the subsequent display.keysym_to_keycode("numbersign") is 0.
 
146
            keysym = XK.string_to_keysym(self._special_X_keysyms[key])
 
147
        return keysym
 
148
        
 
149
    def __is_shifted(self, key) :
 
150
        if key.isupper() :
 
151
            return True
 
152
        if "~!@#$%^&*()_+{}|:\"<>?".find(key) >= 0 :
 
153
            return True
 
154
        return False
 
155
 
 
156
    def __char_to_keycode(self, key) :
 
157
        keysym = self.__get_keysym(key)
 
158
        keycode = self._display.keysym_to_keycode(keysym)
 
159
        if keycode == 0 :
 
160
            print "Sorry, can't map", key
 
161
            
 
162
        if (self.__is_shifted(key)) :
 
163
            shift_mask = X.ShiftMask
 
164
        else :
 
165
            shift_mask = 0
 
166
 
 
167
        return keycode, shift_mask
 
168
 
 
169
class Mouse(object):
 
170
    '''Wrapper around xlib to make moving the mouse easier'''
 
171
        
 
172
    def __init__(self):
 
173
        self._display = Display()
 
174
                
 
175
    def press(self, button=1):
 
176
        '''Press mouse button at current mouse location'''
 
177
        fake_input(self._display, X.ButtonPress, button)
 
178
        self._display.sync()
 
179
                
 
180
    def release(self, button=1):
 
181
        '''Releases mouse button at current mouse location'''
 
182
        fake_input(self._display, X.ButtonRelease, button)
 
183
        self._display.sync()
 
184
                
 
185
    def click(self, button=1):
 
186
        '''Click mouse at current location'''
 
187
        self.press(button)
 
188
        sleep(0.25)
 
189
        self.release(button)
 
190
                
 
191
    def move(self, x, y, animate=True):
 
192
        '''Moves mouse to location (x, y)'''
 
193
        def perform_move(x, y):
 
194
            fake_input(self._display, X.MotionNotify, x=x, y=y)
 
195
            self._display.sync()
 
196
            sleep(0.001)
 
197
 
 
198
        if not animate:
 
199
            perform_move(x, y)
 
200
                        
 
201
        dest_x, dest_y = x, y
 
202
        curr_x, curr_y = self.position()
 
203
                
 
204
        # calculate a path from our current position to our destination
 
205
        dy = float(curr_y - dest_y)
 
206
        dx = float(curr_x - dest_x)
 
207
        slope = dy/dx if dx > 0 else 0
 
208
        yint = curr_y - (slope * curr_x)
 
209
        xscale = 1 if dest_x > curr_x else -1
 
210
        
 
211
        while (int(curr_x) != dest_x):
 
212
            curr_x += xscale;
 
213
            curr_y = int(slope * curr_x + yint) if curr_y > 0 else dest_y
 
214
                        
 
215
            perform_move(curr_x, curr_y)
 
216
                        
 
217
        if (curr_y != dest_y):
 
218
            yscale = 1 if dest_y > curr_y else -1       
 
219
            while (curr_y != dest_y):
 
220
                curr_y += yscale
 
221
                perform_move(curr_x, curr_y)
 
222
                                
 
223
    def position(self):
 
224
        '''Returns the current position of the mouse pointer'''
 
225
        coord = self._display.screen().root.query_pointer()._data
 
226
        x, y = coord["root_x"], coord["root_y"]
 
227
        return x, y
 
228
        
 
229
    def reset(self):
 
230
        self.move(16, 13, animate=False)
 
231
        self.click()
 
232
        self.move(800, 500, animate=False)