<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: tdoc_documentcontentfactory.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-17 14:01:55 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_ucb.hxx"

/**************************************************************************
                                TODO
 **************************************************************************

 *************************************************************************/

#include "cppuhelper/factory.hxx"

#include "tdoc_documentcontentfactory.hxx"

using namespace com::sun;
using namespace com::sun::star;

using namespace tdoc_ucp;

//=========================================================================
//=========================================================================
//
// DocumentContentFactory Implementation.
//
//=========================================================================
//=========================================================================

DocumentContentFactory::DocumentContentFactory(
            const uno::Reference< lang::XMultiServiceFactory >& xSMgr )
: m_xSMgr( xSMgr )
{
}

//=========================================================================
// virtual
DocumentContentFactory::~DocumentContentFactory()
{
}

//=========================================================================
//
// XServiceInfo methods.
//
//=========================================================================

// virtual
::rtl::OUString SAL_CALL DocumentContentFactory::getImplementationName()
    throw ( uno::RuntimeException )
{
    return getImplementationName_Static();
}

//=========================================================================
// virtual
sal_Bool SAL_CALL
DocumentContentFactory::supportsService( const ::rtl::OUString& ServiceName )
    throw ( uno::RuntimeException )
{
    uno::Sequence< rtl::OUString > aSNL = getSupportedServiceNames();
    const rtl::OUString * pArray = aSNL.getConstArray();
    for ( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
    {
        if ( pArray[ i ] == ServiceName )
            return sal_True;
    }
    return sal_False;
}

//=========================================================================
// virtual
uno::Sequence< ::rtl::OUString > SAL_CALL
DocumentContentFactory::getSupportedServiceNames()
    throw ( uno::RuntimeException )
{
    return getSupportedServiceNames_Static();
}

//=========================================================================
// static
rtl::OUString DocumentContentFactory::getImplementationName_Static()
{
    return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
        "com.sun.star.comp.ucb.TransientDocumentsDocumentContentFactory" ) );
}

//=========================================================================
// static
uno::Sequence< rtl::OUString >
DocumentContentFactory::getSupportedServiceNames_Static()
{
    uno::Sequence< rtl::OUString > aSNS( 1 );
    aSNS.getArray()[ 0 ]
        = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
            "com.sun.star.frame.TransientDocumentsDocumentContentFactory" ) );
    return aSNS;
}

//=========================================================================
//
// XTransientDocumentsDocumentContentFactory methods.
//
//=========================================================================

// virtual
uno::Reference< star::ucb::XContent > SAL_CALL
DocumentContentFactory::createDocumentContent(
        const uno::Reference< frame::XModel >& Model )
    throw ( lang::IllegalArgumentException, uno::RuntimeException )
{
    uno::Reference< frame::XTransientDocumentsDocumentContentFactory > xDocFac;
    try
    {
        xDocFac
            = uno::Reference< frame::XTransientDocumentsDocumentContentFactory >(
                m_xSMgr->createInstance(
                    rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
                        "com.sun.star.ucb.TransientDocumentsContentProvider" ) )
                    ),
                uno::UNO_QUERY );
    }
    catch ( uno::Exception const & )
    {
        // handled below.
    }

    if ( xDocFac.is() )
        return xDocFac->createDocumentContent( Model );

    throw uno::RuntimeException(
        rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
            "Unable to obtain document content factory!" ) ),
        static_cast< cppu::OWeakObject * >( this ) );
}

//=========================================================================
//
// Service factory implementation.
//
//=========================================================================

static uno::Reference< uno::XInterface > SAL_CALL
DocumentContentFactory_CreateInstance(
    const uno::Reference< lang::XMultiServiceFactory> & rSMgr )
    throw( uno::Exception )
{
    lang::XServiceInfo * pX = static_cast< lang::XServiceInfo * >(
        new DocumentContentFactory( rSMgr ) );
    return uno::Reference< uno::XInterface >::query( pX );
}

//=========================================================================
// static
uno::Reference< lang::XSingleServiceFactory >
DocumentContentFactory::createServiceFactory(
    const uno::Reference< lang::XMultiServiceFactory >& rxServiceMgr )
{
    return uno::Reference< lang::XSingleServiceFactory >(
            cppu::createOneInstanceFactory(
                rxServiceMgr,
                DocumentContentFactory::getImplementationName_Static(),
                DocumentContentFactory_CreateInstance,
                DocumentContentFactory::getSupportedServiceNames_Static() ) );
}

<commit_msg>INTEGRATION: CWS bgdlremove (1.4.40); FILE MERGED 2007/05/18 14:06:51 kso 1.4.40.1: #i77419# - cleanup of ucbhelper namespaces.<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: tdoc_documentcontentfactory.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: ihi $ $Date: 2007-06-05 18:17:14 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_ucb.hxx"

/**************************************************************************
                                TODO
 **************************************************************************

 *************************************************************************/

#include "cppuhelper/factory.hxx"

#include "tdoc_documentcontentfactory.hxx"

using namespace com::sun::star;
using namespace tdoc_ucp;

//=========================================================================
//=========================================================================
//
// DocumentContentFactory Implementation.
//
//=========================================================================
//=========================================================================

DocumentContentFactory::DocumentContentFactory(
            const uno::Reference< lang::XMultiServiceFactory >& xSMgr )
: m_xSMgr( xSMgr )
{
}

//=========================================================================
// virtual
DocumentContentFactory::~DocumentContentFactory()
{
}

//=========================================================================
//
// XServiceInfo methods.
//
//=========================================================================

// virtual
::rtl::OUString SAL_CALL DocumentContentFactory::getImplementationName()
    throw ( uno::RuntimeException )
{
    return getImplementationName_Static();
}

//=========================================================================
// virtual
sal_Bool SAL_CALL
DocumentContentFactory::supportsService( const ::rtl::OUString& ServiceName )
    throw ( uno::RuntimeException )
{
    uno::Sequence< rtl::OUString > aSNL = getSupportedServiceNames();
    const rtl::OUString * pArray = aSNL.getConstArray();
    for ( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
    {
        if ( pArray[ i ] == ServiceName )
            return sal_True;
    }
    return sal_False;
}

//=========================================================================
// virtual
uno::Sequence< ::rtl::OUString > SAL_CALL
DocumentContentFactory::getSupportedServiceNames()
    throw ( uno::RuntimeException )
{
    return getSupportedServiceNames_Static();
}

//=========================================================================
// static
rtl::OUString DocumentContentFactory::getImplementationName_Static()
{
    return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
        "com.sun.star.comp.ucb.TransientDocumentsDocumentContentFactory" ) );
}

//=========================================================================
// static
uno::Sequence< rtl::OUString >
DocumentContentFactory::getSupportedServiceNames_Static()
{
    uno::Sequence< rtl::OUString > aSNS( 1 );
    aSNS.getArray()[ 0 ]
        = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
            "com.sun.star.frame.TransientDocumentsDocumentContentFactory" ) );
    return aSNS;
}

//=========================================================================
//
// XTransientDocumentsDocumentContentFactory methods.
//
//=========================================================================

// virtual
uno::Reference< ucb::XContent > SAL_CALL
DocumentContentFactory::createDocumentContent(
        const uno::Reference< frame::XModel >& Model )
    throw ( lang::IllegalArgumentException, uno::RuntimeException )
{
    uno::Reference< frame::XTransientDocumentsDocumentContentFactory > xDocFac;
    try
    {
        xDocFac
            = uno::Reference< frame::XTransientDocumentsDocumentContentFactory >(
                m_xSMgr->createInstance(
                    rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
                        "com.sun.star.ucb.TransientDocumentsContentProvider" ) )
                    ),
                uno::UNO_QUERY );
    }
    catch ( uno::Exception const & )
    {
        // handled below.
    }

    if ( xDocFac.is() )
        return xDocFac->createDocumentContent( Model );

    throw uno::RuntimeException(
        rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
            "Unable to obtain document content factory!" ) ),
        static_cast< cppu::OWeakObject * >( this ) );
}

//=========================================================================
//
// Service factory implementation.
//
//=========================================================================

static uno::Reference< uno::XInterface > SAL_CALL
DocumentContentFactory_CreateInstance(
    const uno::Reference< lang::XMultiServiceFactory> & rSMgr )
    throw( uno::Exception )
{
    lang::XServiceInfo * pX = static_cast< lang::XServiceInfo * >(
        new DocumentContentFactory( rSMgr ) );
    return uno::Reference< uno::XInterface >::query( pX );
}

//=========================================================================
// static
uno::Reference< lang::XSingleServiceFactory >
DocumentContentFactory::createServiceFactory(
    const uno::Reference< lang::XMultiServiceFactory >& rxServiceMgr )
{
    return uno::Reference< lang::XSingleServiceFactory >(
            cppu::createOneInstanceFactory(
                rxServiceMgr,
                DocumentContentFactory::getImplementationName_Static(),
                DocumentContentFactory_CreateInstance,
                DocumentContentFactory::getSupportedServiceNames_Static() ) );
}

<|endoftext|>


<commit_before>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/events/platform/x11/x11_hotplug_event_handler.h"

#include <X11/Xatom.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>

#include <algorithm>
#include <cmath>
#include <set>
#include <string>
#include <vector>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_util.h"
#include "base/sys_info.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/worker_pool.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/devices/device_hotplug_event_observer.h"
#include "ui/events/devices/device_util_linux.h"
#include "ui/events/devices/input_device.h"
#include "ui/events/devices/keyboard_device.h"
#include "ui/events/devices/touchscreen_device.h"
#include "ui/gfx/x/x11_types.h"

namespace ui {

namespace {

// The name of the xinput device corresponding to the AT internal keyboard.
const char kATKeyboardName[] = "AT Translated Set 2 keyboard";

// The prefix of xinput devices corresponding to CrOS EC internal keyboards.
const char kCrosEcKeyboardPrefix[] = "cros-ec";

const char* kCachedAtomList[] = {
  "Abs MT Position X",
  "Abs MT Position Y",
  NULL,
};

typedef base::Callback<void(const std::vector<KeyboardDevice>&)>
    KeyboardDeviceCallback;

typedef base::Callback<void(const std::vector<TouchscreenDevice>&)>
    TouchscreenDeviceCallback;

// Used for updating the state on the UI thread once device information is
// parsed on helper threads.
struct UiCallbacks {
  KeyboardDeviceCallback keyboard_callback;
  TouchscreenDeviceCallback touchscreen_callback;
};

// Stores a copy of the XIValuatorClassInfo values so X11 device processing can
// happen on a worker thread. This is needed since X11 structs are not copyable.
struct ValuatorClassInfo {
  ValuatorClassInfo(const XIValuatorClassInfo& info)
      : label(info.label),
        max(info.max),
        min(info.min),
        mode(info.mode),
        number(info.number) {}

  Atom label;
  double max;
  double min;
  int mode;
  int number;
};

// Stores a copy of the XITouchClassInfo values so X11 device processing can
// happen on a worker thread. This is needed since X11 structs are not copyable.
struct TouchClassInfo {
  TouchClassInfo(const XITouchClassInfo& info)
      : mode(info.mode) {}

  int mode;
};

struct DeviceInfo {
  DeviceInfo(const XIDeviceInfo& device, const base::FilePath& path)
      : id(device.deviceid),
        name(device.name),
        use(device.use),
        enabled(device.enabled),
        path(path) {
    for (int i = 0; i < device.num_classes; ++i) {
      switch (device.classes[i]->type) {
        case XIValuatorClass:
          valuator_class_infos.push_back(ValuatorClassInfo(
              *reinterpret_cast<XIValuatorClassInfo*>(device.classes[i])));
          break;
        case XITouchClass:
          touch_class_infos.push_back(TouchClassInfo(
              *reinterpret_cast<XITouchClassInfo*>(device.classes[i])));
          break;
        default:
          break;
      }
    }
  }

  // Unique device identifier.
  int id;

  // Internal device name.
  std::string name;

  // Device type (ie: XIMasterPointer)
  int use;

  // Specifies if the device is enabled and can send events.
  bool enabled;

  // Path to the actual device (ie: /dev/input/eventXX)
  base::FilePath path;

  std::vector<ValuatorClassInfo> valuator_class_infos;

  std::vector<TouchClassInfo> touch_class_infos;
};

// X11 display cache used on worker threads. This is filled on the UI thread and
// passed in to the worker threads.
struct DisplayState {
  Atom mt_position_x;
  Atom mt_position_y;
};

// Returns true if |name| is the name of a known keyboard device. Note, this may
// return false negatives.
bool IsKnownKeyboard(const std::string& name) {
  std::string lower = base::StringToLowerASCII(name);
  return lower.find("keyboard") != std::string::npos;
}

// Returns true if |name| is the name of a known internal keyboard device. Note,
// this may return false negatives.
bool IsInternalKeyboard(const std::string& name) {
  // TODO(rsadam@): Come up with a more generic way of identifying internal
  // keyboards. See crbug.com/420728.
  if (name == kATKeyboardName)
    return true;
  return name.compare(
             0u, strlen(kCrosEcKeyboardPrefix), kCrosEcKeyboardPrefix) == 0;
}

// Returns true if |name| is the name of a known XTEST device. Note, this may
// return false negatives.
bool IsTestKeyboard(const std::string& name) {
  return name.find("XTEST") != std::string::npos;
}

base::FilePath GetDevicePath(XDisplay* dpy, int device_id) {
#if !defined(OS_CHROMEOS)
  return base::FilePath();
#else
  if (!base::SysInfo::IsRunningOnChromeOS())
    return base::FilePath();
#endif

  // Input device has a property "Device Node" pointing to its dev input node,
  // e.g.   Device Node (250): "/dev/input/event8"
  Atom device_node = XInternAtom(dpy, "Device Node", False);
  if (device_node == None)
    return base::FilePath();

  Atom actual_type;
  int actual_format;
  unsigned long nitems, bytes_after;
  unsigned char* data;
  XDevice* dev = XOpenDevice(dpy, device_id);
  if (!dev)
    return base::FilePath();

  if (XGetDeviceProperty(dpy,
                         dev,
                         device_node,
                         0,
                         1000,
                         False,
                         AnyPropertyType,
                         &actual_type,
                         &actual_format,
                         &nitems,
                         &bytes_after,
                         &data) != Success) {
    XCloseDevice(dpy, dev);
    return base::FilePath();
  }

  std::string path;
  // Make sure the returned value is a string.
  if (actual_type == XA_STRING && actual_format == 8)
    path = reinterpret_cast<char*>(data);

  XFree(data);
  XCloseDevice(dpy, dev);

  return base::FilePath(path);
}

// Helper used to parse keyboard information. When it is done it uses
// |reply_runner| and |callback| to update the state on the UI thread.
void HandleKeyboardDevicesInWorker(
    const std::vector<DeviceInfo>& device_infos,
    scoped_refptr<base::TaskRunner> reply_runner,
    const KeyboardDeviceCallback& callback) {
  std::vector<KeyboardDevice> devices;

  for (const DeviceInfo& device_info : device_infos) {
    if (!device_info.enabled || device_info.use != XISlaveKeyboard)
      continue;  // Assume all keyboards are keyboard slaves
    std::string device_name(device_info.name);
    base::TrimWhitespaceASCII(device_name, base::TRIM_TRAILING, &device_name);
    if (IsTestKeyboard(device_name))
      continue;  // Skip test devices.
    InputDeviceType type;
    if (IsInternalKeyboard(device_name)) {
      type = InputDeviceType::INPUT_DEVICE_INTERNAL;
    } else if (IsKnownKeyboard(device_name)) {
      type = InputDeviceType::INPUT_DEVICE_EXTERNAL;
    } else {
      type = InputDeviceType::INPUT_DEVICE_UNKNOWN;
    }
    devices.push_back(
        KeyboardDevice(device_info.id, type, device_name));
  }

  reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices));
}

// Helper used to parse touchscreen information. When it is done it uses
// |reply_runner| and |callback| to update the state on the UI thread.
void HandleTouchscreenDevicesInWorker(
    const std::vector<DeviceInfo>& device_infos,
    const DisplayState& display_state,
    scoped_refptr<base::TaskRunner> reply_runner,
    const TouchscreenDeviceCallback& callback) {
  std::vector<TouchscreenDevice> devices;
  if (display_state.mt_position_x == None ||
      display_state.mt_position_y == None)
    return;

  std::set<int> no_match_touchscreen;
  for (const DeviceInfo& device_info : device_infos) {
    if (!device_info.enabled || device_info.use != XIFloatingSlave)
      continue;  // Assume all touchscreens are floating slaves

    double max_x = -1.0;
    double max_y = -1.0;
    bool is_direct_touch = false;

    for (const ValuatorClassInfo& valuator : device_info.valuator_class_infos) {
      if (display_state.mt_position_x == valuator.label) {
        // Ignore X axis valuator with unexpected properties
        if (valuator.number == 0 && valuator.mode == Absolute &&
            valuator.min == 0.0) {
          max_x = valuator.max;
        }
      } else if (display_state.mt_position_y == valuator.label) {
        // Ignore Y axis valuator with unexpected properties
        if (valuator.number == 1 && valuator.mode == Absolute &&
            valuator.min == 0.0) {
          max_y = valuator.max;
        }
      }
    }

    for (const TouchClassInfo& info : device_info.touch_class_infos) {
      is_direct_touch = info.mode == XIDirectTouch;
    }

    // Touchscreens should have absolute X and Y axes, and be direct touch
    // devices.
    if (max_x > 0.0 && max_y > 0.0 && is_direct_touch) {
      InputDeviceType type = IsTouchscreenInternal(device_info.path)
              ? InputDeviceType::INPUT_DEVICE_INTERNAL
              : InputDeviceType::INPUT_DEVICE_EXTERNAL;
      // |max_x| and |max_y| are inclusive values, so we need to add 1 to get
      // the size.
      devices.push_back(TouchscreenDevice(
          device_info.id,
          type,
          device_info.name,
          gfx::Size(max_x + 1, max_y + 1)));
    }
  }

  reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices));
}

// Called on a worker thread to parse the device information.
void HandleHotplugEventInWorker(
    const std::vector<DeviceInfo>& devices,
    const DisplayState& display_state,
    scoped_refptr<base::TaskRunner> reply_runner,
    const UiCallbacks& callbacks) {
  HandleTouchscreenDevicesInWorker(
      devices, display_state, reply_runner, callbacks.touchscreen_callback);
  HandleKeyboardDevicesInWorker(
      devices, reply_runner, callbacks.keyboard_callback);
}

DeviceHotplugEventObserver* GetHotplugEventObserver() {
  return DeviceDataManager::GetInstance();
}

