Commit bea9410c authored by sail@chromium.org's avatar sail@chromium.org

Implement DeviceOrientation API on Windows

The implemenation is based on Windows sensor framework COM API. It uses
the data of inclinometer 3D sensor which is a required fusion sensor
based on the accelerometer sensor.

BUG=224849
TEST=http://www.html5rocks.com/en/tutorials/device/orientation/deviceorientationsample.html

Review URL: https://codereview.chromium.org/13433002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195256 0039d316-1c4b-4281-b951-d872f2087c98
parent 57b1ef34
// Copyright (c) 2013 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 "content/browser/device_orientation/data_fetcher_impl_win.h"
#include <InitGuid.h>
#include <PortableDeviceTypes.h>
#include <Sensors.h>
#include "base/logging.h"
#include "base/win/iunknown_impl.h"
#include "base/win/windows_version.h"
#include "content/browser/device_orientation/orientation.h"
namespace {
// This should match ProviderImpl::kDesiredSamplingIntervalMs.
const int kPeriodInMilliseconds = 100;
} // namespace
namespace content {
class DataFetcherImplWin::SensorEventSink : public ISensorEvents,
public base::win::IUnknownImpl {
public:
explicit SensorEventSink(DataFetcherImplWin* const fetcher)
: fetcher_(fetcher) {}
virtual ~SensorEventSink() {}
// IUnknown interface
virtual ULONG STDMETHODCALLTYPE AddRef() OVERRIDE {
return IUnknownImpl::AddRef();
}
virtual ULONG STDMETHODCALLTYPE Release() OVERRIDE {
return IUnknownImpl::Release();
}
virtual STDMETHODIMP QueryInterface(REFIID riid, void** ppv) OVERRIDE {
if (riid == __uuidof(ISensorEvents)) {
*ppv = static_cast<ISensorEvents*>(this);
AddRef();
return S_OK;
}
return IUnknownImpl::QueryInterface(riid, ppv);
}
// ISensorEvents interface
STDMETHODIMP OnEvent(ISensor* sensor,
REFGUID event_id,
IPortableDeviceValues* event_data) OVERRIDE {
return S_OK;
}
STDMETHODIMP OnDataUpdated(ISensor* sensor,
ISensorDataReport* new_data) OVERRIDE {
if (NULL == new_data || NULL == sensor)
return E_INVALIDARG;
PROPVARIANT value = {};
scoped_refptr<Orientation> orientation = new Orientation();
if (SUCCEEDED(new_data->GetSensorValue(
SENSOR_DATA_TYPE_TILT_X_DEGREES, &value))) {
orientation->set_beta(value.fltVal);
}
PropVariantClear(&value);
if (SUCCEEDED(new_data->GetSensorValue(
SENSOR_DATA_TYPE_TILT_Y_DEGREES, &value))) {
orientation->set_gamma(value.fltVal);
}
PropVariantClear(&value);
if (SUCCEEDED(new_data->GetSensorValue(
SENSOR_DATA_TYPE_TILT_Z_DEGREES, &value))) {
orientation->set_alpha(value.fltVal);
}
PropVariantClear(&value);
orientation->set_absolute(true);
fetcher_->OnOrientationData(orientation.get());
return S_OK;
}
STDMETHODIMP OnLeave(REFSENSOR_ID sensor_id) OVERRIDE {
return S_OK;
}
STDMETHODIMP OnStateChanged(ISensor* sensor, SensorState state) OVERRIDE {
return S_OK;
}
private:
DataFetcherImplWin* const fetcher_;
DISALLOW_COPY_AND_ASSIGN(SensorEventSink);
};
// Create a DataFetcherImplWin object and return NULL if no valid sensor found.
// static
DataFetcher* DataFetcherImplWin::Create() {
scoped_ptr<DataFetcherImplWin> fetcher(new DataFetcherImplWin);
if (fetcher->Initialize())
return fetcher.release();
LOG(ERROR) << "DataFetcherImplWin::Initialize failed!";
return NULL;
}
DataFetcherImplWin::~DataFetcherImplWin() {
if (sensor_)
sensor_->SetEventSink(NULL);
}
DataFetcherImplWin::DataFetcherImplWin() {
}
void DataFetcherImplWin::OnOrientationData(Orientation* orientation) {
// This method is called on Windows sensor thread.
base::AutoLock autolock(next_orientation_lock_);
next_orientation_ = orientation;
}
const DeviceData* DataFetcherImplWin::GetDeviceData(DeviceData::Type type) {
if (type != DeviceData::kTypeOrientation)
return NULL;
return GetOrientation();
}
const Orientation* DataFetcherImplWin::GetOrientation() {
if (next_orientation_.get()) {
base::AutoLock autolock(next_orientation_lock_);
next_orientation_.swap(current_orientation_);
}
if (!current_orientation_.get())
return new Orientation();
return current_orientation_.get();
}
bool DataFetcherImplWin::Initialize() {
if (base::win::GetVersion() < base::win::VERSION_WIN7)
return false;
base::win::ScopedComPtr<ISensorManager> sensor_manager;
HRESULT hr = sensor_manager.CreateInstance(CLSID_SensorManager);
if (FAILED(hr) || !sensor_manager)
return false;
base::win::ScopedComPtr<ISensorCollection> sensor_collection;
hr = sensor_manager->GetSensorsByType(
SENSOR_TYPE_INCLINOMETER_3D, sensor_collection.Receive());
if (FAILED(hr) || !sensor_collection)
return false;
ULONG count = 0;
hr = sensor_collection->GetCount(&count);
if (FAILED(hr) || !count)
return false;
hr = sensor_collection->GetAt(0, sensor_.Receive());
if (FAILED(hr) || !sensor_)
return false;
base::win::ScopedComPtr<IPortableDeviceValues> device_values;
if (SUCCEEDED(device_values.CreateInstance(CLSID_PortableDeviceValues))) {
if (SUCCEEDED(device_values->SetUnsignedIntegerValue(
SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL, kPeriodInMilliseconds))) {
base::win::ScopedComPtr<IPortableDeviceValues> return_values;
sensor_->SetProperties(device_values.get(), return_values.Receive());
}
}
scoped_refptr<SensorEventSink> sensor_event_impl(new SensorEventSink(this));
base::win::ScopedComPtr<ISensorEvents> sensor_events;
hr = sensor_event_impl->QueryInterface(
__uuidof(ISensorEvents), sensor_events.ReceiveVoid());
if (FAILED(hr) || !sensor_events)
return false;
hr = sensor_->SetEventSink(sensor_events);
if (FAILED(hr))
return false;
return true;
}
} // namespace content
// Copyright (c) 2013 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.
#ifndef CONTENT_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_IMPL_WIN_H_
#define CONTENT_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_IMPL_WIN_H_
#include <SensorsApi.h>
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "base/win/scoped_comptr.h"
#include "content/browser/device_orientation/data_fetcher.h"
#include "content/browser/device_orientation/device_data.h"
namespace content {
class Orientation;
// Windows implementation of DeviceOrientation API.
// The SensorEventSink is installed to Windows sensor thread to listen for
// sensor's data. Upon each notification, DataFetcherImplWin buffers the data.
// Then Chrome sensor polling thread pulls the buffered data via GetDeviceData.
// The Inclinometer 3D sensor (SENSOR_TYPE_INCLINOMETER_3D) is used to get the
// orientation data.
class DataFetcherImplWin : public DataFetcher {
public:
virtual ~DataFetcherImplWin();
// Factory function. It returns NULL on error.
// The created object listens for events for the whole lifetime.
static DataFetcher* Create();
// Implement DataFetcher.
virtual const DeviceData* GetDeviceData(DeviceData::Type type) OVERRIDE;
private:
class SensorEventSink;
friend SensorEventSink;
DataFetcherImplWin();
bool Initialize();
void OnOrientationData(Orientation* orientation);
const Orientation* GetOrientation();
base::win::ScopedComPtr<ISensor> sensor_;
// Value returned by GetDeviceData.
scoped_refptr<Orientation> current_orientation_;
// The 1-element buffer follows DataFetcherImplAndroid implementation.
// It is written by OnOrientationData and read by GetDeviceData.
base::Lock next_orientation_lock_;
scoped_refptr<Orientation> next_orientation_;
DISALLOW_COPY_AND_ASSIGN(DataFetcherImplWin);
};
} // namespace content
#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_IMPL_WIN_H_
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#include "content/browser/device_orientation/accelerometer_mac.h" #include "content/browser/device_orientation/accelerometer_mac.h"
#elif defined(OS_ANDROID) #elif defined(OS_ANDROID)
#include "content/browser/device_orientation/data_fetcher_impl_android.h" #include "content/browser/device_orientation/data_fetcher_impl_android.h"
#elif defined(OS_WIN)
#include "content/browser/device_orientation/data_fetcher_impl_win.h"
#endif #endif
namespace content { namespace content {
...@@ -26,6 +28,8 @@ Provider* Provider::GetInstance() { ...@@ -26,6 +28,8 @@ Provider* Provider::GetInstance() {
default_factory = AccelerometerMac::Create; default_factory = AccelerometerMac::Create;
#elif defined(OS_ANDROID) #elif defined(OS_ANDROID)
default_factory = DataFetcherImplAndroid::Create; default_factory = DataFetcherImplAndroid::Create;
#elif defined(OS_WIN)
default_factory = DataFetcherImplWin::Create;
#endif #endif
instance_ = new ProviderImpl(default_factory); instance_ = new ProviderImpl(default_factory);
......
...@@ -224,6 +224,9 @@ void ProviderImpl::Start(DeviceData::Type type) { ...@@ -224,6 +224,9 @@ void ProviderImpl::Start(DeviceData::Type type) {
polling_thread_ = new PollingThread("Device data polling thread", polling_thread_ = new PollingThread("Device data polling thread",
weak_factory_.GetWeakPtr(), weak_factory_.GetWeakPtr(),
creator_loop_); creator_loop_);
#if defined(OS_WIN)
polling_thread_->init_com_with_mta(true);
#endif
if (!polling_thread_->Start()) { if (!polling_thread_->Start()) {
LOG(ERROR) << "Failed to start device data polling thread"; LOG(ERROR) << "Failed to start device data polling thread";
delete polling_thread_; delete polling_thread_;
......
...@@ -347,6 +347,8 @@ ...@@ -347,6 +347,8 @@
'browser/device_orientation/data_fetcher.h', 'browser/device_orientation/data_fetcher.h',
'browser/device_orientation/data_fetcher_impl_android.cc', 'browser/device_orientation/data_fetcher_impl_android.cc',
'browser/device_orientation/data_fetcher_impl_android.h', 'browser/device_orientation/data_fetcher_impl_android.h',
'browser/device_orientation/data_fetcher_impl_win.cc',
'browser/device_orientation/data_fetcher_impl_win.h',
'browser/device_orientation/device_data.h', 'browser/device_orientation/device_data.h',
'browser/device_orientation/message_filter.cc', 'browser/device_orientation/message_filter.cc',
'browser/device_orientation/message_filter.h', 'browser/device_orientation/message_filter.h',
...@@ -1123,6 +1125,7 @@ ...@@ -1123,6 +1125,7 @@
'-ldwmapi.lib', '-ldwmapi.lib',
'-llocationapi.lib', '-llocationapi.lib',
'-lsensorsapi.lib', '-lsensorsapi.lib',
'-lportabledeviceguids.lib',
], ],
'msvs_settings': { 'msvs_settings': {
'VCLinkerTool': { 'VCLinkerTool': {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment