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