void OnKeyboardDevices(const std::vector<KeyboardDevice>& devices) {
  GetHotplugEventObserver()->OnKeyboardDevicesUpdated(devices);
}

void OnTouchscreenDevices(const std::vector<TouchscreenDevice>& devices) {
  GetHotplugEventObserver()->OnTouchscreenDevicesUpdated(devices);
}

}  // namespace

X11HotplugEventHandler::X11HotplugEventHandler()
    : atom_cache_(gfx::GetXDisplay(), kCachedAtomList) {
}

X11HotplugEventHandler::~X11HotplugEventHandler() {
}

void X11HotplugEventHandler::OnHotplugEvent() {
  const XIDeviceList& device_list =
      DeviceListCacheX11::GetInstance()->GetXI2DeviceList(gfx::GetXDisplay());
  Display* display = gfx::GetXDisplay();

  std::vector<DeviceInfo> device_infos;
  for (int i = 0; i < device_list.count; ++i)
    device_infos.push_back(DeviceInfo(
        device_list[i], GetDevicePath(display, device_list[i].deviceid)));

  // X11 is not thread safe, so first get all the required state.
  DisplayState display_state;
  display_state.mt_position_x = atom_cache_.GetAtom("Abs MT Position X");
  display_state.mt_position_y = atom_cache_.GetAtom("Abs MT Position Y");

  UiCallbacks callbacks;
  callbacks.keyboard_callback = base::Bind(&OnKeyboardDevices);
  callbacks.touchscreen_callback = base::Bind(&OnTouchscreenDevices);

  // Parsing the device information may block, so delegate the operation to a
  // worker thread. Once the device information is extracted the parsed devices
  // will be returned via the callbacks.
  base::WorkerPool::PostTask(FROM_HERE,
                             base::Bind(&HandleHotplugEventInWorker,
                                        device_infos,
                                        display_state,
                                        base::ThreadTaskRunnerHandle::Get(),
                                        callbacks),
                             true /* task_is_slow */);
}

}  // namespace ui
<commit_msg>Avoid X error in X11HotplugEventHandler::OnHotplugEvent().<commit_after>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/events/platform/x11/x11_hotplug_event_handler.h"

#include <X11/Xatom.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>

#include <algorithm>
#include <cmath>
#include <set>
#include <string>
#include <vector>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_util.h"
#include "base/sys_info.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/worker_pool.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/devices/device_hotplug_event_observer.h"
#include "ui/events/devices/device_util_linux.h"
#include "ui/events/devices/input_device.h"
#include "ui/events/devices/keyboard_device.h"
#include "ui/events/devices/touchscreen_device.h"
#include "ui/gfx/x/x11_types.h"

namespace ui {

namespace {

// The name of the xinput device corresponding to the AT internal keyboard.
const char kATKeyboardName[] = "AT Translated Set 2 keyboard";

// The prefix of xinput devices corresponding to CrOS EC internal keyboards.
const char kCrosEcKeyboardPrefix[] = "cros-ec";

const char* kCachedAtomList[] = {
  "Abs MT Position X",
  "Abs MT Position Y",
  NULL,
};

typedef base::Callback<void(const std::vector<KeyboardDevice>&)>
    KeyboardDeviceCallback;

typedef base::Callback<void(const std::vector<TouchscreenDevice>&)>
    TouchscreenDeviceCallback;

// Used for updating the state on the UI thread once device information is
// parsed on helper threads.
struct UiCallbacks {
  KeyboardDeviceCallback keyboard_callback;
  TouchscreenDeviceCallback touchscreen_callback;
};

// Stores a copy of the XIValuatorClassInfo values so X11 device processing can
// happen on a worker thread. This is needed since X11 structs are not copyable.
struct ValuatorClassInfo {
  ValuatorClassInfo(const XIValuatorClassInfo& info)
      : label(info.label),
        max(info.max),
        min(info.min),
        mode(info.mode),
        number(info.number) {}

  Atom label;
  double max;
  double min;
  int mode;
  int number;
};

// Stores a copy of the XITouchClassInfo values so X11 device processing can
// happen on a worker thread. This is needed since X11 structs are not copyable.
struct TouchClassInfo {
  TouchClassInfo(const XITouchClassInfo& info)
      : mode(info.mode) {}

  int mode;
};

struct DeviceInfo {
  DeviceInfo(const XIDeviceInfo& device, const base::FilePath& path)
      : id(device.deviceid),
        name(device.name),
        use(device.use),
        enabled(device.enabled),
        path(path) {
    for (int i = 0; i < device.num_classes; ++i) {
      switch (device.classes[i]->type) {
        case XIValuatorClass:
          valuator_class_infos.push_back(ValuatorClassInfo(
              *reinterpret_cast<XIValuatorClassInfo*>(device.classes[i])));
          break;
        case XITouchClass:
          touch_class_infos.push_back(TouchClassInfo(
              *reinterpret_cast<XITouchClassInfo*>(device.classes[i])));
          break;
        default:
          break;
      }
    }
  }

  // Unique device identifier.
  int id;

  // Internal device name.
  std::string name;

  // Device type (ie: XIMasterPointer)
  int use;

  // Specifies if the device is enabled and can send events.
  bool enabled;

  // Path to the actual device (ie: /dev/input/eventXX)
  base::FilePath path;

  std::vector<ValuatorClassInfo> valuator_class_infos;

  std::vector<TouchClassInfo> touch_class_infos;
};

// X11 display cache used on worker threads. This is filled on the UI thread and
// passed in to the worker threads.
struct DisplayState {
  Atom mt_position_x;
  Atom mt_position_y;
};

// Returns true if |name| is the name of a known keyboard device. Note, this may
// return false negatives.
bool IsKnownKeyboard(const std::string& name) {
  std::string lower = base::StringToLowerASCII(name);
  return lower.find("keyboard") != std::string::npos;
}

// Returns true if |name| is the name of a known internal keyboard device. Note,
// this may return false negatives.
bool IsInternalKeyboard(const std::string& name) {
  // TODO(rsadam@): Come up with a more generic way of identifying internal
  // keyboards. See crbug.com/420728.
  if (name == kATKeyboardName)
    return true;
  return name.compare(
             0u, strlen(kCrosEcKeyboardPrefix), kCrosEcKeyboardPrefix) == 0;
}

// Returns true if |name| is the name of a known XTEST device. Note, this may
// return false negatives.
bool IsTestKeyboard(const std::string& name) {
  return name.find("XTEST") != std::string::npos;
}

base::FilePath GetDevicePath(XDisplay* dpy, const XIDeviceInfo& device) {
#if !defined(OS_CHROMEOS)
  return base::FilePath();
#else
  if (!base::SysInfo::IsRunningOnChromeOS())
    return base::FilePath();
#endif

  // Skip the main pointer and keyboard since XOpenDevice() generates a
  // BadDevice error when passed these devices.
  if (device.use == XIMasterPointer || device.use == XIMasterKeyboard)
    return base::FilePath();

  // Input device has a property "Device Node" pointing to its dev input node,
  // e.g.   Device Node (250): "/dev/input/event8"
  Atom device_node = XInternAtom(dpy, "Device Node", False);
  if (device_node == None)
    return base::FilePath();

  Atom actual_type;
  int actual_format;
  unsigned long nitems, bytes_after;
  unsigned char* data;
  XDevice* dev = XOpenDevice(dpy, device.deviceid);
  if (!dev)
    return base::FilePath();

  if (XGetDeviceProperty(dpy,
                         dev,
                         device_node,
                         0,
                         1000,
                         False,
                         AnyPropertyType,
                         &actual_type,
                         &actual_format,
                         &nitems,
                         &bytes_after,
                         &data) != Success) {
    XCloseDevice(dpy, dev);
    return base::FilePath();
  }

  std::string path;
  // Make sure the returned value is a string.
  if (actual_type == XA_STRING && actual_format == 8)
    path = reinterpret_cast<char*>(data);

  XFree(data);
  XCloseDevice(dpy, dev);

  return base::FilePath(path);
}

// Helper used to parse keyboard information. When it is done it uses
// |reply_runner| and |callback| to update the state on the UI thread.
void HandleKeyboardDevicesInWorker(
    const std::vector<DeviceInfo>& device_infos,
    scoped_refptr<base::TaskRunner> reply_runner,
    const KeyboardDeviceCallback& callback) {
  std::vector<KeyboardDevice> devices;

  for (const DeviceInfo& device_info : device_infos) {
    if (!device_info.enabled || device_info.use != XISlaveKeyboard)
      continue;  // Assume all keyboards are keyboard slaves
    std::string device_name(device_info.name);
    base::TrimWhitespaceASCII(device_name, base::TRIM_TRAILING, &device_name);
    if (IsTestKeyboard(device_name))
      continue;  // Skip test devices.
    InputDeviceType type;
    if (IsInternalKeyboard(device_name)) {
      type = InputDeviceType::INPUT_DEVICE_INTERNAL;
    } else if (IsKnownKeyboard(device_name)) {
      type = InputDeviceType::INPUT_DEVICE_EXTERNAL;
    } else {
      type = InputDeviceType::INPUT_DEVICE_UNKNOWN;
    }
    devices.push_back(
        KeyboardDevice(device_info.id, type, device_name));
  }

  reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices));
}

