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