Save errno for logging before potentially overwriting it.
[chromium-blink-merge.git] / chromeos / display / real_output_configurator_delegate.cc
blob5b0804a7e9e590667493d7b63e0e4ab6e835651d
1 // Copyright (c) 2013 The Chromium 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.
5 #include "chromeos/display/real_output_configurator_delegate.h"
7 #include <X11/Xatom.h>
8 #include <X11/Xlib.h>
9 #include <X11/extensions/dpms.h>
10 #include <X11/extensions/XInput.h>
11 #include <X11/extensions/XInput2.h>
12 #include <X11/extensions/Xrandr.h>
14 #include <cmath>
16 #include "base/logging.h"
17 #include "base/message_loop/message_pump_aurax11.h"
18 #include "chromeos/dbus/dbus_thread_manager.h"
19 #include "chromeos/dbus/power_manager_client.h"
20 #include "chromeos/display/output_util.h"
22 namespace chromeos {
24 namespace {
26 // DPI measurements.
27 const float kMmInInch = 25.4;
28 const float kDpi96 = 96.0;
29 const float kPixelsToMmScale = kMmInInch / kDpi96;
31 bool IsInternalOutput(const XRROutputInfo* output_info) {
32 return IsInternalOutputName(std::string(output_info->name));
35 RRMode GetOutputNativeMode(const XRROutputInfo* output_info) {
36 return output_info->nmode > 0 ? output_info->modes[0] : None;
39 } // namespace
41 RealOutputConfiguratorDelegate::RealOutputConfiguratorDelegate()
42 : display_(base::MessagePumpAuraX11::GetDefaultXDisplay()),
43 window_(DefaultRootWindow(display_)),
44 screen_(NULL),
45 is_panel_fitting_enabled_(false) {
48 RealOutputConfiguratorDelegate::~RealOutputConfiguratorDelegate() {
51 void RealOutputConfiguratorDelegate::SetPanelFittingEnabled(bool enabled) {
52 is_panel_fitting_enabled_ = enabled;
55 void RealOutputConfiguratorDelegate::InitXRandRExtension(int* event_base) {
56 int error_base_ignored = 0;
57 XRRQueryExtension(display_, event_base, &error_base_ignored);
60 void RealOutputConfiguratorDelegate::UpdateXRandRConfiguration(
61 const base::NativeEvent& event) {
62 XRRUpdateConfiguration(event);
65 void RealOutputConfiguratorDelegate::GrabServer() {
66 CHECK(!screen_) << "Server already grabbed";
67 XGrabServer(display_);
68 screen_ = XRRGetScreenResources(display_, window_);
69 CHECK(screen_);
72 void RealOutputConfiguratorDelegate::UngrabServer() {
73 CHECK(screen_) << "Server not grabbed";
74 XRRFreeScreenResources(screen_);
75 screen_ = NULL;
76 XUngrabServer(display_);
79 void RealOutputConfiguratorDelegate::SyncWithServer() {
80 XSync(display_, 0);
83 void RealOutputConfiguratorDelegate::SetBackgroundColor(uint32 color_argb) {
84 // Configuring CRTCs/Framebuffer clears the boot screen image. Set the
85 // same background color while configuring the display to minimize the
86 // duration of black screen at boot time. The background is filled with
87 // black later in ash::DisplayManager. crbug.com/171050.
88 XSetWindowAttributes swa = {0};
89 XColor color;
90 Colormap colormap = DefaultColormap(display_, 0);
91 // XColor uses 16 bits per color.
92 color.red = (color_argb & 0x00FF0000) >> 8;
93 color.green = (color_argb & 0x0000FF00);
94 color.blue = (color_argb & 0x000000FF) << 8;
95 color.flags = DoRed | DoGreen | DoBlue;
96 XAllocColor(display_, colormap, &color);
97 swa.background_pixel = color.pixel;
98 XChangeWindowAttributes(display_, window_, CWBackPixel, &swa);
99 XFreeColors(display_, colormap, &color.pixel, 1, 0);
102 void RealOutputConfiguratorDelegate::ForceDPMSOn() {
103 CHECK(DPMSEnable(display_));
104 CHECK(DPMSForceLevel(display_, DPMSModeOn));
107 std::vector<OutputConfigurator::OutputSnapshot>
108 RealOutputConfiguratorDelegate::GetOutputs() {
109 CHECK(screen_) << "Server not grabbed";
111 std::vector<OutputConfigurator::OutputSnapshot> outputs;
112 XRROutputInfo* one_info = NULL;
113 XRROutputInfo* two_info = NULL;
114 RRCrtc last_used_crtc = None;
116 for (int i = 0; i < screen_->noutput && outputs.size() < 2; ++i) {
117 RROutput this_id = screen_->outputs[i];
118 XRROutputInfo* output_info = XRRGetOutputInfo(display_, screen_, this_id);
119 bool is_connected = (output_info->connection == RR_Connected);
121 if (is_connected) {
122 OutputConfigurator::OutputSnapshot to_populate;
123 to_populate.output = this_id;
124 to_populate.has_display_id =
125 GetDisplayId(this_id, i, &to_populate.display_id);
126 to_populate.is_internal = IsInternalOutput(output_info);
127 // Use the index as a valid display id even if the internal
128 // display doesn't have valid EDID because the index
129 // will never change.
130 if (!to_populate.has_display_id && to_populate.is_internal)
131 to_populate.has_display_id = true;
133 (outputs.empty() ? one_info : two_info) = output_info;
135 // Now, look up the current CRTC and any related info.
136 if (output_info->crtc) {
137 XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(
138 display_, screen_, output_info->crtc);
139 to_populate.current_mode = crtc_info->mode;
140 to_populate.height = crtc_info->height;
141 to_populate.y = crtc_info->y;
142 XRRFreeCrtcInfo(crtc_info);
145 // Assign a CRTC that isn't already in use.
146 for (int j = 0; j < output_info->ncrtc; ++j) {
147 if (output_info->crtcs[j] != last_used_crtc) {
148 to_populate.crtc = output_info->crtcs[j];
149 last_used_crtc = to_populate.crtc;
150 break;
154 to_populate.native_mode = GetOutputNativeMode(output_info);
155 to_populate.is_aspect_preserving_scaling =
156 IsOutputAspectPreservingScaling(this_id);
157 to_populate.touch_device_id = None;
159 VLOG(2) << "Found display " << outputs.size() << ":"
160 << " output=" << to_populate.output
161 << " crtc=" << to_populate.crtc
162 << " current_mode=" << to_populate.current_mode;
163 outputs.push_back(to_populate);
164 } else {
165 XRRFreeOutputInfo(output_info);
169 if (outputs.size() == 2) {
170 bool one_is_internal = IsInternalOutput(one_info);
171 bool two_is_internal = IsInternalOutput(two_info);
172 int internal_outputs = (one_is_internal ? 1 : 0) +
173 (two_is_internal ? 1 : 0);
174 DCHECK_LT(internal_outputs, 2);
175 LOG_IF(WARNING, internal_outputs == 2)
176 << "Two internal outputs detected.";
178 bool can_mirror = false;
179 for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) {
180 // Try preserving external output's aspect ratio on the first attempt.
181 // If that fails, fall back to the highest matching resolution.
182 bool preserve_aspect = attempt == 0;
184 if (internal_outputs == 1) {
185 if (one_is_internal) {
186 can_mirror = FindOrCreateMirrorMode(one_info, two_info,
187 outputs[0].output, is_panel_fitting_enabled_, preserve_aspect,
188 &outputs[0].mirror_mode, &outputs[1].mirror_mode);
189 } else { // if (two_is_internal)
190 can_mirror = FindOrCreateMirrorMode(two_info, one_info,
191 outputs[1].output, is_panel_fitting_enabled_, preserve_aspect,
192 &outputs[1].mirror_mode, &outputs[0].mirror_mode);
194 } else { // if (internal_outputs == 0)
195 // No panel fitting for external outputs, so fall back to exact match.
196 can_mirror = FindOrCreateMirrorMode(one_info, two_info,
197 outputs[0].output, false, preserve_aspect,
198 &outputs[0].mirror_mode, &outputs[1].mirror_mode);
199 if (!can_mirror && preserve_aspect) {
200 // FindOrCreateMirrorMode will try to preserve aspect ratio of
201 // what it thinks is external display, so if it didn't succeed
202 // with one, maybe it will succeed with the other. This way we
203 // will have correct aspect ratio on at least one of them.
204 can_mirror = FindOrCreateMirrorMode(two_info, one_info,
205 outputs[1].output, false, preserve_aspect,
206 &outputs[1].mirror_mode, &outputs[0].mirror_mode);
212 GetTouchscreens(&outputs);
213 XRRFreeOutputInfo(one_info);
214 XRRFreeOutputInfo(two_info);
215 return outputs;
218 bool RealOutputConfiguratorDelegate::GetModeDetails(RRMode mode,
219 int* width,
220 int* height,
221 bool* interlaced) {
222 CHECK(screen_) << "Server not grabbed";
223 // TODO: Determine if we need to organize modes in a way which provides
224 // better than O(n) lookup time. In many call sites, for example, the
225 // "next" mode is typically what we are looking for so using this
226 // helper might be too expensive.
227 for (int i = 0; i < screen_->nmode; ++i) {
228 if (mode == screen_->modes[i].id) {
229 const XRRModeInfo& info = screen_->modes[i];
230 if (width)
231 *width = info.width;
232 if (height)
233 *height = info.height;
234 if (interlaced)
235 *interlaced = info.modeFlags & RR_Interlace;
236 return true;
239 return false;
242 void RealOutputConfiguratorDelegate::ConfigureCrtc(
243 OutputConfigurator::CrtcConfig* config) {
244 CHECK(screen_) << "Server not grabbed";
245 VLOG(1) << "ConfigureCrtc: crtc=" << config->crtc
246 << " mode=" << config->mode
247 << " output=" << config->output
248 << " x=" << config->x
249 << " y=" << config->y;
251 RROutput* outputs = NULL;
252 int num_outputs = 0;
253 if (config->output && config->mode) {
254 outputs = &config->output;
255 num_outputs = 1;
258 XRRSetCrtcConfig(display_,
259 screen_,
260 config->crtc,
261 CurrentTime,
262 config->x,
263 config->y,
264 config->mode,
265 RR_Rotate_0,
266 outputs,
267 num_outputs);
270 void RealOutputConfiguratorDelegate::CreateFrameBuffer(
271 int width,
272 int height,
273 const std::vector<OutputConfigurator::CrtcConfig>& configs) {
274 CHECK(screen_) << "Server not grabbed";
275 int current_width = DisplayWidth(display_, DefaultScreen(display_));
276 int current_height = DisplayHeight(display_, DefaultScreen(display_));
277 VLOG(1) << "CreateFrameBuffer: new=" << width << "x" << height
278 << " current=" << current_width << "x" << current_height;
279 if (width == current_width && height == current_height)
280 return;
282 DestroyUnusedCrtcs(configs);
283 int mm_width = width * kPixelsToMmScale;
284 int mm_height = height * kPixelsToMmScale;
285 XRRSetScreenSize(display_, window_, width, height, mm_width, mm_height);
288 void RealOutputConfiguratorDelegate::ConfigureCTM(
289 int touch_device_id,
290 const OutputConfigurator::CoordinateTransformation& ctm) {
291 VLOG(1) << "ConfigureCTM: id=" << touch_device_id
292 << " scale=" << ctm.x_scale << "x" << ctm.y_scale
293 << " offset=(" << ctm.x_offset << ", " << ctm.y_offset << ")";
294 int ndevices = 0;
295 XIDeviceInfo* info = XIQueryDevice(display_, touch_device_id, &ndevices);
296 Atom prop = XInternAtom(display_, "Coordinate Transformation Matrix", False);
297 Atom float_atom = XInternAtom(display_, "FLOAT", False);
298 if (ndevices == 1 && prop != None && float_atom != None) {
299 Atom type;
300 int format;
301 unsigned long num_items;
302 unsigned long bytes_after;
303 unsigned char* data = NULL;
304 // Verify that the property exists with correct format, type, etc.
305 int status = XIGetProperty(display_, info->deviceid, prop, 0, 0, False,
306 AnyPropertyType, &type, &format, &num_items, &bytes_after, &data);
307 if (data)
308 XFree(data);
309 if (status == Success && type == float_atom && format == 32) {
310 float value[3][3] = {
311 { ctm.x_scale, 0.0, ctm.x_offset },
312 { 0.0, ctm.y_scale, ctm.y_offset },
313 { 0.0, 0.0, 1.0 }
315 XIChangeProperty(display_,
316 info->deviceid,
317 prop,
318 type,
319 format,
320 PropModeReplace,
321 reinterpret_cast<unsigned char*>(value),
325 XIFreeDeviceInfo(info);
328 void RealOutputConfiguratorDelegate::SendProjectingStateToPowerManager(
329 bool projecting) {
330 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
331 SetIsProjecting(projecting);
334 void RealOutputConfiguratorDelegate::DestroyUnusedCrtcs(
335 const std::vector<OutputConfigurator::CrtcConfig>& configs) {
336 CHECK(screen_) << "Server not grabbed";
337 // Setting the screen size will fail if any CRTC doesn't fit afterwards.
338 // At the same time, turning CRTCs off and back on uses up a lot of time.
339 // This function tries to be smart to avoid too many off/on cycles:
340 // - We disable all the CRTCs we won't need after the FB resize.
341 // - We set the new modes on CRTCs, if they fit in both the old and new
342 // FBs, and park them at (0,0)
343 // - We disable the CRTCs we will need but don't fit in the old FB. Those
344 // will be reenabled after the resize.
345 // We don't worry about the cached state of the outputs here since we are
346 // not interested in the state we are setting - we just try to get the CRTCs
347 // out of the way so we can rebuild the frame buffer.
348 for (int i = 0; i < screen_->ncrtc; ++i) {
349 // Default config is to disable the crtcs.
350 OutputConfigurator::CrtcConfig config(
351 screen_->crtcs[i], 0, 0, None, None);
352 for (std::vector<OutputConfigurator::CrtcConfig>::const_iterator it =
353 configs.begin(); it != configs.end(); ++it) {
354 if (config.crtc == it->crtc) {
355 config.mode = it->mode;
356 config.output = it->output;
357 break;
361 if (config.mode != None) {
362 // In case our CRTC doesn't fit in our current framebuffer, disable it.
363 // It'll get reenabled after we resize the framebuffer.
364 int mode_width = 0, mode_height = 0;
365 CHECK(GetModeDetails(config.mode, &mode_width, &mode_height, NULL));
366 int current_width = DisplayWidth(display_, DefaultScreen(display_));
367 int current_height = DisplayHeight(display_, DefaultScreen(display_));
368 if (mode_width > current_width || mode_height > current_height) {
369 config.mode = None;
370 config.output = None;
374 ConfigureCrtc(&config);
378 bool RealOutputConfiguratorDelegate::IsOutputAspectPreservingScaling(
379 RROutput id) {
380 bool ret = false;
382 Atom scaling_prop = XInternAtom(display_, "scaling mode", False);
383 Atom full_aspect_atom = XInternAtom(display_, "Full aspect", False);
384 if (scaling_prop == None || full_aspect_atom == None)
385 return false;
387 int nprop = 0;
388 Atom* props = XRRListOutputProperties(display_, id, &nprop);
389 for (int j = 0; j < nprop && !ret; j++) {
390 Atom prop = props[j];
391 if (scaling_prop == prop) {
392 unsigned char* values = NULL;
393 int actual_format;
394 unsigned long nitems;
395 unsigned long bytes_after;
396 Atom actual_type;
397 int success;
399 success = XRRGetOutputProperty(display_, id, prop, 0, 100, False, False,
400 AnyPropertyType, &actual_type, &actual_format, &nitems,
401 &bytes_after, &values);
402 if (success == Success && actual_type == XA_ATOM &&
403 actual_format == 32 && nitems == 1) {
404 Atom value = reinterpret_cast<Atom*>(values)[0];
405 if (full_aspect_atom == value)
406 ret = true;
408 if (values)
409 XFree(values);
412 if (props)
413 XFree(props);
415 return ret;
418 bool RealOutputConfiguratorDelegate::FindOrCreateMirrorMode(
419 XRROutputInfo* internal_info,
420 XRROutputInfo* external_info,
421 RROutput internal_output_id,
422 bool try_creating,
423 bool preserve_aspect,
424 RRMode* internal_mirror_mode,
425 RRMode* external_mirror_mode) {
426 RRMode internal_mode_id = GetOutputNativeMode(internal_info);
427 RRMode external_mode_id = GetOutputNativeMode(external_info);
429 if (internal_mode_id == None || external_mode_id == None)
430 return false;
432 int internal_native_width = 0, internal_native_height = 0;
433 int external_native_width = 0, external_native_height = 0;
434 CHECK(GetModeDetails(internal_mode_id, &internal_native_width,
435 &internal_native_height, NULL));
436 CHECK(GetModeDetails(external_mode_id, &external_native_width,
437 &external_native_height, NULL));
439 // Check if some external output resolution can be mirrored on internal.
440 // Prefer the modes in the order that X sorts them,
441 // assuming this is the order in which they look better on the monitor.
442 // If X's order is not satisfactory, we can either fix X's sorting,
443 // or implement our sorting here.
444 for (int i = 0; i < external_info->nmode; i++) {
445 external_mode_id = external_info->modes[i];
446 int external_width = 0, external_height = 0;
447 bool is_external_interlaced = false;
448 CHECK(GetModeDetails(external_mode_id, &external_width, &external_height,
449 &is_external_interlaced));
450 bool is_native_aspect_ratio =
451 external_native_width * external_height ==
452 external_native_height * external_width;
453 if (preserve_aspect && !is_native_aspect_ratio)
454 continue; // Allow only aspect ratio preserving modes for mirroring
456 // Try finding exact match
457 for (int j = 0; j < internal_info->nmode; j++) {
458 internal_mode_id = internal_info->modes[j];
459 int internal_width = 0, internal_height = 0;
460 bool is_internal_interlaced = false;
461 CHECK(GetModeDetails(internal_mode_id, &internal_width,
462 &internal_height, &is_internal_interlaced));
463 if (internal_width == external_width &&
464 internal_height == external_height &&
465 is_internal_interlaced == is_external_interlaced) {
466 *internal_mirror_mode = internal_mode_id;
467 *external_mirror_mode = external_mode_id;
468 return true; // Mirror mode found
472 // Try to create a matching internal output mode by panel fitting
473 if (try_creating) {
474 // We can downscale by 1.125, and upscale indefinitely
475 // Downscaling looks ugly, so, can fit == can upscale
476 // Also, internal panels don't support fitting interlaced modes
477 bool can_fit =
478 internal_native_width >= external_width &&
479 internal_native_height >= external_height &&
480 !is_external_interlaced;
481 if (can_fit) {
482 XRRAddOutputMode(display_, internal_output_id, external_mode_id);
483 *internal_mirror_mode = *external_mirror_mode = external_mode_id;
484 return true; // Mirror mode created
489 return false;
492 void RealOutputConfiguratorDelegate::GetTouchscreens(
493 std::vector<OutputConfigurator::OutputSnapshot>* outputs) {
494 int ndevices = 0;
495 Atom valuator_x = XInternAtom(display_, "Abs MT Position X", False);
496 Atom valuator_y = XInternAtom(display_, "Abs MT Position Y", False);
497 if (valuator_x == None || valuator_y == None)
498 return;
500 XIDeviceInfo* info = XIQueryDevice(display_, XIAllDevices, &ndevices);
501 for (int i = 0; i < ndevices; i++) {
502 if (!info[i].enabled || info[i].use != XIFloatingSlave)
503 continue; // Assume all touchscreens are floating slaves
505 double width = -1.0;
506 double height = -1.0;
507 bool is_direct_touch = false;
509 for (int j = 0; j < info[i].num_classes; j++) {
510 XIAnyClassInfo* class_info = info[i].classes[j];
512 if (class_info->type == XIValuatorClass) {
513 XIValuatorClassInfo* valuator_info =
514 reinterpret_cast<XIValuatorClassInfo*>(class_info);
516 if (valuator_x == valuator_info->label) {
517 // Ignore X axis valuator with unexpected properties
518 if (valuator_info->number == 0 && valuator_info->mode == Absolute &&
519 valuator_info->min == 0.0) {
520 width = valuator_info->max;
522 } else if (valuator_y == valuator_info->label) {
523 // Ignore Y axis valuator with unexpected properties
524 if (valuator_info->number == 1 && valuator_info->mode == Absolute &&
525 valuator_info->min == 0.0) {
526 height = valuator_info->max;
530 #if defined(USE_XI2_MT)
531 if (class_info->type == XITouchClass) {
532 XITouchClassInfo* touch_info =
533 reinterpret_cast<XITouchClassInfo*>(class_info);
534 is_direct_touch = touch_info->mode == XIDirectTouch;
536 #endif
539 // Touchscreens should have absolute X and Y axes,
540 // and be direct touch devices.
541 if (width > 0.0 && height > 0.0 && is_direct_touch) {
542 size_t k = 0;
543 for (; k < outputs->size(); k++) {
544 if ((*outputs)[k].native_mode == None ||
545 (*outputs)[k].touch_device_id != None)
546 continue;
547 int native_mode_width = 0, native_mode_height = 0;
548 if (!GetModeDetails((*outputs)[k].native_mode, &native_mode_width,
549 &native_mode_height, NULL))
550 continue;
552 // Allow 1 pixel difference between screen and touchscreen
553 // resolutions. Because in some cases for monitor resolution
554 // 1024x768 touchscreen's resolution would be 1024x768, but for
555 // some 1023x767. It really depends on touchscreen's firmware
556 // configuration.
557 if (std::abs(native_mode_width - width) <= 1.0 &&
558 std::abs(native_mode_height - height) <= 1.0) {
559 (*outputs)[k].touch_device_id = info[i].deviceid;
561 VLOG(2) << "Found touchscreen for output #" << k
562 << " id " << (*outputs)[k].touch_device_id
563 << " width " << width
564 << " height " << height;
565 break;
569 VLOG_IF(2, k == outputs->size())
570 << "No matching output - ignoring touchscreen"
571 << " id " << info[i].deviceid
572 << " width " << width
573 << " height " << height;
577 XIFreeDeviceInfo(info);
580 } // namespace chromeos