// Helper used to parse touchscreen information. When it is done it uses
// |reply_runner| and |callback| to update the state on the UI thread.
void HandleTouchscreenDevicesInWorker(
    const std::vector<DeviceInfo>& device_infos,
    const DisplayState& display_state,
    scoped_refptr<base::TaskRunner> reply_runner,
    const TouchscreenDeviceCallback& callback) {
  std::vector<TouchscreenDevice> devices;
  if (display_state.mt_position_x == None ||
      display_state.mt_position_y == None)
    return;

  std::set<int> no_match_touchscreen;
  for (const DeviceInfo& device_info : device_infos) {
    if (!device_info.enabled || device_info.use != XIFloatingSlave)
      continue;  // Assume all touchscreens are floating slaves

    double max_x = -1.0;
    double max_y = -1.0;
    bool is_direct_touch = false;

    for (const ValuatorClassInfo& valuator : device_info.valuator_class_infos) {
      if (display_state.mt_position_x == valuator.label) {
        // Ignore X axis valuator with unexpected properties
        if (valuator.number == 0 && valuator.mode == Absolute &&
            valuator.min == 0.0) {
          max_x = valuator.max;
        }
      } else if (display_state.mt_position_y == valuator.label) {
        // Ignore Y axis valuator with unexpected properties
        if (valuator.number == 1 && valuator.mode == Absolute &&
            valuator.min == 0.0) {
          max_y = valuator.max;
        }
      }
    }

    for (const TouchClassInfo& info : device_info.touch_class_infos) {
      is_direct_touch = info.mode == XIDirectTouch;
    }

    // Touchscreens should have absolute X and Y axes, and be direct touch
    // devices.
    if (max_x > 0.0 && max_y > 0.0 && is_direct_touch) {
      InputDeviceType type = IsTouchscreenInternal(device_info.path)
              ? InputDeviceType::INPUT_DEVICE_INTERNAL
              : InputDeviceType::INPUT_DEVICE_EXTERNAL;
      // |max_x| and |max_y| are inclusive values, so we need to add 1 to get
      // the size.
      devices.push_back(TouchscreenDevice(
          device_info.id,
          type,
          device_info.name,
          gfx::Size(max_x + 1, max_y + 1)));
    }
  }

  reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices));
}

// Called on a worker thread to parse the device information.
void HandleHotplugEventInWorker(
    const std::vector<DeviceInfo>& devices,
    const DisplayState& display_state,
    scoped_refptr<base::TaskRunner> reply_runner,
    const UiCallbacks& callbacks) {
  HandleTouchscreenDevicesInWorker(
      devices, display_state, reply_runner, callbacks.touchscreen_callback);
  HandleKeyboardDevicesInWorker(
      devices, reply_runner, callbacks.keyboard_callback);
}

DeviceHotplugEventObserver* GetHotplugEventObserver() {
  return DeviceDataManager::GetInstance();
}

void OnKeyboardDevices(const std::vector<KeyboardDevice>& devices) {
  GetHotplugEventObserver()->OnKeyboardDevicesUpdated(devices);
}

void OnTouchscreenDevices(const std::vector<TouchscreenDevice>& devices) {
  GetHotplugEventObserver()->OnTouchscreenDevicesUpdated(devices);
}

}  // namespace

X11HotplugEventHandler::X11HotplugEventHandler()
    : atom_cache_(gfx::GetXDisplay(), kCachedAtomList) {
}

X11HotplugEventHandler::~X11HotplugEventHandler() {
}

void X11HotplugEventHandler::OnHotplugEvent() {
  const XIDeviceList& device_list =
      DeviceListCacheX11::GetInstance()->GetXI2DeviceList(gfx::GetXDisplay());
  Display* display = gfx::GetXDisplay();

  std::vector<DeviceInfo> device_infos;
  for (int i = 0; i < device_list.count; ++i) {
    const XIDeviceInfo& device = device_list[i];
    device_infos.push_back(DeviceInfo(device, GetDevicePath(display, device)));
  }

  // X11 is not thread safe, so first get all the required state.
  DisplayState display_state;
  display_state.mt_position_x = atom_cache_.GetAtom("Abs MT Position X");
  display_state.mt_position_y = atom_cache_.GetAtom("Abs MT Position Y");

  UiCallbacks callbacks;
  callbacks.keyboard_callback = base::Bind(&OnKeyboardDevices);
  callbacks.touchscreen_callback = base::Bind(&OnTouchscreenDevices);

  // Parsing the device information may block, so delegate the operation to a
  // worker thread. Once the device information is extracted the parsed devices
  // will be returned via the callbacks.
  base::WorkerPool::PostTask(FROM_HERE,
                             base::Bind(&HandleHotplugEventInWorker,
                                        device_infos,
                                        display_state,
                                        base::ThreadTaskRunnerHandle::Get(),
                                        callbacks),
                             true /* task_is_slow */);
}

}  // namespace ui
<|endoftext|>


<commit_before>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/events/platform/x11/x11_hotplug_event_handler.h"

#include <X11/Xatom.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>

#include <algorithm>
#include <cmath>
#include <set>
#include <string>
#include <vector>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_util.h"
#include "base/sys_info.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/worker_pool.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/devices/device_hotplug_event_observer.h"
#include "ui/events/devices/device_util_linux.h"
#include "ui/events/devices/input_device.h"
#include "ui/events/devices/keyboard_device.h"
#include "ui/events/devices/touchscreen_device.h"
#include "ui/gfx/x/x11_types.h"

namespace ui {

namespace {

// The name of the xinput device corresponding to the AT internal keyboard.
const char kATKeyboardName[] = "AT Translated Set 2 keyboard";

// The prefix of xinput devices corresponding to CrOS EC internal keyboards.
const char kCrosEcKeyboardPrefix[] = "cros-ec";

// Names of all known internal devices that should not be considered as
// keyboards.
// TODO(rsadam@): Identify these devices using udev rules. (Crbug.com/420728.)
const char* kKnownInvalidKeyboardDeviceNames[] = {"Power Button",
                                                  "Sleep Button",
                                                  "Video Bus"};

const char* kCachedAtomList[] = {
  "Abs MT Position X",
  "Abs MT Position Y",
  NULL,
};

typedef base::Callback<void(const std::vector<KeyboardDevice>&)>
    KeyboardDeviceCallback;

typedef base::Callback<void(const std::vector<TouchscreenDevice>&)>
    TouchscreenDeviceCallback;

// Used for updating the state on the UI thread once device information is
// parsed on helper threads.
struct UiCallbacks {
  KeyboardDeviceCallback keyboard_callback;
  TouchscreenDeviceCallback touchscreen_callback;
};

// Stores a copy of the XIValuatorClassInfo values so X11 device processing can
// happen on a worker thread. This is needed since X11 structs are not copyable.
struct ValuatorClassInfo {
  ValuatorClassInfo(const XIValuatorClassInfo& info)
      : label(info.label),
        max(info.max),
        min(info.min),
        mode(info.mode),
        number(info.number) {}

  Atom label;
  double max;
  double min;
  int mode;
  int number;
};

// Stores a copy of the XITouchClassInfo values so X11 device processing can
// happen on a worker thread. This is needed since X11 structs are not copyable.
struct TouchClassInfo {
  TouchClassInfo(const XITouchClassInfo& info)
      : mode(info.mode) {}

  int mode;
};

struct DeviceInfo {
  DeviceInfo(const XIDeviceInfo& device, const base::FilePath& path)
      : id(device.deviceid),
        name(device.name),
        use(device.use),
        enabled(device.enabled),
        path(path) {
    for (int i = 0; i < device.num_classes; ++i) {
      switch (device.classes[i]->type) {
        case XIValuatorClass:
          valuator_class_infos.push_back(ValuatorClassInfo(
              *reinterpret_cast<XIValuatorClassInfo*>(device.classes[i])));
          break;
        case XITouchClass:
          touch_class_infos.push_back(TouchClassInfo(
              *reinterpret_cast<XITouchClassInfo*>(device.classes[i])));
          break;
        default:
          break;
      }
    }
  }

  // Unique device identifier.
  int id;

  // Internal device name.
  std::string name;

  // Device type (ie: XIMasterPointer)
  int use;

  // Specifies if the device is enabled and can send events.
  bool enabled;

  // Path to the actual device (ie: /dev/input/eventXX)
  base::FilePath path;

  std::vector<ValuatorClassInfo> valuator_class_infos;

  std::vector<TouchClassInfo> touch_class_infos;
};

// X11 display cache used on worker threads. This is filled on the UI thread and
// passed in to the worker threads.
struct DisplayState {
  Atom mt_position_x;
  Atom mt_position_y;
};

// Returns true if |name| is the name of a known invalid keyboard device. Note,
// this may return false negatives.
bool IsKnownInvalidKeyboardDevice(const std::string& name) {
  for (const char* device_name : kKnownInvalidKeyboardDeviceNames) {
    if (name == device_name)
      return true;
  }
  return false;
}

// Returns true if |name| is the name of a known internal keyboard device. Note,
// this may return false negatives.
bool IsInternalKeyboard(const std::string& name) {
  // TODO(rsadam@): Come up with a more generic way of identifying internal
  // keyboards. See crbug.com/420728.
  if (name == kATKeyboardName)
    return true;
  return name.compare(
             0u, strlen(kCrosEcKeyboardPrefix), kCrosEcKeyboardPrefix) == 0;
}

// Returns true if |name| is the name of a known XTEST device. Note, this may
// return false negatives.
bool IsTestKeyboard(const std::string& name) {
  return name.find("XTEST") != std::string::npos;
}

base::FilePath GetDevicePath(XDisplay* dpy, const XIDeviceInfo& device) {
#if !defined(OS_CHROMEOS)
  return base::FilePath();
#else
  if (!base::SysInfo::IsRunningOnChromeOS())
    return base::FilePath();
#endif

  // Skip the main pointer and keyboard since XOpenDevice() generates a
  // BadDevice error when passed these devices.
  if (device.use == XIMasterPointer || device.use == XIMasterKeyboard)
    return base::FilePath();

  // Input device has a property "Device Node" pointing to its dev input node,
  // e.g.   Device Node (250): "/dev/input/event8"
  Atom device_node = XInternAtom(dpy, "Device Node", False);
  if (device_node == None)
    return base::FilePath();

  Atom actual_type;
  int actual_format;
  unsigned long nitems, bytes_after;
  unsigned char* data;
  XDevice* dev = XOpenDevice(dpy, device.deviceid);
  if (!dev)
    return base::FilePath();

  if (XGetDeviceProperty(dpy,
                         dev,
                         device_node,
                         0,
                         1000,
                         False,
                         AnyPropertyType,
                         &actual_type,
                         &actual_format,
                         &nitems,
                         &bytes_after,
                         &data) != Success) {
    XCloseDevice(dpy, dev);
    return base::FilePath();
  }

  std::string path;
  // Make sure the returned value is a string.
  if (actual_type == XA_STRING && actual_format == 8)
    path = reinterpret_cast<char*>(data);

  XFree(data);
  XCloseDevice(dpy, dev);

  return base::FilePath(path);
}

// Helper used to parse keyboard information. When it is done it uses
// |reply_runner| and |callback| to update the state on the UI thread.
void HandleKeyboardDevicesInWorker(
    const std::vector<DeviceInfo>& device_infos,
    scoped_refptr<base::TaskRunner> reply_runner,
    const KeyboardDeviceCallback& callback) {
  std::vector<KeyboardDevice> devices;

  for (const DeviceInfo& device_info : device_infos) {
    if (!device_info.enabled || device_info.use != XISlaveKeyboard)
      continue;  // Assume all keyboards are keyboard slaves
    std::string device_name(device_info.name);
    base::TrimWhitespaceASCII(device_name, base::TRIM_TRAILING, &device_name);
    if (IsTestKeyboard(device_name))
      continue;  // Skip test devices.
    InputDeviceType type;
    if (IsInternalKeyboard(device_name)) {
      type = InputDeviceType::INPUT_DEVICE_INTERNAL;
    } else if (IsKnownInvalidKeyboardDevice(device_name)) {
      type = InputDeviceType::INPUT_DEVICE_UNKNOWN;
    } else {
      type = InputDeviceType::INPUT_DEVICE_EXTERNAL;
    }
    devices.push_back(
        KeyboardDevice(device_info.id, type, device_name));
  }

  reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices));
}

// Helper used to parse touchscreen information. When it is done it uses
// |reply_runner| and |callback| to update the state on the UI thread.
void HandleTouchscreenDevicesInWorker(
    const std::vector<DeviceInfo>& device_infos,
    const DisplayState& display_state,
    scoped_refptr<base::TaskRunner> reply_runner,
    const TouchscreenDeviceCallback& callback) {
  std::vector<TouchscreenDevice> devices;
  if (display_state.mt_position_x == None ||
      display_state.mt_position_y == None)
    return;

  std::set<int> no_match_touchscreen;
  for (const DeviceInfo& device_info : device_infos) {
    if (!device_info.enabled || device_info.use != XIFloatingSlave)
      continue;  // Assume all touchscreens are floating slaves

    double max_x = -1.0;
    double max_y = -1.0;
    bool is_direct_touch = false;

    for (const ValuatorClassInfo& valuator : device_info.valuator_class_infos) {
      if (display_state.mt_position_x == valuator.label) {
        // Ignore X axis valuator with unexpected properties
        if (valuator.number == 0 && valuator.mode == Absolute &&
            valuator.min == 0.0) {
          max_x = valuator.max;
        }
      } else if (display_state.mt_position_y == valuator.label) {
        // Ignore Y axis valuator with unexpected properties
        if (valuator.number == 1 && valuator.mode == Absolute &&
            valuator.min == 0.0) {
          max_y = valuator.max;
        }
      }
    }

    for (const TouchClassInfo& info : device_info.touch_class_infos) {
      is_direct_touch = info.mode == XIDirectTouch;
    }

    // Touchscreens should have absolute X and Y axes, and be direct touch
    // devices.
    if (max_x > 0.0 && max_y > 0.0 && is_direct_touch) {
      InputDeviceType type = GetInputDeviceTypeFromPath(device_info.path);
      // |max_x| and |max_y| are inclusive values, so we need to add 1 to get
      // the size.
      devices.push_back(TouchscreenDevice(
          device_info.id,
          type,
          device_info.name,
          gfx::Size(max_x + 1, max_y + 1)));
    }
  }

  reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices));
}

// Called on a worker thread to parse the device information.
void HandleHotplugEventInWorker(
    const std::vector<DeviceInfo>& devices,
    const DisplayState& display_state,
    scoped_refptr<base::TaskRunner> reply_runner,
    const UiCallbacks& callbacks) {
  HandleTouchscreenDevicesInWorker(
      devices, display_state, reply_runner, callbacks.touchscreen_callback);
  HandleKeyboardDevicesInWorker(
      devices, reply_runner, callbacks.keyboard_callback);
}

DeviceHotplugEventObserver* GetHotplugEventObserver() {
  return DeviceDataManager::GetInstance();
}

void OnKeyboardDevices(const std::vector<KeyboardDevice>& devices) {
  GetHotplugEventObserver()->OnKeyboardDevicesUpdated(devices);
}

void OnTouchscreenDevices(const std::vector<TouchscreenDevice>& devices) {
  GetHotplugEventObserver()->OnTouchscreenDevicesUpdated(devices);
}

}  // namespace

X11HotplugEventHandler::X11HotplugEventHandler()
    : atom_cache_(gfx::GetXDisplay(), kCachedAtomList) {
}

X11HotplugEventHandler::~X11HotplugEventHandler() {
}

void X11HotplugEventHandler::OnHotplugEvent() {
  const XIDeviceList& device_list =
      DeviceListCacheX11::GetInstance()->GetXI2DeviceList(gfx::GetXDisplay());
  Display* display = gfx::GetXDisplay();

  std::vector<DeviceInfo> device_infos;
  for (int i = 0; i < device_list.count; ++i) {
    const XIDeviceInfo& device = device_list[i];
    device_infos.push_back(DeviceInfo(device, GetDevicePath(display, device)));
  }

  // X11 is not thread safe, so first get all the required state.
  DisplayState display_state;
  display_state.mt_position_x = atom_cache_.GetAtom("Abs MT Position X");
  display_state.mt_position_y = atom_cache_.GetAtom("Abs MT Position Y");

  UiCallbacks callbacks;
  callbacks.keyboard_callback = base::Bind(&OnKeyboardDevices);
  callbacks.touchscreen_callback = base::Bind(&OnTouchscreenDevices);

  // Parsing the device information may block, so delegate the operation to a
  // worker thread. Once the device information is extracted the parsed devices
  // will be returned via the callbacks.
  base::WorkerPool::PostTask(FROM_HERE,
                             base::Bind(&HandleHotplugEventInWorker,
                                        device_infos,
                                        display_state,
                                        base::ThreadTaskRunnerHandle::Get(),
                                        callbacks),
                             true /* task_is_slow */);
}

}  // namespace ui
<commit_msg>events: x11: Use device path to detect internal keyboards<commit_after>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/events/platform/x11/x11_hotplug_event_handler.h"

#include <X11/Xatom.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>

#include <algorithm>
#include <cmath>
#include <set>
#include <string>
#include <vector>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_util.h"
#include "base/sys_info.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/worker_pool.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/devices/device_hotplug_event_observer.h"
#include "ui/events/devices/device_util_linux.h"
#include "ui/events/devices/input_device.h"
#include "ui/events/devices/keyboard_device.h"
#include "ui/events/devices/touchscreen_device.h"
#include "ui/gfx/x/x11_types.h"

namespace ui {

namespace {

// Names of all known internal devices that should not be considered as
// keyboards.
// TODO(rsadam@): Identify these devices using udev rules. (Crbug.com/420728.)
const char* kKnownInvalidKeyboardDeviceNames[] = {"Power Button",
                                                  "Sleep Button",
                                                  "Video Bus"};

const char* kCachedAtomList[] = {
  "Abs MT Position X",
  "Abs MT Position Y",
  NULL,
};

typedef base::Callback<void(const std::vector<KeyboardDevice>&)>
    KeyboardDeviceCallback;

typedef base::Callback<void(const std::vector<TouchscreenDevice>&)>
    TouchscreenDeviceCallback;

// Used for updating the state on the UI thread once device information is
// parsed on helper threads.
struct UiCallbacks {
  KeyboardDeviceCallback keyboard_callback;
  TouchscreenDeviceCallback touchscreen_callback;
};

// Stores a copy of the XIValuatorClassInfo values so X11 device processing can
// happen on a worker thread. This is needed since X11 structs are not copyable.
struct ValuatorClassInfo {
  ValuatorClassInfo(const XIValuatorClassInfo& info)
      : label(info.label),
        max(info.max),
        min(info.min),
        mode(info.mode),
        number(info.number) {}

