/*****************************************************************************
* sensors.cpp: Windows sensor handling
*****************************************************************************
* Copyright © 2017 Steve Lhomme
* Copyright © 2017 VideoLabs
*
* Authors: Steve Lhomme <robux4@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "events.h"
#include "sensors.h"
#include <initguid.h>
#include <wrl/client.h>
#include <propsys.h> /* stupid mingw headers don't include this */
#include <sensors.h>
#include <sensorsapi.h>
#include <new>
using Microsoft::WRL::ComPtr;
class SensorReceiver : public ISensorEvents
{
public:
SensorReceiver(const vout_display_owner_t *o, const vlc_viewpoint_t & init_viewpoint)
:owner(*o)
,current_pos(init_viewpoint)
{}
virtual ~SensorReceiver()
{}
STDMETHODIMP QueryInterface(REFIID iid, void** ppv)
{
if (ppv == NULL)
{
return E_POINTER;
}
if (iid == __uuidof(IUnknown))
{
*ppv = static_cast<IUnknown*>(this);
}
else if (iid == __uuidof(ISensorEvents))
{
*ppv = static_cast<ISensorEvents*>(this);
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) Release()
{
ULONG count = InterlockedDecrement(&m_cRef);
if (count == 0)
{
delete this;
return 0;
}
return count;
}
HRESULT STDMETHODCALLTYPE OnStateChanged(ISensor *pSensor, SensorState state)
{
(void)pSensor;
(void)state;
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDataUpdated(ISensor *pSensor, ISensorDataReport *pNewData)
{
(void)pSensor;
vlc_viewpoint_t old_pos = current_pos;
HRESULT hr;
PROPVARIANT pvRot;
PropVariantInit(&pvRot);
hr = pNewData->GetSensorValue(SENSOR_DATA_TYPE_TILT_X_DEGREES, &pvRot);
if (SUCCEEDED(hr) && pvRot.vt == VT_R4)
{
current_pos.pitch = pvRot.fltVal;
PropVariantClear(&pvRot);
}
hr = pNewData->GetSensorValue(SENSOR_DATA_TYPE_TILT_Y_DEGREES, &pvRot);
if (SUCCEEDED(hr) && pvRot.vt == VT_R4)
{
current_pos.roll = pvRot.fltVal;
PropVariantClear(&pvRot);
}
hr = pNewData->GetSensorValue(SENSOR_DATA_TYPE_TILT_Z_DEGREES, &pvRot);
if (SUCCEEDED(hr) && pvRot.vt == VT_R4)
{
current_pos.yaw = pvRot.fltVal;
PropVariantClear(&pvRot);
}
// TODO: use current_pos directly?
/* Zero initialize vp for field of view. */
vlc_viewpoint_t vp {};
vp.fov = 0.f;
vlc_viewpoint_from_euler(&vp,
old_pos.yaw - current_pos.yaw,
old_pos.pitch - current_pos.pitch,
old_pos.roll - current_pos.roll
);
if (owner.viewpoint_moved)
owner.viewpoint_moved(owner.sys, &vp);
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnEvent(ISensor *pSensor, REFGUID eventID, IPortableDeviceValues *pEventData)
{
(void)pSensor;
(void)eventID;
(void)pEventData;
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnLeave(REFSENSOR_ID ID)
{
(void)ID;
return S_OK;
}
private:
vout_display_owner_t owner;
vlc_viewpoint_t current_pos;
long m_cRef;
};
void *HookWindowsSensors(vlc_logger *vd, const vout_display_owner_t *move, HWND hwnd)
{
ComPtr<ISensorManager> pSensorManager;
HRESULT hr = CoCreateInstance( __uuidof(SensorManager),
NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(pSensorManager.GetAddressOf()) );
if (FAILED(hr))
return NULL;
ComPtr<ISensorCollection> pInclinometers;
hr = pSensorManager->GetSensorsByType(SENSOR_TYPE_INCLINOMETER_3D, &pInclinometers);
if (FAILED(hr))
{
vlc_debug(vd, "inclinometer not found. (hr=0x%lX)", hr);
return NULL;
}
ULONG count;
pInclinometers->GetCount(&count);
vlc_debug(vd, "Found %lu inclinometer", count);
for (ULONG i=0; i<count; ++i)
{
ComPtr<ISensor> pSensor;
hr = pInclinometers->GetAt(i, &pSensor);
if (FAILED(hr))
continue;
SensorState state = SENSOR_STATE_NOT_AVAILABLE;
hr = pSensor->GetState(&state);
if (FAILED(hr))
continue;
if (state == SENSOR_STATE_ACCESS_DENIED)
hr = pSensorManager->RequestPermissions(hwnd, pInclinometers.Get(), TRUE);
if (FAILED(hr))
continue;
vlc_viewpoint_t start_viewpoint;
vlc_viewpoint_init(&start_viewpoint);
PROPVARIANT pvRot;
PropVariantInit(&pvRot);
hr = pSensor->GetProperty(SENSOR_DATA_TYPE_TILT_X_DEGREES, &pvRot);
if (SUCCEEDED(hr) && pvRot.vt == VT_R4)
{
start_viewpoint.pitch = pvRot.fltVal;
PropVariantClear(&pvRot);
}
hr = pSensor->GetProperty(SENSOR_DATA_TYPE_TILT_Y_DEGREES, &pvRot);
if (SUCCEEDED(hr) && pvRot.vt == VT_R4)
{
start_viewpoint.roll = pvRot.fltVal;
PropVariantClear(&pvRot);
}
hr = pSensor->GetProperty(SENSOR_DATA_TYPE_TILT_Z_DEGREES, &pvRot);
if (SUCCEEDED(hr) && pvRot.vt == VT_R4)
{
start_viewpoint.yaw = pvRot.fltVal;
PropVariantClear(&pvRot);
}
SensorReceiver *received = new(std::nothrow) SensorReceiver(move, start_viewpoint);
if (received)
{
pSensor->SetEventSink(received);
return pSensor.Detach();
}
}
return NULL;
}
void UnhookWindowsSensors(void *vSensor)
{
if (!vSensor)
return;
ISensor *pSensor = static_cast<ISensor*>(vSensor);
pSensor->SetEventSink(NULL);
pSensor->Release();
}
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet am 2026-11-04)
¤
*© Formatika GbR, Deutschland