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 import sys_power
13from autotest_lib.client.cros.multimedia import display_facade_native
14from autotest_lib.client.cros.multimedia import facade_resource
15from autotest_lib.client.cros.multimedia.display_info import DisplayInfo
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_index):
55        """Moves the current window to the indicated display.
56
57        @param display_index: The index of the indicated display.
58        """
59        self._display_component.move_to_display(display_index)
60
61
62    def set_fullscreen(self, is_fullscreen):
63        """Sets the current window to full screen.
64
65        @param is_fullscreen: True or False to indicate fullscreen state.
66        @return True if success, False otherwise.
67        """
68        return self._display_component.set_fullscreen(is_fullscreen)
69
70
71    def load_url(self, url):
72        """Loads the given url in a new tab. The new tab will be active.
73
74        @param url: The url to load as a string.
75        @return a str, the tab descriptor of the opened tab.
76        """
77        return self._display_component.load_url(url)
78
79
80    def load_calibration_image(self, resolution):
81        """Load a full screen calibration image from the HTTP server.
82
83        @param resolution: A tuple (width, height) of resolution.
84        @return a str, the tab descriptor of the opened tab.
85        """
86        return self._display_component.load_calibration_image(resolution)
87
88
89    def load_color_sequence(self, tab_descriptor, color_sequence):
90        """Displays a series of colors on full screen on the tab.
91        tab_descriptor is returned by any open tab API of display facade.
92        e.g.,
93        tab_descriptor = load_url('about:blank')
94        load_color_sequence(tab_descriptor, color)
95
96        @param tab_descriptor: Indicate which tab to test.
97        @param color_sequence: An integer list for switching colors.
98        @return A list of the timestamp for each switch.
99        """
100        return self._display_component.load_color_sequence(tab_descriptor,
101                                                           color_sequence)
102
103
104    def close_tab(self, tab_descriptor):
105        """Disables fullscreen and closes the tab of the given tab descriptor.
106        tab_descriptor is returned by any open tab API of display facade.
107        e.g.,
108        1.
109        tab_descriptor = load_url(url)
110        close_tab(tab_descriptor)
111
112        2.
113        tab_descriptor = load_calibration_image(resolution)
114        close_tab(tab_descriptor)
115
116        @param tab_descriptor: Indicate which tab to close.
117        """
118        self._display_component.close_tab(tab_descriptor)
119
120
121    def is_mirrored_enabled(self):
122        """Checks the mirrored state.
123
124        @return True if mirrored mode is enabled.
125        """
126        return self._display_component.is_mirrored_enabled()
127
128
129    def set_mirrored(self, is_mirrored):
130        """Sets mirrored mode.
131
132        @param is_mirrored: True or False to indicate mirrored state.
133        @throws error.TestError when the call fails.
134        """
135        if not self._display_component.set_mirrored(is_mirrored):
136            raise error.TestError('Failed to set_mirrored(%s)' % is_mirrored)
137
138
139    def is_display_primary(self, internal=True):
140        """Checks if internal screen is primary display.
141
142        @param internal: is internal/external screen primary status requested
143        @return boolean True if internal display is primary.
144        """
145        return self._display_component.is_display_primary(internal)
146
147
148    def suspend_resume(self, suspend_time=10):
149        """Suspends the DUT for a given time in second.
150
151        @param suspend_time: Suspend time in second.
152        """
153        try:
154            self._display_component.suspend_resume(suspend_time)
155        except sys_power.SpuriousWakeupError as e:
156            # Log suspend/resume errors but continue the test.
157            logging.error('suspend_resume error: %s', str(e))
158
159
160    def suspend_resume_bg(self, suspend_time=10):
161        """Suspends the DUT for a given time in second in the background.
162
163        @param suspend_time: Suspend time in second, default: 10s.
164        """
165        self._display_component.suspend_resume_bg(suspend_time)
166
167
168    def wait_external_display_connected(self, display):
169        """Waits for the specified display to be connected.
170
171        @param display: The display name as a string, like 'HDMI1', or
172                        False if no external display is expected.
173        @return: True if display is connected; False otherwise.
174        """
175        return self._display_component.wait_external_display_connected(display)
176
177
178    def hide_cursor(self):
179        """Hides mouse cursor by sending a keystroke."""
180        self._display_component.hide_cursor()
181
182
183    def hide_typing_cursor(self):
184        """Hides typing cursor by moving outside typing bar."""
185        self._display_component.hide_typing_cursor()
186
187
188    def set_content_protection(self, state):
189        """Sets the content protection of the external screen.
190
191        @param state: One of the states 'Undesired', 'Desired', or 'Enabled'
192        """
193        self._display_component.set_content_protection(state)
194
195
196    def get_content_protection(self):
197        """Gets the state of the content protection.
198
199        @param output: The output name as a string.
200        @return: A string of the state, like 'Undesired', 'Desired', or 'Enabled'.
201                 False if not supported.
202        """
203        return self._display_component.get_content_protection()
204
205
206    def _take_screenshot(self, screenshot_func):
207        """Gets screenshot from frame buffer.
208
209        @param screenshot_func: function to take a screenshot and save the image
210                to specified path. Usage: screenshot_func(path).
211
212        @return: An Image object.
213                 Notice that the returned image may not be in RGB format,
214                 depending on PIL implementation.
215        """
216        with tempfile.NamedTemporaryFile(suffix='.png') as f:
217            screenshot_func(f.name)
218            return Image.open(f.name)
219
220
221    def capture_internal_screen(self):
222        """Captures the internal screen framebuffer.
223
224        @return: An Image object.
225        """
226        screenshot_func = self._display_component.take_internal_screenshot
227        return self._take_screenshot(screenshot_func)
228
229
230    # TODO(ihf): This function needs to be fixed for multiple screens.
231    def capture_external_screen(self):
232        """Captures the external screen framebuffer.
233
234        @return: An Image object.
235        """
236        screenshot_func = self._display_component.take_external_screenshot
237        return self._take_screenshot(screenshot_func)
238
239
240    def get_external_resolution(self):
241        """Gets the resolution of the external screen.
242
243        @return The resolution tuple (width, height) or None if no external
244                display is connected.
245        """
246        resolution = self._display_component.get_external_resolution()
247        return tuple(resolution) if resolution else None
248
249
250    def get_internal_resolution(self):
251        """Gets the resolution of the internal screen.
252
253        @return The resolution tuple (width, height) or None if no internal
254                display.
255        """
256        resolution = self._display_component.get_internal_resolution()
257        return tuple(resolution) if resolution else None
258
259
260    def set_resolution(self, display_index, width, height):
261        """Sets the resolution on the specified display.
262
263        @param display_index: index of the display to set resolutions for.
264        @param width: width of the resolution
265        @param height: height of the resolution
266        """
267        self._display_component.set_resolution(display_index, width, height)
268
269
270    def get_display_info(self):
271        """Gets the information of all the displays that are connected to the
272                DUT.
273
274        @return: list of object DisplayInfo for display informtion
275        """
276        return map(DisplayInfo, self._display_component.get_display_info())
277
278
279    def get_display_modes(self, display_index):
280        """Gets the display modes of the specified display.
281
282        @param display_index: index of the display to get modes from; the index
283            is from the DisplayInfo list obtained by get_display_info().
284
285        @return: list of DisplayMode dicts.
286        """
287        return self._display_component.get_display_modes(display_index)
288
289
290    def get_available_resolutions(self, display_index):
291        """Gets the resolutions from the specified display.
292
293        @param display_index: index of the display to get modes from.
294
295        @return a list of (width, height) tuples.
296        """
297        return [tuple(r) for r in
298                self._display_component.get_available_resolutions(
299                    display_index)]
300
301
302    def get_display_rotation(self, display_index):
303        """Gets the display rotation for the specified display.
304
305        @param display_index: index of the display to get modes from.
306
307        @return: Degree of rotation.
308        """
309        return self._display_component.get_display_rotation(display_index)
310
311
312    def set_display_rotation(self, display_index, rotation,
313                             delay_before_rotation=0, delay_after_rotation=0):
314        """Sets the display rotation for the specified display.
315
316        @param display_index: index of the display to get modes from.
317        @param rotation: degree of rotation
318        @param delay_before_rotation: time in second for delay before rotation
319        @param delay_after_rotation: time in second for delay after rotation
320        """
321        self._display_component.set_display_rotation(
322                display_index, rotation, delay_before_rotation,
323                delay_after_rotation)
324
325
326    def get_first_external_display_index(self):
327        """Gets the first external display index.
328
329        @return the index of the first external display; False if not found.
330        """
331        return self._display_component.get_first_external_display_index()
332
333
334    def reset_connector_if_applicable(self, connector_type):
335        """Resets video connector from host end if applicable.
336
337        This is the workaround method for remote display facade adapter only.
338        Put an empty method here in local adapter to prevent AttributeError of
339        client test.
340
341        @param connector_type: A string, like "VGA", "DVI", "HDMI", or "DP".
342        """
343        pass
344