  Atom label;
  double max;
  double min;
  int mode;
  int number;
};

// Stores a copy of the XITouchClassInfo values so X11 device processing can
// happen on a worker thread. This is needed since X11 structs are not copyable.
struct TouchClassInfo {
  TouchClassInfo(const XITouchClassInfo& info)
      : mode(info.mode) {}

  int mode;
};

struct DeviceInfo {
  DeviceInfo(const XIDeviceInfo& device, const base::FilePath& path)
      : id(device.deviceid),
        name(device.name),
        use(device.use),
        enabled(device.enabled),
        path(path) {
    for (int i = 0; i < device.num_classes; ++i) {
      switch (device.classes[i]->type) {
        case XIValuatorClass:
          valuator_class_infos.push_back(ValuatorClassInfo(
              *reinterpret_cast<XIValuatorClassInfo*>(device.classes[i])));
          break;
        case XITouchClass:
          touch_class_infos.push_back(TouchClassInfo(
              *reinterpret_cast<XITouchClassInfo*>(device.classes[i])));
          break;
        default:
          break;
      }
    }
  }

  // Unique device identifier.
  int id;

  // Internal device name.
  std::string name;

  // Device type (ie: XIMasterPointer)
  int use;

  // Specifies if the device is enabled and can send events.
  bool enabled;

  // Path to the actual device (ie: /dev/input/eventXX)
  base::FilePath path;

  std::vector<ValuatorClassInfo> valuator_class_infos;

  std::vector<TouchClassInfo> touch_class_infos;
};

// X11 display cache used on worker threads. This is filled on the UI thread and
// passed in to the worker threads.
struct DisplayState {
  Atom mt_position_x;
  Atom mt_position_y;
};

// Returns true if |name| is the name of a known invalid keyboard device. Note,
// this may return false negatives.
bool IsKnownInvalidKeyboardDevice(const std::string& name) {
  for (const char* device_name : kKnownInvalidKeyboardDeviceNames) {
    if (name == device_name)
      return true;
  }
  return false;
}

// Returns true if |name| is the name of a known XTEST device. Note, this may
// return false negatives.
bool IsTestKeyboard(const std::string& name) {
  return name.find("XTEST") != std::string::npos;
}

base::FilePath GetDevicePath(XDisplay* dpy, const XIDeviceInfo& device) {
#if !defined(OS_CHROMEOS)
  return base::FilePath();
#else
  if (!base::SysInfo::IsRunningOnChromeOS())
    return base::FilePath();
#endif

  // Skip the main pointer and keyboard since XOpenDevice() generates a
  // BadDevice error when passed these devices.
  if (device.use == XIMasterPointer || device.use == XIMasterKeyboard)
    return base::FilePath();

  // Input device has a property "Device Node" pointing to its dev input node,
  // e.g.   Device Node (250): "/dev/input/event8"
  Atom device_node = XInternAtom(dpy, "Device Node", False);
  if (device_node == None)
    return base::FilePath();

  Atom actual_type;
  int actual_format;
  unsigned long nitems, bytes_after;
  unsigned char* data;
  XDevice* dev = XOpenDevice(dpy, device.deviceid);
  if (!dev)
    return base::FilePath();

  if (XGetDeviceProperty(dpy,
                         dev,
                         device_node,
                         0,
                         1000,
                         False,
                         AnyPropertyType,
                         &actual_type,
                         &actual_format,
                         &nitems,
                         &bytes_after,
                         &data) != Success) {
    XCloseDevice(dpy, dev);
    return base::FilePath();
  }

  std::string path;
  // Make sure the returned value is a string.
  if (actual_type == XA_STRING && actual_format == 8)
    path = reinterpret_cast<char*>(data);

  XFree(data);
  XCloseDevice(dpy, dev);

  return base::FilePath(path);
}

// Helper used to parse keyboard information. When it is done it uses
// |reply_runner| and |callback| to update the state on the UI thread.
void HandleKeyboardDevicesInWorker(
    const std::vector<DeviceInfo>& device_infos,
    scoped_refptr<base::TaskRunner> reply_runner,
    const KeyboardDeviceCallback& callback) {
  std::vector<KeyboardDevice> devices;

  for (const DeviceInfo& device_info : device_infos) {
    if (!device_info.enabled || device_info.use != XISlaveKeyboard)
      continue;  // Assume all keyboards are keyboard slaves
    std::string device_name(device_info.name);
    base::TrimWhitespaceASCII(device_name, base::TRIM_TRAILING, &device_name);
    if (IsTestKeyboard(device_name))
      continue;  // Skip test devices.
    if (IsKnownInvalidKeyboardDevice(device_name))
      continue;  // Skip invalid devices.
    InputDeviceType type = GetInputDeviceTypeFromPath(device_info.path);
    devices.push_back(
        KeyboardDevice(device_info.id, type, device_name));
  }

  reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices));
}

// Helper used to parse touchscreen information. When it is done it uses
// |reply_runner| and |callback| to update the state on the UI thread.
void HandleTouchscreenDevicesInWorker(
    const std::vector<DeviceInfo>& device_infos,
    const DisplayState& display_state,
    scoped_refptr<base::TaskRunner> reply_runner,
    const TouchscreenDeviceCallback& callback) {
  std::vector<TouchscreenDevice> devices;
  if (display_state.mt_position_x == None ||
      display_state.mt_position_y == None)
    return;

  std::set<int> no_match_touchscreen;
  for (const DeviceInfo& device_info : device_infos) {
    if (!device_info.enabled || device_info.use != XIFloatingSlave)
      continue;  // Assume all touchscreens are floating slaves

    double max_x = -1.0;
    double max_y = -1.0;
    bool is_direct_touch = false;

    for (const ValuatorClassInfo& valuator : device_info.valuator_class_infos) {
      if (display_state.mt_position_x == valuator.label) {
        // Ignore X axis valuator with unexpected properties
        if (valuator.number == 0 && valuator.mode == Absolute &&
            valuator.min == 0.0) {
          max_x = valuator.max;
        }
      } else if (display_state.mt_position_y == valuator.label) {
        // Ignore Y axis valuator with unexpected properties
        if (valuator.number == 1 && valuator.mode == Absolute &&
            valuator.min == 0.0) {
          max_y = valuator.max;
        }
      }
    }

    for (const TouchClassInfo& info : device_info.touch_class_infos) {
      is_direct_touch = info.mode == XIDirectTouch;
    }

    // Touchscreens should have absolute X and Y axes, and be direct touch
    // devices.
    if (max_x > 0.0 && max_y > 0.0 && is_direct_touch) {
      InputDeviceType type = GetInputDeviceTypeFromPath(device_info.path);
      // |max_x| and |max_y| are inclusive values, so we need to add 1 to get
      // the size.
      devices.push_back(TouchscreenDevice(
          device_info.id,
          type,
          device_info.name,
          gfx::Size(max_x + 1, max_y + 1)));
    }
  }

  reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices));
}

// Called on a worker thread to parse the device information.
void HandleHotplugEventInWorker(
    const std::vector<DeviceInfo>& devices,
    const DisplayState& display_state,
    scoped_refptr<base::TaskRunner> reply_runner,
    const UiCallbacks& callbacks) {
  HandleTouchscreenDevicesInWorker(
      devices, display_state, reply_runner, callbacks.touchscreen_callback);
  HandleKeyboardDevicesInWorker(
      devices, reply_runner, callbacks.keyboard_callback);
}

DeviceHotplugEventObserver* GetHotplugEventObserver() {
  return DeviceDataManager::GetInstance();
}

void OnKeyboardDevices(const std::vector<KeyboardDevice>& devices) {
  GetHotplugEventObserver()->OnKeyboardDevicesUpdated(devices);
}

void OnTouchscreenDevices(const std::vector<TouchscreenDevice>& devices) {
  GetHotplugEventObserver()->OnTouchscreenDevicesUpdated(devices);
}

}  // namespace

X11HotplugEventHandler::X11HotplugEventHandler()
    : atom_cache_(gfx::GetXDisplay(), kCachedAtomList) {
}

X11HotplugEventHandler::~X11HotplugEventHandler() {
}

void X11HotplugEventHandler::OnHotplugEvent() {
  const XIDeviceList& device_list =
      DeviceListCacheX11::GetInstance()->GetXI2DeviceList(gfx::GetXDisplay());
  Display* display = gfx::GetXDisplay();

  std::vector<DeviceInfo> device_infos;
  for (int i = 0; i < device_list.count; ++i) {
    const XIDeviceInfo& device = device_list[i];
    device_infos.push_back(DeviceInfo(device, GetDevicePath(display, device)));
  }

  // X11 is not thread safe, so first get all the required state.
  DisplayState display_state;
  display_state.mt_position_x = atom_cache_.GetAtom("Abs MT Position X");
  display_state.mt_position_y = atom_cache_.GetAtom("Abs MT Position Y");

  UiCallbacks callbacks;
  callbacks.keyboard_callback = base::Bind(&OnKeyboardDevices);
  callbacks.touchscreen_callback = base::Bind(&OnTouchscreenDevices);

  // Parsing the device information may block, so delegate the operation to a
  // worker thread. Once the device information is extracted the parsed devices
  // will be returned via the callbacks.
  base::WorkerPool::PostTask(FROM_HERE,
                             base::Bind(&HandleHotplugEventInWorker,
                                        device_infos,
                                        display_state,
                                        base::ThreadTaskRunnerHandle::Get(),
                                        callbacks),
                             true /* task_is_slow */);
}

}  // namespace ui
<|endoftext|>


