Home | History | Annotate | Download | only in display
      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.
      4 
      5 #include "chromeos/display/real_output_configurator_delegate.h"
      6 
      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>
     13 
     14 #include <cmath>
     15 #include <set>
     16 #include <utility>
     17 
     18 #include "base/logging.h"
     19 #include "base/message_loop/message_pump_x11.h"
     20 #include "base/x11/edid_parser_x11.h"
     21 #include "base/x11/x11_error_tracker.h"
     22 #include "chromeos/dbus/dbus_thread_manager.h"
     23 #include "chromeos/dbus/power_manager_client.h"
     24 #include "chromeos/display/output_util.h"
     25 
     26 namespace chromeos {
     27 
     28 namespace {
     29 
     30 // DPI measurements.
     31 const float kMmInInch = 25.4;
     32 const float kDpi96 = 96.0;
     33 const float kPixelsToMmScale = kMmInInch / kDpi96;
     34 
     35 // Prefixes of output name
     36 const char kOutputName_VGA[] = "VGA";
     37 const char kOutputName_HDMI[] = "HDMI";
     38 const char kOutputName_DVI[] = "DVI";
     39 const char kOutputName_DisplayPort[] = "DP";
     40 
     41 const char kContentProtectionAtomName[] = "Content Protection";
     42 const char kProtectionUndesiredAtomName[] = "Undesired";
     43 const char kProtectionDesiredAtomName[] = "Desired";
     44 const char kProtectionEnabledAtomName[] = "Enabled";
     45 
     46 bool IsInternalOutput(const XRROutputInfo* output_info) {
     47   return IsInternalOutputName(std::string(output_info->name));
     48 }
     49 
     50 RRMode GetOutputNativeMode(const XRROutputInfo* output_info) {
     51   return output_info->nmode > 0 ? output_info->modes[0] : None;
     52 }
     53 
     54 }  // namespace
     55 
     56 RealOutputConfiguratorDelegate::RealOutputConfiguratorDelegate()
     57     : display_(base::MessagePumpX11::GetDefaultXDisplay()),
     58       window_(DefaultRootWindow(display_)),
     59       screen_(NULL) {
     60 }
     61 
     62 RealOutputConfiguratorDelegate::~RealOutputConfiguratorDelegate() {
     63 }
     64 
     65 void RealOutputConfiguratorDelegate::InitXRandRExtension(int* event_base) {
     66   int error_base_ignored = 0;
     67   XRRQueryExtension(display_, event_base, &error_base_ignored);
     68 }
     69 
     70 void RealOutputConfiguratorDelegate::UpdateXRandRConfiguration(
     71     const base::NativeEvent& event) {
     72   XRRUpdateConfiguration(event);
     73 }
     74 
     75 void RealOutputConfiguratorDelegate::GrabServer() {
     76   CHECK(!screen_) << "Server already grabbed";
     77   XGrabServer(display_);
     78   screen_ = XRRGetScreenResources(display_, window_);
     79   CHECK(screen_);
     80 }
     81 
     82 void RealOutputConfiguratorDelegate::UngrabServer() {
     83   CHECK(screen_) << "Server not grabbed";
     84   XRRFreeScreenResources(screen_);
     85   screen_ = NULL;
     86   XUngrabServer(display_);
     87 }
     88 
     89 void RealOutputConfiguratorDelegate::SyncWithServer() {
     90   XSync(display_, 0);
     91 }
     92 
     93 void RealOutputConfiguratorDelegate::SetBackgroundColor(uint32 color_argb) {
     94   // Configuring CRTCs/Framebuffer clears the boot screen image.  Set the
     95   // same background color while configuring the display to minimize the
     96   // duration of black screen at boot time. The background is filled with
     97   // black later in ash::DisplayManager.  crbug.com/171050.
     98   XSetWindowAttributes swa = {0};
     99   XColor color;
    100   Colormap colormap = DefaultColormap(display_, 0);
    101   // XColor uses 16 bits per color.
    102   color.red = (color_argb & 0x00FF0000) >> 8;
    103   color.green = (color_argb & 0x0000FF00);
    104   color.blue = (color_argb & 0x000000FF) << 8;
    105   color.flags = DoRed | DoGreen | DoBlue;
    106   XAllocColor(display_, colormap, &color);
    107   swa.background_pixel = color.pixel;
    108   XChangeWindowAttributes(display_, window_, CWBackPixel, &swa);
    109   XFreeColors(display_, colormap, &color.pixel, 1, 0);
    110 }
    111 
    112 void RealOutputConfiguratorDelegate::ForceDPMSOn() {
    113   CHECK(DPMSEnable(display_));
    114   CHECK(DPMSForceLevel(display_, DPMSModeOn));
    115 }
    116 
    117 std::vector<OutputConfigurator::OutputSnapshot>
    118 RealOutputConfiguratorDelegate::GetOutputs() {
    119   CHECK(screen_) << "Server not grabbed";
    120 
    121   std::vector<OutputConfigurator::OutputSnapshot> outputs;
    122   RRCrtc last_used_crtc = None;
    123 
    124   for (int i = 0; i < screen_->noutput && outputs.size() < 2; ++i) {
    125     RROutput output_id = screen_->outputs[i];
    126     XRROutputInfo* output_info = XRRGetOutputInfo(display_, screen_, output_id);
    127     if (output_info->connection == RR_Connected) {
    128       OutputConfigurator::OutputSnapshot output = InitOutputSnapshot(
    129           output_id, output_info, &last_used_crtc, i);
    130       VLOG(2) << "Found display " << outputs.size() << ":"
    131               << " output=" << output.output
    132               << " crtc=" << output.crtc
    133               << " current_mode=" << output.current_mode;
    134       outputs.push_back(output);
    135     }
    136     XRRFreeOutputInfo(output_info);
    137   }
    138 
    139   GetTouchscreens(&outputs);
    140   return outputs;
    141 }
    142 
    143 void RealOutputConfiguratorDelegate::AddOutputMode(RROutput output,
    144                                                    RRMode mode) {
    145   CHECK(screen_) << "Server not grabbed";
    146   VLOG(1) << "AddOutputMode: output=" << output << " mode=" << mode;
    147   XRRAddOutputMode(display_, output, mode);
    148 }
    149 
    150 bool RealOutputConfiguratorDelegate::ConfigureCrtc(
    151     RRCrtc crtc,
    152     RRMode mode,
    153     RROutput output,
    154     int x,
    155     int y) {
    156   CHECK(screen_) << "Server not grabbed";
    157   VLOG(1) << "ConfigureCrtc: crtc=" << crtc
    158           << " mode=" << mode
    159           << " output=" << output
    160           << " x=" << x
    161           << " y=" << y;
    162   // Xrandr.h is full of lies. XRRSetCrtcConfig() is defined as returning a
    163   // Status, which is typically 0 for failure and 1 for success. In
    164   // actuality it returns a RRCONFIGSTATUS, which uses 0 for success.
    165   return XRRSetCrtcConfig(display_,
    166                           screen_,
    167                           crtc,
    168                           CurrentTime,
    169                           x,
    170                           y,
    171                           mode,
    172                           RR_Rotate_0,
    173                           (output && mode) ? &output : NULL,
    174                           (output && mode) ? 1 : 0) == RRSetConfigSuccess;
    175 }
    176 
    177 void RealOutputConfiguratorDelegate::CreateFrameBuffer(
    178     int width,
    179     int height,
    180     const std::vector<OutputConfigurator::OutputSnapshot>& outputs) {
    181   CHECK(screen_) << "Server not grabbed";
    182   int current_width = DisplayWidth(display_, DefaultScreen(display_));
    183   int current_height = DisplayHeight(display_, DefaultScreen(display_));
    184   VLOG(1) << "CreateFrameBuffer: new=" << width << "x" << height
    185           << " current=" << current_width << "x" << current_height;
    186   if (width ==  current_width && height == current_height)
    187     return;
    188 
    189   DestroyUnusedCrtcs(outputs);
    190   int mm_width = width * kPixelsToMmScale;
    191   int mm_height = height * kPixelsToMmScale;
    192   XRRSetScreenSize(display_, window_, width, height, mm_width, mm_height);
    193 }
    194 
    195 void RealOutputConfiguratorDelegate::ConfigureCTM(
    196     int touch_device_id,
    197     const OutputConfigurator::CoordinateTransformation& ctm) {
    198   VLOG(1) << "ConfigureCTM: id=" << touch_device_id
    199           << " scale=" << ctm.x_scale << "x" << ctm.y_scale
    200           << " offset=(" << ctm.x_offset << ", " << ctm.y_offset << ")";
    201   int ndevices = 0;
    202   XIDeviceInfo* info = XIQueryDevice(display_, touch_device_id, &ndevices);
    203   Atom prop = XInternAtom(display_, "Coordinate Transformation Matrix", False);
    204   Atom float_atom = XInternAtom(display_, "FLOAT", False);
    205   if (ndevices == 1 && prop != None && float_atom != None) {
    206     Atom type;
    207     int format;
    208     unsigned long num_items;
    209     unsigned long bytes_after;
    210     unsigned char* data = NULL;
    211     // Verify that the property exists with correct format, type, etc.
    212     int status = XIGetProperty(display_, info->deviceid, prop, 0, 0, False,
    213         AnyPropertyType, &type, &format, &num_items, &bytes_after, &data);
    214     if (data)
    215       XFree(data);
    216     if (status == Success && type == float_atom && format == 32) {
    217       float value[3][3] = {
    218           { ctm.x_scale,         0.0, ctm.x_offset },
    219           {         0.0, ctm.y_scale, ctm.y_offset },
    220           {         0.0,         0.0,          1.0 }
    221       };
    222       XIChangeProperty(display_,
    223                        info->deviceid,
    224                        prop,
    225                        type,
    226                        format,
    227                        PropModeReplace,
    228                        reinterpret_cast<unsigned char*>(value),
    229                        9);
    230     }
    231   }
    232   XIFreeDeviceInfo(info);
    233 }
    234 
    235 void RealOutputConfiguratorDelegate::SendProjectingStateToPowerManager(
    236     bool projecting) {
    237   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
    238       SetIsProjecting(projecting);
    239 }
    240 
    241 bool RealOutputConfiguratorDelegate::InitModeInfo(
    242     RRMode mode,
    243     OutputConfigurator::ModeInfo* mode_info) {
    244   DCHECK(mode_info);
    245   CHECK(screen_) << "Server not grabbed";
    246   // TODO: Determine if we need to organize modes in a way which provides
    247   // better than O(n) lookup time.  In many call sites, for example, the
    248   // "next" mode is typically what we are looking for so using this
    249   // helper might be too expensive.
    250   for (int i = 0; i < screen_->nmode; ++i) {
    251     if (mode == screen_->modes[i].id) {
    252       const XRRModeInfo& info = screen_->modes[i];
    253       mode_info->width = info.width;
    254       mode_info->height = info.height;
    255       mode_info->interlaced = info.modeFlags & RR_Interlace;
    256       if (info.hTotal && info.vTotal) {
    257         mode_info->refresh_rate = static_cast<float>(info.dotClock) /
    258             (static_cast<float>(info.hTotal) *
    259              static_cast<float>(info.vTotal));
    260       } else {
    261         mode_info->refresh_rate = 0.0f;
    262       }
    263       return true;
    264     }
    265   }
    266   return false;
    267 }
    268 
    269 OutputConfigurator::OutputSnapshot
    270 RealOutputConfiguratorDelegate::InitOutputSnapshot(
    271     RROutput id,
    272     XRROutputInfo* info,
    273     RRCrtc* last_used_crtc,
    274     int index) {
    275   OutputConfigurator::OutputSnapshot output;
    276   output.output = id;
    277   output.width_mm = info->mm_width;
    278   output.height_mm = info->mm_height;
    279   output.has_display_id = base::GetDisplayId(id, index, &output.display_id);
    280   output.is_internal = IsInternalOutput(info);
    281   output.index = index;
    282 
    283   // Use the index as a valid display ID even if the internal
    284   // display doesn't have valid EDID because the index
    285   // will never change.
    286   if (!output.has_display_id && output.is_internal)
    287     output.has_display_id = true;
    288 
    289   if (info->crtc) {
    290     XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(display_, screen_, info->crtc);
    291     output.current_mode = crtc_info->mode;
    292     output.x = crtc_info->x;
    293     output.y = crtc_info->y;
    294     XRRFreeCrtcInfo(crtc_info);
    295   }
    296 
    297   // Assign a CRTC that isn't already in use.
    298   for (int i = 0; i < info->ncrtc; ++i) {
    299     if (info->crtcs[i] != *last_used_crtc) {
    300       output.crtc = info->crtcs[i];
    301       *last_used_crtc = output.crtc;
    302       break;
    303     }
    304   }
    305 
    306   output.native_mode = GetOutputNativeMode(info);
    307   output.is_aspect_preserving_scaling = IsOutputAspectPreservingScaling(id);
    308   output.touch_device_id = None;
    309 
    310   for (int i = 0; i < info->nmode; ++i) {
    311     const RRMode mode = info->modes[i];
    312     OutputConfigurator::ModeInfo mode_info;
    313     if (InitModeInfo(mode, &mode_info))
    314       output.mode_infos.insert(std::make_pair(mode, mode_info));
    315     else
    316       LOG(WARNING) << "Unable to find XRRModeInfo for mode " << mode;
    317   }
    318 
    319   std::string name(info->name);
    320   if (output.is_internal) {
    321     output.type = OUTPUT_TYPE_INTERNAL;
    322   } else if (name.find(kOutputName_VGA) == 0) {
    323     output.type = OUTPUT_TYPE_VGA;
    324   } else if (name.find(kOutputName_HDMI) == 0) {
    325     output.type = OUTPUT_TYPE_HDMI;
    326   } else if (name.find(kOutputName_DVI) == 0) {
    327     output.type = OUTPUT_TYPE_DVI;
    328   } else if (name.find(kOutputName_DisplayPort) == 0) {
    329     output.type = OUTPUT_TYPE_DISPLAYPORT;
    330   } else {
    331     LOG(ERROR) << "Unknown link type: " << name;
    332     output.type = OUTPUT_TYPE_UNKNOWN;
    333   }
    334 
    335   return output;
    336 }
    337 
    338 bool RealOutputConfiguratorDelegate::GetHDCPState(RROutput id,
    339                                                   HDCPState* state) {
    340   unsigned char* values = NULL;
    341   int actual_format = 0;
    342   unsigned long nitems = 0;
    343   unsigned long bytes_after = 0;
    344   Atom actual_type = None;
    345   int success = 0;
    346   // TODO(kcwu): Use X11AtomCache to save round trip time of XInternAtom.
    347   Atom prop = XInternAtom(display_, kContentProtectionAtomName, False);
    348 
    349   bool ok = true;
    350   // TODO(kcwu): Move this to x11_util (similar method calls in this file and
    351   // output_util.cc)
    352   success = XRRGetOutputProperty(display_, id, prop, 0, 100, False,
    353                                  False, AnyPropertyType, &actual_type,
    354                                  &actual_format, &nitems, &bytes_after,
    355                                  &values);
    356   if (actual_type == None) {
    357     LOG(ERROR) << "Property '" << kContentProtectionAtomName
    358                << "' does not exist";
    359     ok = false;
    360   } else if (success == Success && actual_type == XA_ATOM &&
    361              actual_format == 32 && nitems == 1) {
    362     Atom value = reinterpret_cast<Atom*>(values)[0];
    363     if (value == XInternAtom(display_, kProtectionUndesiredAtomName, False)) {
    364       *state = HDCP_STATE_UNDESIRED;
    365     } else if (value == XInternAtom(display_, kProtectionDesiredAtomName,
    366                                     False)) {
    367       *state = HDCP_STATE_DESIRED;
    368     } else if (value == XInternAtom(display_, kProtectionEnabledAtomName,
    369                                     False)) {
    370       *state = HDCP_STATE_ENABLED;
    371     } else {
    372       LOG(ERROR) << "Unknown " << kContentProtectionAtomName << " value: "
    373                  << value;
    374       ok = false;
    375     }
    376   } else {
    377     LOG(ERROR) << "XRRGetOutputProperty failed";
    378     ok = false;
    379   }
    380   if (values)
    381     XFree(values);
    382 
    383   VLOG(3) << "HDCP state: " << ok << "," << *state;
    384   return ok;
    385 }
    386 
    387 bool RealOutputConfiguratorDelegate::SetHDCPState(RROutput id,
    388                                                   HDCPState state) {
    389   Atom name = XInternAtom(display_, kContentProtectionAtomName, False);
    390   Atom value = None;
    391   switch (state) {
    392     case HDCP_STATE_UNDESIRED:
    393       value = XInternAtom(display_, kProtectionUndesiredAtomName, False);
    394       break;
    395     case HDCP_STATE_DESIRED:
    396       value = XInternAtom(display_, kProtectionDesiredAtomName, False);
    397       break;
    398     default:
    399       NOTREACHED() << "Invalid HDCP state: " << state;
    400       return false;
    401   }
    402   base::X11ErrorTracker err_tracker;
    403   unsigned char* data = reinterpret_cast<unsigned char*>(&value);
    404   XRRChangeOutputProperty(display_, id, name, XA_ATOM, 32,
    405                           PropModeReplace, data, 1);
    406   if (err_tracker.FoundNewError()) {
    407     LOG(ERROR) << "XRRChangeOutputProperty failed";
    408     return false;
    409   } else {
    410     return true;
    411   }
    412 }
    413 
    414 void RealOutputConfiguratorDelegate::DestroyUnusedCrtcs(
    415     const std::vector<OutputConfigurator::OutputSnapshot>& outputs) {
    416   CHECK(screen_) << "Server not grabbed";
    417   // Setting the screen size will fail if any CRTC doesn't fit afterwards.
    418   // At the same time, turning CRTCs off and back on uses up a lot of time.
    419   // This function tries to be smart to avoid too many off/on cycles:
    420   // - We disable all the CRTCs we won't need after the FB resize.
    421   // - We set the new modes on CRTCs, if they fit in both the old and new
    422   //   FBs, and park them at (0,0)
    423   // - We disable the CRTCs we will need but don't fit in the old FB. Those
    424   //   will be reenabled after the resize.
    425   // We don't worry about the cached state of the outputs here since we are
    426   // not interested in the state we are setting - we just try to get the CRTCs
    427   // out of the way so we can rebuild the frame buffer.
    428   for (int i = 0; i < screen_->ncrtc; ++i) {
    429     // Default config is to disable the crtcs.
    430     RRCrtc crtc = screen_->crtcs[i];
    431     RRMode mode = None;
    432     RROutput output = None;
    433     const OutputConfigurator::ModeInfo* mode_info = NULL;
    434     for (std::vector<OutputConfigurator::OutputSnapshot>::const_iterator it =
    435          outputs.begin(); it != outputs.end(); ++it) {
    436       if (crtc == it->crtc) {
    437         mode = it->current_mode;
    438         output = it->output;
    439         if (mode != None)
    440           mode_info = OutputConfigurator::GetModeInfo(*it, mode);
    441         break;
    442       }
    443     }
    444 
    445     if (mode_info) {
    446       // In case our CRTC doesn't fit in our current framebuffer, disable it.
    447       // It'll get reenabled after we resize the framebuffer.
    448       int current_width = DisplayWidth(display_, DefaultScreen(display_));
    449       int current_height = DisplayHeight(display_, DefaultScreen(display_));
    450       if (mode_info->width > current_width ||
    451           mode_info->height > current_height) {
    452         mode = None;
    453         output = None;
    454         mode_info = NULL;
    455       }
    456     }
    457 
    458     ConfigureCrtc(crtc, mode, output, 0, 0);
    459   }
    460 }
    461 
    462 bool RealOutputConfiguratorDelegate::IsOutputAspectPreservingScaling(
    463     RROutput id) {
    464   bool ret = false;
    465 
    466   Atom scaling_prop = XInternAtom(display_, "scaling mode", False);
    467   Atom full_aspect_atom = XInternAtom(display_, "Full aspect", False);
    468   if (scaling_prop == None || full_aspect_atom == None)
    469     return false;
    470 
    471   int nprop = 0;
    472   Atom* props = XRRListOutputProperties(display_, id, &nprop);
    473   for (int j = 0; j < nprop && !ret; j++) {
    474     Atom prop = props[j];
    475     if (scaling_prop == prop) {
    476       unsigned char* values = NULL;
    477       int actual_format;
    478       unsigned long nitems;
    479       unsigned long bytes_after;
    480       Atom actual_type;
    481       int success;
    482 
    483       success = XRRGetOutputProperty(display_, id, prop, 0, 100, False, False,
    484           AnyPropertyType, &actual_type, &actual_format, &nitems,
    485           &bytes_after, &values);
    486       if (success == Success && actual_type == XA_ATOM &&
    487           actual_format == 32 && nitems == 1) {
    488         Atom value = reinterpret_cast<Atom*>(values)[0];
    489         if (full_aspect_atom == value)
    490           ret = true;
    491       }
    492       if (values)
    493         XFree(values);
    494     }
    495   }
    496   if (props)
    497     XFree(props);
    498 
    499   return ret;
    500 }
    501 
    502 void RealOutputConfiguratorDelegate::GetTouchscreens(
    503     std::vector<OutputConfigurator::OutputSnapshot>* outputs) {
    504   int ndevices = 0;
    505   Atom valuator_x = XInternAtom(display_, "Abs MT Position X", False);
    506   Atom valuator_y = XInternAtom(display_, "Abs MT Position Y", False);
    507   if (valuator_x == None || valuator_y == None)
    508     return;
    509 
    510   std::set<int> no_match_touchscreen;
    511   XIDeviceInfo* info = XIQueryDevice(display_, XIAllDevices, &ndevices);
    512   for (int i = 0; i < ndevices; i++) {
    513     if (!info[i].enabled || info[i].use != XIFloatingSlave)
    514       continue;  // Assume all touchscreens are floating slaves
    515 
    516     double width = -1.0;
    517     double height = -1.0;
    518     bool is_direct_touch = false;
    519 
    520     for (int j = 0; j < info[i].num_classes; j++) {
    521       XIAnyClassInfo* class_info = info[i].classes[j];
    522 
    523       if (class_info->type == XIValuatorClass) {
    524         XIValuatorClassInfo* valuator_info =
    525             reinterpret_cast<XIValuatorClassInfo*>(class_info);
    526 
    527         if (valuator_x == valuator_info->label) {
    528           // Ignore X axis valuator with unexpected properties
    529           if (valuator_info->number == 0 && valuator_info->mode == Absolute &&
    530               valuator_info->min == 0.0) {
    531             width = valuator_info->max;
    532           }
    533         } else if (valuator_y == valuator_info->label) {
    534           // Ignore Y axis valuator with unexpected properties
    535           if (valuator_info->number == 1 && valuator_info->mode == Absolute &&
    536               valuator_info->min == 0.0) {
    537             height = valuator_info->max;
    538           }
    539         }
    540       }
    541 #if defined(USE_XI2_MT)
    542       if (class_info->type == XITouchClass) {
    543         XITouchClassInfo* touch_info =
    544             reinterpret_cast<XITouchClassInfo*>(class_info);
    545         is_direct_touch = touch_info->mode == XIDirectTouch;
    546       }
    547 #endif
    548     }
    549 
    550     // Touchscreens should have absolute X and Y axes,
    551     // and be direct touch devices.
    552     if (width > 0.0 && height > 0.0 && is_direct_touch) {
    553       size_t k = 0;
    554       for (; k < outputs->size(); k++) {
    555         OutputConfigurator::OutputSnapshot* output = &(*outputs)[k];
    556         if (output->native_mode == None || output->touch_device_id != None)
    557           continue;
    558 
    559         const OutputConfigurator::ModeInfo* mode_info =
    560             OutputConfigurator::GetModeInfo(*output, output->native_mode);
    561         if (!mode_info)
    562           continue;
    563 
    564         // Allow 1 pixel difference between screen and touchscreen
    565         // resolutions.  Because in some cases for monitor resolution
    566         // 1024x768 touchscreen's resolution would be 1024x768, but for
    567         // some 1023x767.  It really depends on touchscreen's firmware
    568         // configuration.
    569         if (std::abs(mode_info->width - width) <= 1.0 &&
    570             std::abs(mode_info->height - height) <= 1.0) {
    571           output->touch_device_id = info[i].deviceid;
    572 
    573           VLOG(2) << "Found touchscreen for output #" << k
    574                   << " id " << output->touch_device_id
    575                   << " width " << width
    576                   << " height " << height;
    577           break;
    578         }
    579       }
    580 
    581       if (k == outputs->size()) {
    582         no_match_touchscreen.insert(info[i].deviceid);
    583         VLOG(2) << "No matching output for touchscreen"
    584                 << " id " << info[i].deviceid
    585                 << " width " << width
    586                 << " height " << height;
    587       }
    588 
    589     }
    590   }
    591 
    592   // Sometimes we can't find a matching screen for the touchscreen, e.g.
    593   // due to the touchscreen's reporting range having no correlation with the
    594   // screen's resolution. In this case, we arbitrarily assign unmatched
    595   // touchscreens to unmatched screens.
    596   for (std::set<int>::iterator it = no_match_touchscreen.begin();
    597        it != no_match_touchscreen.end();
    598        it++) {
    599     for (size_t i = 0; i < outputs->size(); i++) {
    600       if ((*outputs)[i].is_internal == false &&
    601           (*outputs)[i].native_mode != None &&
    602           (*outputs)[i].touch_device_id == None ) {
    603         (*outputs)[i].touch_device_id = *it;
    604         VLOG(2) << "Arbitrarily matching touchscreen "
    605                 << (*outputs)[i].touch_device_id << " to output #" << i;
    606         break;
    607       }
    608     }
    609   }
    610 
    611   XIFreeDeviceInfo(info);
    612 }
    613 
    614 }  // namespace chromeos
    615