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