<commit_before>/*
    This file is part of Magnum.

    Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016
              Vladimír Vondruš <mosra@centrum.cz>

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*/

#include <sstream>
#include <Corrade/TestSuite/Tester.h>

#include "Magnum/Audio/Buffer.h"

namespace Magnum { namespace Audio { namespace Test {

struct BufferTest: TestSuite::Tester {
    explicit BufferTest();

    void debugFormat();
};

BufferTest::BufferTest() {
    addTests({&BufferTest::debugFormat});
}

void BufferTest::debugFormat() {
    std::ostringstream out;
    Debug(&out) << Buffer::Format::Stereo16;
    CORRADE_COMPARE(out.str(), "Audio::Buffer::Format::Stereo16\n");
}

}}}

CORRADE_TEST_MAIN(Magnum::Audio::Test::BufferTest)
<commit_msg>Audio: test Buffer construction.<commit_after>/*
    This file is part of Magnum.

    Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016
              Vladimír Vondruš <mosra@centrum.cz>

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*/

#include <sstream>
#include <Corrade/TestSuite/Tester.h>

#include "Magnum/Audio/Buffer.h"
#include "Magnum/Audio/Context.h"

namespace Magnum { namespace Audio { namespace Test {

struct BufferTest: TestSuite::Tester {
    explicit BufferTest();

    void construct();
    void debugFormat();

    Context _context;
};

BufferTest::BufferTest() {
    addTests({&BufferTest::construct,
              &BufferTest::debugFormat});
}

void BufferTest::construct() {
    Buffer buf;
    CORRADE_VERIFY(buf.id() != 0);
}

void BufferTest::debugFormat() {
    std::ostringstream out;
    Debug(&out) << Buffer::Format::Stereo16;
    CORRADE_COMPARE(out.str(), "Audio::Buffer::Format::Stereo16\n");
}

}}}

CORRADE_TEST_MAIN(Magnum::Audio::Test::BufferTest)
<|endoftext|>


<commit_before>/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

/// @file
///     @author Don Gagne <don@thegagnes.com>

#include "MissionManager.h"
#include "Vehicle.h"
#include "FirmwarePlugin.h"
#include "MAVLinkProtocol.h"
#include "QGCApplication.h"
#include "MissionCommandTree.h"
#include "MissionCommandUIInfo.h"

QGC_LOGGING_CATEGORY(MissionManagerLog, "MissionManagerLog")

MissionManager::MissionManager(Vehicle* vehicle)
    : PlanManager(vehicle, MAV_MISSION_TYPE_MISSION)
{

}

MissionManager::~MissionManager()
{

}
void MissionManager::writeArduPilotGuidedMissionItem(const QGeoCoordinate& gotoCoord, bool altChangeOnly)
{
    if (inProgress()) {
        qCDebug(MissionManagerLog) << "writeArduPilotGuidedMissionItem called while transaction in progress";
        return;
    }

    _transactionInProgress = TransactionWrite;

    mavlink_message_t       messageOut;
    mavlink_mission_item_t  missionItem;

    memset(&missionItem, 8, sizeof(missionItem));
    missionItem.target_system =     _vehicle->id();
    missionItem.target_component =  _vehicle->defaultComponentId();
    missionItem.seq =               0;
    missionItem.command =           MAV_CMD_NAV_WAYPOINT;
    missionItem.param1 =            0;
    missionItem.param2 =            0;
    missionItem.param3 =            0;
    missionItem.param4 =            0;
    missionItem.x =                 gotoCoord.latitude();
    missionItem.y =                 gotoCoord.longitude();
    missionItem.z =                 gotoCoord.altitude();
    missionItem.frame =             MAV_FRAME_GLOBAL_RELATIVE_ALT;
    missionItem.current =           altChangeOnly ? 3 : 2;
    missionItem.autocontinue =      true;

    _dedicatedLink = _vehicle->priorityLink();
    mavlink_msg_mission_item_encode_chan(qgcApp()->toolbox()->mavlinkProtocol()->getSystemId(),
                                         qgcApp()->toolbox()->mavlinkProtocol()->getComponentId(),
                                         _dedicatedLink->mavlinkChannel(),
                                         &messageOut,
                                         &missionItem);

    _vehicle->sendMessageOnLink(_dedicatedLink, messageOut);
    _startAckTimeout(AckGuidedItem);
    emit inProgressChanged(true);
}

void MissionManager::generateResumeMission(int resumeIndex)
{
    if (_vehicle->isOfflineEditingVehicle()) {
        return;
    }

    if (inProgress()) {
        qCDebug(MissionManagerLog) << "generateResumeMission called while transaction in progress";
        return;
    }

    for (int i=0; i<_missionItems.count(); i++) {
        MissionItem* item = _missionItems[i];
        if (item->command() == MAV_CMD_DO_JUMP) {
            qgcApp()->showMessage(tr("Unable to generate resume mission due to MAV_CMD_DO_JUMP command."));
            return;
        }
    }

    // Be anal about crap input
    resumeIndex = qMax(0, qMin(resumeIndex, _missionItems.count() - 1));

    // Adjust resume index to be a location based command
    const MissionCommandUIInfo* uiInfo = qgcApp()->toolbox()->missionCommandTree()->getUIInfo(_vehicle, _missionItems[resumeIndex]->command());
    if (!uiInfo || uiInfo->isStandaloneCoordinate() || !uiInfo->specifiesCoordinate()) {
        // We have to back up to the last command which the vehicle flies through
        while (--resumeIndex > 0) {
            uiInfo = qgcApp()->toolbox()->missionCommandTree()->getUIInfo(_vehicle, _missionItems[resumeIndex]->command());
            if (uiInfo && (uiInfo->specifiesCoordinate() && !uiInfo->isStandaloneCoordinate())) {
                // Found it
                break;
            }

        }
    }
    resumeIndex = qMax(0, resumeIndex);

    QList<MissionItem*> resumeMission;

    QList<MAV_CMD> includedResumeCommands;

    // If any command in this list occurs before the resumeIndex it will be added to the front of the mission
    includedResumeCommands << MAV_CMD_DO_CONTROL_VIDEO
                           << MAV_CMD_DO_SET_ROI
                           << MAV_CMD_DO_DIGICAM_CONFIGURE
                           << MAV_CMD_DO_DIGICAM_CONTROL
                           << MAV_CMD_DO_MOUNT_CONFIGURE
                           << MAV_CMD_DO_MOUNT_CONTROL
                           << MAV_CMD_DO_SET_CAM_TRIGG_DIST
                           << MAV_CMD_DO_FENCE_ENABLE
                           << MAV_CMD_IMAGE_START_CAPTURE
                           << MAV_CMD_IMAGE_STOP_CAPTURE
                           << MAV_CMD_VIDEO_START_CAPTURE
                           << MAV_CMD_VIDEO_STOP_CAPTURE
                           << MAV_CMD_DO_CHANGE_SPEED
                           << MAV_CMD_SET_CAMERA_MODE
                           << MAV_CMD_NAV_TAKEOFF;

    bool addHomePosition = _vehicle->firmwarePlugin()->sendHomePositionToVehicle();

    int prefixCommandCount = 0;
    for (int i=0; i<_missionItems.count(); i++) {
        MissionItem* oldItem = _missionItems[i];
        if ((i == 0 && addHomePosition) || i >= resumeIndex || includedResumeCommands.contains(oldItem->command())) {
            if (i < resumeIndex) {
                prefixCommandCount++;
            }
            MissionItem* newItem = new MissionItem(*oldItem, this);
            newItem->setIsCurrentItem(false);
            resumeMission.append(newItem);
        }
    }
    prefixCommandCount = qMax(0, qMin(prefixCommandCount, resumeMission.count()));  // Anal prevention against crashes

    // De-dup and remove no-ops from the commands which were added to the front of the mission
    bool foundROI = false;
    bool foundCameraSetMode = false;
    bool foundCameraStartStop = false;
    prefixCommandCount--;   // Change from count to array index
    while (prefixCommandCount >= 0) {
        MissionItem* resumeItem = resumeMission[prefixCommandCount];
        switch (resumeItem->command()) {
        case MAV_CMD_SET_CAMERA_MODE:
            // Only keep the last one
            if (foundCameraSetMode) {
                resumeMission.removeAt(prefixCommandCount);
            }
            foundCameraSetMode = true;
            break;
        case MAV_CMD_DO_SET_ROI:
            // Only keep the last one
            if (foundROI) {
                resumeMission.removeAt(prefixCommandCount);
            }
            foundROI = true;
            break;
        case MAV_CMD_DO_SET_CAM_TRIGG_DIST:
        case MAV_CMD_IMAGE_STOP_CAPTURE:
        case MAV_CMD_VIDEO_START_CAPTURE:
        case MAV_CMD_VIDEO_STOP_CAPTURE:
            // Only keep the first of these commands that are found
            if (foundCameraStartStop) {
                resumeMission.removeAt(prefixCommandCount);
            }
            foundCameraStartStop = true;
            break;
        case MAV_CMD_IMAGE_START_CAPTURE:
            if (resumeItem->param3() != 0) {
                // Remove commands which do not trigger by time
                resumeMission.removeAt(prefixCommandCount);
                break;
            }
            if (foundCameraStartStop) {
                // Only keep the first of these commands that are found
                resumeMission.removeAt(prefixCommandCount);
            }
            foundCameraStartStop = true;
            break;
        default:
            break;
        }

        prefixCommandCount--;
    }

    // Adjust sequence numbers and current item
    int seqNum = 0;
    for (int i=0; i<resumeMission.count(); i++) {
        resumeMission[i]->setSequenceNumber(seqNum++);
    }
    int setCurrentIndex = addHomePosition ? 1 : 0;
    resumeMission[setCurrentIndex]->setIsCurrentItem(true);

    // Send to vehicle
    _clearAndDeleteWriteMissionItems();
    for (int i=0; i<resumeMission.count(); i++) {
        _writeMissionItems.append(new MissionItem(*resumeMission[i], this));
    }
    _resumeMission = true;
    _writeMissionItemsWorker();

    // Clean up no longer needed resume items
    for (int i=0; i<resumeMission.count(); i++) {
        resumeMission[i]->deleteLater();
    }
}
<commit_msg>Fix bug in ArduPilot goto location support<commit_after>/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

