• 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 move_to_display(self, display_id):
63        """Moves the current window to the indicated display.
64
65        @param display_id: The id of the indicated display.
66        """
67        self._display_proxy.move_to_display(display_id)
68
69
70    def set_fullscreen(self, is_fullscreen):
71        """Sets the current window to full screen.
72
73        @param is_fullscreen: True or False to indicate fullscreen state.
74        @return True if success, False otherwise.
75        """
76        return self._display_proxy.set_fullscreen(is_fullscreen)
77
78
79    def load_url(self, url):
80        """Loads the given url in a new tab. The new tab will be active.
81
82        @param url: The url to load as a string.
83        @return a str, the tab descriptor of the opened tab.
84        """
85        return self._display_proxy.load_url(url)
86
87
88    def load_calibration_image(self, resolution):
89        """Load a full screen calibration image from the HTTP server.
90
91        @param resolution: A tuple (width, height) of resolution.
92        @return a str, the tab descriptor of the opened tab.
93        """
94        return self._display_proxy.load_calibration_image(resolution)
95
96
97    def load_color_sequence(self, tab_descriptor, color_sequence):
98        """Displays a series of colors on full screen on the tab.
99        tab_descriptor is returned by any open tab API of display facade.
100        e.g.,
101        tab_descriptor = load_url('about:blank')
102        load_color_sequence(tab_descriptor, color)
103
104        @param tab_descriptor: Indicate which tab to test.
105        @param color_sequence: An integer list for switching colors.
106        @return A list of the timestamp for each switch.
107        """
108        return self._display_proxy.load_color_sequence(tab_descriptor,
109                                                       color_sequence)
110
111
112    def close_tab(self, tab_descriptor):
113        """Disables fullscreen and closes the tab of the given tab descriptor.
114        tab_descriptor is returned by any open tab API of display facade.
115        e.g.,
116        1.
117        tab_descriptor = load_url(url)
118        close_tab(tab_descriptor)
119
120        2.
121        tab_descriptor = load_calibration_image(resolution)
122        close_tab(tab_descriptor)
123
124        @param tab_descriptor: Indicate which tab to close.
125        """
126        self._display_proxy.close_tab(tab_descriptor)
127
128
129    def is_mirrored_enabled(self):
130        """Checks the mirrored state.
131
132        @return True if mirrored mode is enabled.
133        """
134        return self._display_proxy.is_mirrored_enabled()
135
136
137    def set_mirrored(self, is_mirrored):
138        """Sets mirrored mode.
139
140        @param is_mirrored: True or False to indicate mirrored state.
141        @throws error.TestError when the call fails.
142        """
143        if not self._display_proxy.set_mirrored(is_mirrored):
144            raise error.TestError('Failed to set_mirrored(%s)' % is_mirrored)
145
146
147    def is_display_primary(self, internal=True):
148        """Checks if internal screen is primary display.
149
150        @param internal: is internal/external screen primary status requested
151        @return boolean True if internal display is primary.
152        """
153        return self._display_proxy.is_display_primary(internal)
154
155
156    def suspend_resume(self, suspend_time=10):
157        """Suspends the DUT for a given time in second.
158
159        @param suspend_time: Suspend time in second, default: 10s.
160        """
161        try:
162            self._display_proxy.suspend_resume(suspend_time)
163        except xmlrpclib.Fault as e:
164            # Log suspend/resume errors but continue the test.
165            logging.error('suspend_resume error: %s', str(e))
166
167
168    def suspend_resume_bg(self, suspend_time=10):
169        """Suspends the DUT for a given time in second in the background.
170
171        @param suspend_time: Suspend time in second, default: 10s.
172        """
173        # TODO(waihong): Use other general API instead of this RPC.
174        self._display_proxy.suspend_resume_bg(suspend_time)
175
176
177    def wait_external_display_connected(self, display):
178        """Waits for the specified display to be connected.
179
180        @param display: The display name as a string, like 'HDMI1', or
181                        False if no external display is expected.
182        @return: True if display is connected; False otherwise.
183        """
184        return self._display_proxy.wait_external_display_connected(display)
185
186
187    def hide_cursor(self):
188        """Hides mouse cursor by sending a keystroke."""
189        self._display_proxy.hide_cursor()
190
191
192    def hide_typing_cursor(self):
193        """Hides typing cursor by moving outside typing bar."""
194        self._display_proxy.hide_typing_cursor()
195
196
197    def set_content_protection(self, state):
198        """Sets the content protection of the external screen.
199
200        @param state: One of the states 'Undesired', 'Desired', or 'Enabled'
201        """
202        self._display_proxy.set_content_protection(state)
203
204
205    def get_content_protection(self):
206        """Gets the state of the content protection.
207
208        @param output: The output name as a string.
209        @return: A string of the state, like 'Undesired', 'Desired', or 'Enabled'.
210                 False if not supported.
211        """
212        return self._display_proxy.get_content_protection()
213
214
215    def _take_screenshot(self, screenshot_func):
216        """Gets screenshot from DUT.
217
218        @param screenshot_func: function to take a screenshot and save the image
219                to specified path on DUT. Usage: screenshot_func(remote_path).
220
221        @return: An Image object.
222                 Notice that the returned image may not be in RGB format,
223                 depending on PIL implementation.
224        """
225        with tempfile.NamedTemporaryFile(suffix='.png') as f:
226            basename = os.path.basename(f.name)
227            remote_path = os.path.join('/tmp', basename)
228            screenshot_func(remote_path)
229            self._client.get_file(remote_path, f.name)
230            return Image.open(f.name)
231
232
233    def capture_internal_screen(self):
234        """Captures the internal screen framebuffer.
235
236        @return: An Image object.
237        """
238        screenshot_func = self._display_proxy.take_internal_screenshot
239        return self._take_screenshot(screenshot_func)
240
241
242    # TODO(ihf): This needs to be fixed for multiple external screens.
243    def capture_external_screen(self):
244        """Captures the external screen framebuffer.
245
246        @return: An Image object.
247        """
248        screenshot_func = self._display_proxy.take_external_screenshot
249        return self._take_screenshot(screenshot_func)
250
251
252    def capture_calibration_image(self):
253        """Captures the calibration image.
254
255        @return: An Image object.
256        """
257        screenshot_func = self._display_proxy.save_calibration_image
258        return self._take_screenshot(screenshot_func)
259
260
261    def get_external_resolution(self):
262        """Gets the resolution of the external screen.
263
264        @return The resolution tuple (width, height) or None if no external
265                display is connected.
266        """
267        resolution = self._display_proxy.get_external_resolution()
268        return tuple(resolution) if resolution else None
269
270
271    def get_internal_resolution(self):
272        """Gets the resolution of the internal screen.
273
274        @return The resolution tuple (width, height) or None if no internal
275                display.
276        """
277        resolution = self._display_proxy.get_internal_resolution()
278        return tuple(resolution) if resolution else None
279
280
281    def set_resolution(self, display_id, width, height):
282        """Sets the resolution on the specified display.
283
284        @param display_id: id of the display to set resolutions for.
285        @param width: width of the resolution
286        @param height: height of the resolution
287        """
288        self._display_proxy.set_resolution(display_id, width, height)
289
290
291    # pylint: disable = W0141
292    def get_display_info(self):
293        """Gets the information of all the displays that are connected to the
294                DUT.
295
296        @return: list of object DisplayInfo for display informtion
297        """
298        return map(DisplayInfo, self._display_proxy.get_display_info())
299
300
301    def get_display_modes(self, display_id):
302        """Gets the display modes of the specified display.
303
304        @param display_id: id of the display to get modes from; the id is from
305            the DisplayInfo list obtained by get_display_info().
306
307        @return: list of DisplayMode dicts.
308        """
309        return self._display_proxy.get_display_modes(display_id)
310
311
312    def get_available_resolutions(self, display_id):
313        """Gets the resolutions from the specified display.
314
315        @param display_id: id of the display to get modes from.
316
317        @return a list of (width, height) tuples.
318        """
319        return [tuple(r) for r in
320                self._display_proxy.get_available_resolutions(display_id)]
321
322
323    def get_display_rotation(self, display_id):
324        """Gets the display rotation for the specified display.
325
326        @param display_id: id of the display to get modes from.
327
328        @return: Degree of rotation.
329        """
330        return self._display_proxy.get_display_rotation(display_id)
331
332
333    def set_display_rotation(self, display_id, rotation,
334                             delay_before_rotation=0, delay_after_rotation=0):
335        """Sets the display rotation for the specified display.
336
337        @param display_id: id of the display to get modes from.
338        @param rotation: degree of rotation
339        @param delay_before_rotation: time in second for delay before rotation
340        @param delay_after_rotation: time in second for delay after rotation
341        """
342        self._display_proxy.set_display_rotation(
343                display_id, rotation, delay_before_rotation,
344                delay_after_rotation)
345
346
347    def get_internal_display_id(self):
348        """Gets the internal display id.
349
350        @return the id of the internal display.
351        """
352        return self._display_proxy.get_internal_display_id()
353
354
355    def get_first_external_display_id(self):
356        """Gets the first external display id.
357
358        @return the id of the first external display; -1 if not found.
359        """
360        return self._display_proxy.get_first_external_display_id()
361
362
363    def reset_connector_if_applicable(self, connector_type):
364        """Resets Type-C video connector from host end if applicable.
365
366        It's the workaround sequence since sometimes Type-C dongle becomes
367        corrupted and needs to be re-plugged.
368
369        @param connector_type: A string, like "VGA", "DVI", "HDMI", or "DP".
370        """
371        logging.info('Connector Type %s.', connector_type)
372        return self._display_proxy.reset_connector_if_applicable(connector_type)
373