• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""An adapter to remotely access the display facade on DUT."""
6
7import logging
8import os
9import tempfile
10import xmlrpclib
11
12from PIL import Image
13
14from autotest_lib.client.common_lib import error
15from autotest_lib.client.cros.multimedia.display_info import DisplayInfo
16
17
18class DisplayFacadeRemoteAdapter(object):
19    """DisplayFacadeRemoteAdapter is an adapter to remotely control DUT display.
20
21    The Autotest host object representing the remote DUT, passed to this
22    class on initialization, can be accessed from its _client property.
23
24    """
25    def __init__(self, host, remote_facade_proxy):
26        """Construct a DisplayFacadeRemoteAdapter.
27
28        @param host: Host object representing a remote host.
29        @param remote_facade_proxy: RemoteFacadeProxy object.
30        """
31        self._client = host
32        self._proxy = remote_facade_proxy
33
34
35    @property
36    def _display_proxy(self):
37        """Gets the proxy to DUT display facade.
38
39        @return XML RPC proxy to DUT display facade.
40        """
41        return self._proxy.display
42
43
44    def get_external_connector_name(self):
45        """Gets the name of the external output connector.
46
47        @return The external output connector name as a string; False if nothing
48                is connected.
49        """
50        return self._display_proxy.get_external_connector_name()
51
52
53    def get_internal_connector_name(self):
54        """Gets the name of the internal output connector.
55
56        @return The internal output connector name as a string; False if nothing
57                is connected.
58        """
59        return self._display_proxy.get_internal_connector_name()
60
61
62    def get_display_notifications(self):
63        """Gets the display notifications
64
65        @return: Returns a list of display related notifications only.
66        """
67        return self._display_proxy.get_display_notifications()
68
69    def move_to_display(self, display_id):
70        """Moves the current window to the indicated display.
71
72        @param display_id: The id of the indicated display.
73        """
74        self._display_proxy.move_to_display(display_id)
75
76
77    def create_window(self, url='chrome://newtab'):
78        """Creates a new window from chrome.windows.create API.
79
80        @param url: Optional URL for the new window.
81
82        @return Identifier for the new window.
83        """
84        return self._display_proxy.create_window(url)
85
86
87    def update_window(self, window_id, state=None, bounds=None):
88        """Updates an existing window using the chrome.windows.update API.
89
90        @param window_id: Identifier for the window to update.
91        @param state: Optional string to set the state such as 'normal',
92                      'maximized', or 'fullscreen'.
93        @param bounds: Optional dictionary with keys top, left, width, and
94                       height to reposition the window.
95        """
96        self._display_proxy.update_window(window_id, state, bounds)
97
98
99    def set_fullscreen(self, is_fullscreen):
100        """Sets the current window to full screen.
101
102        @param is_fullscreen: True or False to indicate fullscreen state.
103        @return True if success, False otherwise.
104        """
105        return self._display_proxy.set_fullscreen(is_fullscreen)
106
107
108    def load_url(self, url):
109        """Loads the given url in a new tab. The new tab will be active.
110
111        @param url: The url to load as a string.
112        @return a str, the tab descriptor of the opened tab.
113        """
114        return self._display_proxy.load_url(url)
115
116
117    def load_calibration_image(self, resolution):
118        """Load a full screen calibration image from the HTTP server.
119
120        @param resolution: A tuple (width, height) of resolution.
121        @return a str, the tab descriptor of the opened tab.
122        """
123        return self._display_proxy.load_calibration_image(resolution)
124
125
126    def load_color_sequence(self, tab_descriptor, color_sequence):
127        """Displays a series of colors on full screen on the tab.
128        tab_descriptor is returned by any open tab API of display facade.
129        e.g.,
130        tab_descriptor = load_url('about:blank')
131        load_color_sequence(tab_descriptor, color)
132
133        @param tab_descriptor: Indicate which tab to test.
134        @param color_sequence: An integer list for switching colors.
135        @return A list of the timestamp for each switch.
136        """
137        return self._display_proxy.load_color_sequence(tab_descriptor,
138                                                       color_sequence)
139
140
141    def close_tab(self, tab_descriptor):
142        """Disables fullscreen and closes the tab of the given tab descriptor.
143        tab_descriptor is returned by any open tab API of display facade.
144        e.g.,
145        1.
146        tab_descriptor = load_url(url)
147        close_tab(tab_descriptor)
148
149        2.
150        tab_descriptor = load_calibration_image(resolution)
151        close_tab(tab_descriptor)
152
153        @param tab_descriptor: Indicate which tab to close.
154        """
155        self._display_proxy.close_tab(tab_descriptor)
156
157
158    def is_mirrored_enabled(self):
159        """Checks the mirrored state.
160
161        @return True if mirrored mode is enabled.
162        """
163        return self._display_proxy.is_mirrored_enabled()
164
165
166    def set_mirrored(self, is_mirrored):
167        """Sets mirrored mode.
168
169        @param is_mirrored: True or False to indicate mirrored state.
170        @throws error.TestError when the call fails.
171        """
172        if not self._display_proxy.set_mirrored(is_mirrored):
173            raise error.TestError('Failed to set_mirrored(%s)' % is_mirrored)
174
175
176    def is_display_primary(self, internal=True):
177        """Checks if internal screen is primary display.
178
179        @param internal: is internal/external screen primary status requested
180        @return boolean True if internal display is primary.
181        """
182        return self._display_proxy.is_display_primary(internal)
183
184
185    def suspend_resume(self, suspend_time=10):
186        """Suspends the DUT for a given time in second.
187
188        @param suspend_time: Suspend time in second, default: 10s.
189        """
190        try:
191            self._display_proxy.suspend_resume(suspend_time)
192        except xmlrpclib.Fault as e:
193            # Log suspend/resume errors but continue the test.
194            logging.error('suspend_resume error: %s', str(e))
195
196
197    def suspend_resume_bg(self, suspend_time=10):
198        """Suspends the DUT for a given time in second in the background.
199
200        @param suspend_time: Suspend time in second, default: 10s.
201        """
202        # TODO(waihong): Use other general API instead of this RPC.
203        self._display_proxy.suspend_resume_bg(suspend_time)
204
205
206    def wait_external_display_connected(self, display):
207        """Waits for the specified display to be connected.
208
209        @param display: The display name as a string, like 'HDMI1', or
210                        False if no external display is expected.
211        @return: True if display is connected; False otherwise.
212        """
213        return self._display_proxy.wait_external_display_connected(display)
214
215
216    def hide_cursor(self):
217        """Hides mouse cursor by sending a keystroke."""
218        self._display_proxy.hide_cursor()
219
220
221    def hide_typing_cursor(self):
222        """Hides typing cursor by moving outside typing bar."""
223        self._display_proxy.hide_typing_cursor()
224
225
226    def set_content_protection(self, state):
227        """Sets the content protection of the external screen.
228
229        @param state: One of the states 'Undesired', 'Desired', or 'Enabled'
230        """
231        self._display_proxy.set_content_protection(state)
232
233
234    def get_content_protection(self):
235        """Gets the state of the content protection.
236
237        @param output: The output name as a string.
238        @return: A string of the state, like 'Undesired', 'Desired', or 'Enabled'.
239                 False if not supported.
240        """
241        return self._display_proxy.get_content_protection()
242
243
244    def _take_screenshot(self, screenshot_func):
245        """Gets screenshot from DUT.
246
247        @param screenshot_func: function to take a screenshot and save the image
248                to specified path on DUT. Usage: screenshot_func(remote_path).
249
250        @return: An Image object.
251                 Notice that the returned image may not be in RGB format,
252                 depending on PIL implementation.
253        """
254        with tempfile.NamedTemporaryFile(suffix='.png') as f:
255            basename = os.path.basename(f.name)
256            remote_path = os.path.join('/tmp', basename)
257            screenshot_func(remote_path)
258            self._client.get_file(remote_path, f.name)
259            return Image.open(f.name)
260
261
262    def capture_internal_screen(self):
263        """Captures the internal screen framebuffer.
264
265        @return: An Image object.
266        """
267        screenshot_func = self._display_proxy.take_internal_screenshot
268        return self._take_screenshot(screenshot_func)
269
270
271    # TODO(ihf): This needs to be fixed for multiple external screens.
272    def capture_external_screen(self):
273        """Captures the external screen framebuffer.
274
275        @return: An Image object.
276        """
277        screenshot_func = self._display_proxy.take_external_screenshot
278        return self._take_screenshot(screenshot_func)
279
280
281    def capture_calibration_image(self):
282        """Captures the calibration image.
283
284        @return: An Image object.
285        """
286        screenshot_func = self._display_proxy.save_calibration_image
287        return self._take_screenshot(screenshot_func)
288
289
290    def get_external_resolution(self):
291        """Gets the resolution of the external screen.
292
293        @return The resolution tuple (width, height) or None if no external
294                display is connected.
295        """
296        resolution = self._display_proxy.get_external_resolution()
297        return tuple(resolution) if resolution else None
298
299
300    def get_internal_resolution(self):
301        """Gets the resolution of the internal screen.
302
303        @return The resolution tuple (width, height) or None if no internal
304                display.
305        """
306        resolution = self._display_proxy.get_internal_resolution()
307        return tuple(resolution) if resolution else None
308
309
310    def set_resolution(self, display_id, width, height):
311        """Sets the resolution on the specified display.
312
313        @param display_id: id of the display to set resolutions for.
314        @param width: width of the resolution
315        @param height: height of the resolution
316        """
317        self._display_proxy.set_resolution(display_id, width, height)
318
319
320    # pylint: disable = W0141
321    def get_display_info(self):
322        """Gets the information of all the displays that are connected to the
323                DUT.
324
325        @return: list of object DisplayInfo for display informtion
326        """
327        return map(DisplayInfo, self._display_proxy.get_display_info())
328
329
330    def get_display_modes(self, display_id):
331        """Gets the display modes of the specified display.
332
333        @param display_id: id of the display to get modes from; the id is from
334            the DisplayInfo list obtained by get_display_info().
335
336        @return: list of DisplayMode dicts.
337        """
338        return self._display_proxy.get_display_modes(display_id)
339
340
341    def get_available_resolutions(self, display_id):
342        """Gets the resolutions from the specified display.
343
344        @param display_id: id of the display to get modes from.
345
346        @return a list of (width, height) tuples.
347        """
348        return [tuple(r) for r in
349                self._display_proxy.get_available_resolutions(display_id)]
350
351
352    def get_display_rotation(self, display_id):
353        """Gets the display rotation for the specified display.
354
355        @param display_id: id of the display to get modes from.
356
357        @return: Degree of rotation.
358        """
359        return self._display_proxy.get_display_rotation(display_id)
360
361
362    def set_display_rotation(self, display_id, rotation,
363                             delay_before_rotation=0, delay_after_rotation=0):
364        """Sets the display rotation for the specified display.
365
366        @param display_id: id of the display to get modes from.
367        @param rotation: degree of rotation
368        @param delay_before_rotation: time in second for delay before rotation
369        @param delay_after_rotation: time in second for delay after rotation
370        """
371        self._display_proxy.set_display_rotation(
372                display_id, rotation, delay_before_rotation,
373                delay_after_rotation)
374
375
376    def get_internal_display_id(self):
377        """Gets the internal display id.
378
379        @return the id of the internal display.
380        """
381        return self._display_proxy.get_internal_display_id()
382
383
384    def get_first_external_display_id(self):
385        """Gets the first external display id.
386
387        @return the id of the first external display; -1 if not found.
388        """
389        return self._display_proxy.get_first_external_display_id()
390
391
392    def reset_connector_if_applicable(self, connector_type):
393        """Resets Type-C video connector from host end if applicable.
394
395        It's the workaround sequence since sometimes Type-C dongle becomes
396        corrupted and needs to be re-plugged.
397
398        @param connector_type: A string, like "VGA", "DVI", "HDMI", or "DP".
399        """
400        logging.info('Connector Type %s.', connector_type)
401        return self._display_proxy.reset_connector_if_applicable(connector_type)
402