/// @file
///     @author Don Gagne <don@thegagnes.com>

#include "MissionManager.h"
#include "Vehicle.h"
#include "FirmwarePlugin.h"
#include "MAVLinkProtocol.h"
#include "QGCApplication.h"
#include "MissionCommandTree.h"
#include "MissionCommandUIInfo.h"

QGC_LOGGING_CATEGORY(MissionManagerLog, "MissionManagerLog")

MissionManager::MissionManager(Vehicle* vehicle)
    : PlanManager(vehicle, MAV_MISSION_TYPE_MISSION)
{

}

MissionManager::~MissionManager()
{

}
void MissionManager::writeArduPilotGuidedMissionItem(const QGeoCoordinate& gotoCoord, bool altChangeOnly)
{
    if (inProgress()) {
        qCDebug(MissionManagerLog) << "writeArduPilotGuidedMissionItem called while transaction in progress";
        return;
    }

    _transactionInProgress = TransactionWrite;

    _connectToMavlink();

    mavlink_message_t       messageOut;
    mavlink_mission_item_t  missionItem;

    memset(&missionItem, 8, sizeof(missionItem));
    missionItem.target_system =     _vehicle->id();
    missionItem.target_component =  _vehicle->defaultComponentId();
    missionItem.seq =               0;
    missionItem.command =           MAV_CMD_NAV_WAYPOINT;
    missionItem.param1 =            0;
    missionItem.param2 =            0;
    missionItem.param3 =            0;
    missionItem.param4 =            0;
    missionItem.x =                 gotoCoord.latitude();
    missionItem.y =                 gotoCoord.longitude();
    missionItem.z =                 gotoCoord.altitude();
    missionItem.frame =             MAV_FRAME_GLOBAL_RELATIVE_ALT;
    missionItem.current =           altChangeOnly ? 3 : 2;
    missionItem.autocontinue =      true;

    _dedicatedLink = _vehicle->priorityLink();
    mavlink_msg_mission_item_encode_chan(qgcApp()->toolbox()->mavlinkProtocol()->getSystemId(),
                                         qgcApp()->toolbox()->mavlinkProtocol()->getComponentId(),
                                         _dedicatedLink->mavlinkChannel(),
                                         &messageOut,
                                         &missionItem);

    _vehicle->sendMessageOnLink(_dedicatedLink, messageOut);
    _startAckTimeout(AckGuidedItem);
    emit inProgressChanged(true);
}

void MissionManager::generateResumeMission(int resumeIndex)
{
    if (_vehicle->isOfflineEditingVehicle()) {
        return;
    }

    if (inProgress()) {
        qCDebug(MissionManagerLog) << "generateResumeMission called while transaction in progress";
        return;
    }

    for (int i=0; i<_missionItems.count(); i++) {
        MissionItem* item = _missionItems[i];
        if (item->command() == MAV_CMD_DO_JUMP) {
            qgcApp()->showMessage(tr("Unable to generate resume mission due to MAV_CMD_DO_JUMP command."));
            return;
        }
    }

    // Be anal about crap input
    resumeIndex = qMax(0, qMin(resumeIndex, _missionItems.count() - 1));

    // Adjust resume index to be a location based command
    const MissionCommandUIInfo* uiInfo = qgcApp()->toolbox()->missionCommandTree()->getUIInfo(_vehicle, _missionItems[resumeIndex]->command());
    if (!uiInfo || uiInfo->isStandaloneCoordinate() || !uiInfo->specifiesCoordinate()) {
        // We have to back up to the last command which the vehicle flies through
        while (--resumeIndex > 0) {
            uiInfo = qgcApp()->toolbox()->missionCommandTree()->getUIInfo(_vehicle, _missionItems[resumeIndex]->command());
            if (uiInfo && (uiInfo->specifiesCoordinate() && !uiInfo->isStandaloneCoordinate())) {
                // Found it
                break;
            }

        }
    }
    resumeIndex = qMax(0, resumeIndex);

    QList<MissionItem*> resumeMission;

    QList<MAV_CMD> includedResumeCommands;

    // If any command in this list occurs before the resumeIndex it will be added to the front of the mission
    includedResumeCommands << MAV_CMD_DO_CONTROL_VIDEO
                           << MAV_CMD_DO_SET_ROI
                           << MAV_CMD_DO_DIGICAM_CONFIGURE
                           << MAV_CMD_DO_DIGICAM_CONTROL
                           << MAV_CMD_DO_MOUNT_CONFIGURE
                           << MAV_CMD_DO_MOUNT_CONTROL
                           << MAV_CMD_DO_SET_CAM_TRIGG_DIST
                           << MAV_CMD_DO_FENCE_ENABLE
                           << MAV_CMD_IMAGE_START_CAPTURE
                           << MAV_CMD_IMAGE_STOP_CAPTURE
                           << MAV_CMD_VIDEO_START_CAPTURE
                           << MAV_CMD_VIDEO_STOP_CAPTURE
                           << MAV_CMD_DO_CHANGE_SPEED
                           << MAV_CMD_SET_CAMERA_MODE
                           << MAV_CMD_NAV_TAKEOFF;

    bool addHomePosition = _vehicle->firmwarePlugin()->sendHomePositionToVehicle();

    int prefixCommandCount = 0;
    for (int i=0; i<_missionItems.count(); i++) {
        MissionItem* oldItem = _missionItems[i];
        if ((i == 0 && addHomePosition) || i >= resumeIndex || includedResumeCommands.contains(oldItem->command())) {
            if (i < resumeIndex) {
                prefixCommandCount++;
            }
            MissionItem* newItem = new MissionItem(*oldItem, this);
            newItem->setIsCurrentItem(false);
            resumeMission.append(newItem);
        }
    }
    prefixCommandCount = qMax(0, qMin(prefixCommandCount, resumeMission.count()));  // Anal prevention against crashes

    // De-dup and remove no-ops from the commands which were added to the front of the mission
    bool foundROI = false;
    bool foundCameraSetMode = false;
    bool foundCameraStartStop = false;
    prefixCommandCount--;   // Change from count to array index
    while (prefixCommandCount >= 0) {
        MissionItem* resumeItem = resumeMission[prefixCommandCount];
        switch (resumeItem->command()) {
        case MAV_CMD_SET_CAMERA_MODE:
            // Only keep the last one
            if (foundCameraSetMode) {
                resumeMission.removeAt(prefixCommandCount);
            }
            foundCameraSetMode = true;
            break;
        case MAV_CMD_DO_SET_ROI:
            // Only keep the last one
            if (foundROI) {
                resumeMission.removeAt(prefixCommandCount);
            }
            foundROI = true;
            break;
        case MAV_CMD_DO_SET_CAM_TRIGG_DIST:
        case MAV_CMD_IMAGE_STOP_CAPTURE:
        case MAV_CMD_VIDEO_START_CAPTURE:
        case MAV_CMD_VIDEO_STOP_CAPTURE:
            // Only keep the first of these commands that are found
            if (foundCameraStartStop) {
                resumeMission.removeAt(prefixCommandCount);
            }
            foundCameraStartStop = true;
            break;
        case MAV_CMD_IMAGE_START_CAPTURE:
            if (resumeItem->param3() != 0) {
                // Remove commands which do not trigger by time
                resumeMission.removeAt(prefixCommandCount);
                break;
            }
            if (foundCameraStartStop) {
                // Only keep the first of these commands that are found
                resumeMission.removeAt(prefixCommandCount);
            }
            foundCameraStartStop = true;
            break;
        default:
            break;
        }

        prefixCommandCount--;
    }

    // Adjust sequence numbers and current item
    int seqNum = 0;
    for (int i=0; i<resumeMission.count(); i++) {
        resumeMission[i]->setSequenceNumber(seqNum++);
    }
    int setCurrentIndex = addHomePosition ? 1 : 0;
    resumeMission[setCurrentIndex]->setIsCurrentItem(true);

    // Send to vehicle
    _clearAndDeleteWriteMissionItems();
    for (int i=0; i<resumeMission.count(); i++) {
        _writeMissionItems.append(new MissionItem(*resumeMission[i], this));
    }
    _resumeMission = true;
    _writeMissionItemsWorker();

    // Clean up no longer needed resume items
    for (int i=0; i<resumeMission.count(); i++) {
        resumeMission[i]->deleteLater();
    }
}
<|endoftext|>