| # Copyright 2014 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """An adapter to access the local display facade.""" |
| |
| import logging |
| import tempfile |
| from PIL import Image |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.cros.multimedia import display_facade_native |
| from autotest_lib.client.cros.multimedia import facade_resource |
| from autotest_lib.client.cros.multimedia.display_info import DisplayInfo |
| from autotest_lib.client.cros.power import sys_power |
| |
| |
| class DisplayFacadeLocalAdapter(object): |
| """DisplayFacadeLocalAdapter is an adapter to control the local display. |
| |
| Methods with non-native-type arguments go to this class and do some |
| conversion; otherwise, go to the DisplayFacadeNative class. |
| """ |
| |
| def __init__(self, chrome): |
| """Construct a DisplayFacadeLocalAdapter. |
| |
| @param chrome: A Chrome object. |
| """ |
| # Create a DisplayFacadeNative object as a component such that this |
| # class can expose and manipulate its interfaces. |
| self._display_component = display_facade_native.DisplayFacadeNative( |
| facade_resource.FacadeResource(chrome_object=chrome)) |
| |
| |
| def get_external_connector_name(self): |
| """Gets the name of the external output connector. |
| |
| @return The external output connector name as a string; False if nothing |
| is connected. |
| """ |
| return self._display_component.get_external_connector_name() |
| |
| |
| def get_internal_connector_name(self): |
| """Gets the name of the internal output connector. |
| |
| @return The internal output connector name as a string; False if nothing |
| is connected. |
| """ |
| return self._display_component.get_internal_connector_name() |
| |
| |
| def move_to_display(self, display_id): |
| """Moves the current window to the indicated display. |
| |
| @param display_id: The id of the indicated display. |
| """ |
| self._display_component.move_to_display(display_id) |
| |
| |
| def create_window(self, url='chrome://newtab'): |
| """Creates a new window from chrome.windows.create API. |
| |
| @param url: Optional URL for the new window. |
| |
| @return Identifier for the new window. |
| """ |
| return self._display_component.create_window(url) |
| |
| |
| def update_window(self, window_id, state=None, bounds=None): |
| """Updates an existing window using the chrome.windows.update API. |
| |
| @param window_id: Identifier for the window to update. |
| @param state: Optional string to set the state such as 'normal', |
| 'maximized', or 'fullscreen'. |
| @param bounds: Optional dictionary with keys top, left, width, and |
| height to reposition the window. |
| """ |
| self._display_component.update_window(window_id, state, bounds) |
| |
| |
| def set_fullscreen(self, is_fullscreen): |
| """Sets the current window to full screen. |
| |
| @param is_fullscreen: True or False to indicate fullscreen state. |
| @return True if success, False otherwise. |
| """ |
| return self._display_component.set_fullscreen(is_fullscreen) |
| |
| |
| def load_url(self, url): |
| """Loads the given url in a new tab. The new tab will be active. |
| |
| @param url: The url to load as a string. |
| @return a str, the tab descriptor of the opened tab. |
| """ |
| return self._display_component.load_url(url) |
| |
| |
| def load_calibration_image(self, resolution): |
| """Load a full screen calibration image from the HTTP server. |
| |
| @param resolution: A tuple (width, height) of resolution. |
| @return a str, the tab descriptor of the opened tab. |
| """ |
| return self._display_component.load_calibration_image(resolution) |
| |
| |
| def load_color_sequence(self, tab_descriptor, color_sequence): |
| """Displays a series of colors on full screen on the tab. |
| tab_descriptor is returned by any open tab API of display facade. |
| e.g., |
| tab_descriptor = load_url('about:blank') |
| load_color_sequence(tab_descriptor, color) |
| |
| @param tab_descriptor: Indicate which tab to test. |
| @param color_sequence: An integer list for switching colors. |
| @return A list of the timestamp for each switch. |
| """ |
| return self._display_component.load_color_sequence(tab_descriptor, |
| color_sequence) |
| |
| |
| def close_tab(self, tab_descriptor): |
| """Disables fullscreen and closes the tab of the given tab descriptor. |
| tab_descriptor is returned by any open tab API of display facade. |
| e.g., |
| 1. |
| tab_descriptor = load_url(url) |
| close_tab(tab_descriptor) |
| |
| 2. |
| tab_descriptor = load_calibration_image(resolution) |
| close_tab(tab_descriptor) |
| |
| @param tab_descriptor: Indicate which tab to close. |
| """ |
| self._display_component.close_tab(tab_descriptor) |
| |
| |
| def is_mirrored_enabled(self): |
| """Checks the mirrored state. |
| |
| @return True if mirrored mode is enabled. |
| """ |
| return self._display_component.is_mirrored_enabled() |
| |
| |
| def set_mirrored(self, is_mirrored): |
| """Sets mirrored mode. |
| |
| @param is_mirrored: True or False to indicate mirrored state. |
| @throws error.TestError when the call fails. |
| """ |
| if not self._display_component.set_mirrored(is_mirrored): |
| raise error.TestError('Failed to set_mirrored(%s)' % is_mirrored) |
| |
| |
| def is_display_primary(self, internal=True): |
| """Checks if internal screen is primary display. |
| |
| @param internal: is internal/external screen primary status requested |
| @return boolean True if internal display is primary. |
| """ |
| return self._display_component.is_display_primary(internal) |
| |
| |
| def suspend_resume(self, suspend_time=10): |
| """Suspends the DUT for a given time in second. |
| |
| @param suspend_time: Suspend time in second. |
| """ |
| try: |
| self._display_component.suspend_resume(suspend_time) |
| except sys_power.SpuriousWakeupError as e: |
| # Log suspend/resume errors but continue the test. |
| logging.error('suspend_resume error: %s', str(e)) |
| |
| |
| def suspend_resume_bg(self, suspend_time=10): |
| """Suspends the DUT for a given time in second in the background. |
| |
| @param suspend_time: Suspend time in second, default: 10s. |
| """ |
| self._display_component.suspend_resume_bg(suspend_time) |
| |
| |
| def wait_external_display_connected(self, display): |
| """Waits for the specified display to be connected. |
| |
| @param display: The display name as a string, like 'HDMI1', or |
| False if no external display is expected. |
| @return: True if display is connected; False otherwise. |
| """ |
| return self._display_component.wait_external_display_connected(display) |
| |
| |
| def hide_cursor(self): |
| """Hides mouse cursor by sending a keystroke.""" |
| self._display_component.hide_cursor() |
| |
| |
| def hide_typing_cursor(self): |
| """Hides typing cursor by moving outside typing bar.""" |
| self._display_component.hide_typing_cursor() |
| |
| |
| def set_content_protection(self, state): |
| """Sets the content protection of the external screen. |
| |
| @param state: One of the states 'Undesired', 'Desired', or 'Enabled' |
| """ |
| self._display_component.set_content_protection(state) |
| |
| |
| def get_content_protection(self): |
| """Gets the state of the content protection. |
| |
| @param output: The output name as a string. |
| @return: A string of the state, like 'Undesired', 'Desired', or 'Enabled'. |
| False if not supported. |
| """ |
| return self._display_component.get_content_protection() |
| |
| |
| def _take_screenshot(self, screenshot_func): |
| """Gets screenshot from frame buffer. |
| |
| @param screenshot_func: function to take a screenshot and save the image |
| to specified path. Usage: screenshot_func(path). |
| |
| @return: An Image object. |
| Notice that the returned image may not be in RGB format, |
| depending on PIL implementation. |
| """ |
| with tempfile.NamedTemporaryFile(suffix='.png') as f: |
| screenshot_func(f.name) |
| return Image.open(f.name) |
| |
| |
| def capture_internal_screen(self): |
| """Captures the internal screen framebuffer. |
| |
| @return: An Image object. |
| """ |
| screenshot_func = self._display_component.take_internal_screenshot |
| return self._take_screenshot(screenshot_func) |
| |
| |
| # TODO(ihf): This function needs to be fixed for multiple screens. |
| def capture_external_screen(self): |
| """Captures the external screen framebuffer. |
| |
| @return: An Image object. |
| """ |
| screenshot_func = self._display_component.take_external_screenshot |
| return self._take_screenshot(screenshot_func) |
| |
| |
| def capture_calibration_image(self): |
| """Captures the calibration image. |
| |
| @return: An Image object. |
| """ |
| screenshot_func = self._display_component.save_calibration_image |
| return self._take_screenshot(screenshot_func) |
| |
| |
| def get_external_resolution(self): |
| """Gets the resolution of the external screen. |
| |
| @return The resolution tuple (width, height) or None if no external |
| display is connected. |
| """ |
| resolution = self._display_component.get_external_resolution() |
| return tuple(resolution) if resolution else None |
| |
| |
| def get_internal_resolution(self): |
| """Gets the resolution of the internal screen. |
| |
| @return The resolution tuple (width, height) or None if no internal |
| display. |
| """ |
| resolution = self._display_component.get_internal_resolution() |
| return tuple(resolution) if resolution else None |
| |
| |
| def set_resolution(self, display_id, width, height): |
| """Sets the resolution on the specified display. |
| |
| @param display_id: id of the display to set resolutions for. |
| @param width: width of the resolution |
| @param height: height of the resolution |
| """ |
| self._display_component.set_resolution(display_id, width, height) |
| |
| |
| def get_display_info(self): |
| """Gets the information of all the displays that are connected to the |
| DUT. |
| |
| @return: list of object DisplayInfo for display informtion |
| """ |
| return map(DisplayInfo, self._display_component.get_display_info()) |
| |
| |
| def get_display_modes(self, display_id): |
| """Gets the display modes of the specified display. |
| |
| @param display_id: id of the display to get modes from; the id is from |
| the DisplayInfo list obtained by get_display_info(). |
| |
| @return: list of DisplayMode dicts. |
| """ |
| return self._display_component.get_display_modes(display_id) |
| |
| |
| def get_available_resolutions(self, display_id): |
| """Gets the resolutions from the specified display. |
| |
| @param display_id: id of the display to get modes from. |
| |
| @return a list of (width, height) tuples. |
| """ |
| return [tuple(r) for r in |
| self._display_component.get_available_resolutions(display_id)] |
| |
| |
| def get_display_rotation(self, display_id): |
| """Gets the display rotation for the specified display. |
| |
| @param display_id: id of the display to get modes from. |
| |
| @return: Degree of rotation. |
| """ |
| return self._display_component.get_display_rotation(display_id) |
| |
| |
| def set_display_rotation(self, display_id, rotation, |
| delay_before_rotation=0, delay_after_rotation=0): |
| """Sets the display rotation for the specified display. |
| |
| @param display_id: id of the display to get modes from. |
| @param rotation: degree of rotation |
| @param delay_before_rotation: time in second for delay before rotation |
| @param delay_after_rotation: time in second for delay after rotation |
| """ |
| self._display_component.set_display_rotation( |
| display_id, rotation, delay_before_rotation, |
| delay_after_rotation) |
| |
| |
| def get_internal_display_id(self): |
| """Gets the internal display id. |
| |
| @return the id of the internal display. |
| """ |
| return self._display_component.get_internal_display_id() |
| |
| |
| def get_first_external_display_id(self): |
| """Gets the first external display id. |
| |
| @return the id of the first external display; -1 if not found. |
| """ |
| return self._display_component.get_first_external_display_id() |
| |
| |
| def reset_connector_if_applicable(self, connector_type): |
| """Resets Type-C video connector from host end if applicable. |
| |
| It's the workaround sequence since sometimes Type-C dongle becomes |
| corrupted and needs to be re-plugged. |
| |
| @param connector_type: A string, like "VGA", "DVI", "HDMI", or "DP". |
| """ |
| return self._display_component.reset_connector_if_applicable( |
| connector_type) |