Home | History | Annotate | Download | only in multimedia
      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 
      7 import logging
      8 import os
      9 import tempfile
     10 import xmlrpclib
     11 
     12 from PIL import Image
     13 
     14 from autotest_lib.client.common_lib import error
     15 from autotest_lib.client.cros.multimedia.display_info import DisplayInfo
     16 
     17 
     18 class 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