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 7import logging 8import tempfile 9from PIL import Image 10 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.cros.multimedia import display_facade_native 13from autotest_lib.client.cros.multimedia import facade_resource 14from autotest_lib.client.cros.multimedia.display_info import DisplayInfo 15from autotest_lib.client.cros.power import sys_power 16 17 18class 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