Path: blob/master/thirdparty/sdl/sensor/windows/SDL_windowssensor.c
20969 views
/*1Simple DirectMedia Layer2Copyright (C) 1997-2025 Sam Lantinga <[email protected]>34This software is provided 'as-is', without any express or implied5warranty. In no event will the authors be held liable for any damages6arising from the use of this software.78Permission is granted to anyone to use this software for any purpose,9including commercial applications, and to alter it and redistribute it10freely, subject to the following restrictions:11121. The origin of this software must not be misrepresented; you must not13claim that you wrote the original software. If you use this software14in a product, an acknowledgment in the product documentation would be15appreciated but is not required.162. Altered source versions must be plainly marked as such, and must not be17misrepresented as being the original software.183. This notice may not be removed or altered from any source distribution.19*/20#include "SDL_internal.h"2122#ifdef SDL_SENSOR_WINDOWS2324#include "SDL_windowssensor.h"25#include "../SDL_syssensor.h"26#include "../../core/windows/SDL_windows.h"2728#define COBJMACROS29#include <initguid.h>30#include <sensorsapi.h>31#include <sensors.h>3233DEFINE_GUID(SDL_CLSID_SensorManager, 0x77A1C827, 0xFCD2, 0x4689, 0x89, 0x15, 0x9D, 0x61, 0x3C, 0xC5, 0xFA, 0x3E);34DEFINE_GUID(SDL_IID_SensorManager, 0xBD77DB67, 0x45A8, 0x42DC, 0x8D, 0x00, 0x6D, 0xCF, 0x15, 0xF8, 0x37, 0x7A);35DEFINE_GUID(SDL_IID_SensorManagerEvents, 0x9B3B0B86, 0x266A, 0x4AAD, 0xB2, 0x1F, 0xFD, 0xE5, 0x50, 0x10, 0x01, 0xB7);36DEFINE_GUID(SDL_IID_SensorEvents, 0x5D8DCC91, 0x4641, 0x47E7, 0xB7, 0xC3, 0xB7, 0x4F, 0x48, 0xA6, 0xC3, 0x91);3738// These constants aren't available in Visual Studio 2015 or earlier Windows SDK39DEFINE_PROPERTYKEY(SDL_SENSOR_DATA_TYPE_ANGULAR_VELOCITY_X_DEGREES_PER_SECOND, 0X3F8A69A2, 0X7C5, 0X4E48, 0XA9, 0X65, 0XCD, 0X79, 0X7A, 0XAB, 0X56, 0XD5, 10); //[VT_R8]40DEFINE_PROPERTYKEY(SDL_SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Y_DEGREES_PER_SECOND, 0X3F8A69A2, 0X7C5, 0X4E48, 0XA9, 0X65, 0XCD, 0X79, 0X7A, 0XAB, 0X56, 0XD5, 11); //[VT_R8]41DEFINE_PROPERTYKEY(SDL_SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Z_DEGREES_PER_SECOND, 0X3F8A69A2, 0X7C5, 0X4E48, 0XA9, 0X65, 0XCD, 0X79, 0X7A, 0XAB, 0X56, 0XD5, 12); //[VT_R8]4243typedef struct44{45SDL_SensorID id;46ISensor *sensor;47SENSOR_ID sensor_id;48char *name;49SDL_SensorType type;50SDL_Sensor *sensor_opened;5152} SDL_Windows_Sensor;5354static bool SDL_windowscoinit;55static ISensorManager *SDL_sensor_manager;56static int SDL_num_sensors;57static SDL_Windows_Sensor *SDL_sensors;5859static bool ConnectSensor(ISensor *sensor);60static bool DisconnectSensor(ISensor *sensor);6162static HRESULT STDMETHODCALLTYPE ISensorManagerEventsVtbl_QueryInterface(ISensorManagerEvents *This, REFIID riid, void **ppvObject)63{64if (!ppvObject) {65return E_INVALIDARG;66}6768*ppvObject = NULL;69if (WIN_IsEqualIID(riid, &IID_IUnknown) || WIN_IsEqualIID(riid, &SDL_IID_SensorManagerEvents)) {70*ppvObject = This;71return S_OK;72}73return E_NOINTERFACE;74}7576static ULONG STDMETHODCALLTYPE ISensorManagerEventsVtbl_AddRef(ISensorManagerEvents *This)77{78return 1;79}8081static ULONG STDMETHODCALLTYPE ISensorManagerEventsVtbl_Release(ISensorManagerEvents *This)82{83return 1;84}8586static HRESULT STDMETHODCALLTYPE ISensorManagerEventsVtbl_OnSensorEnter(ISensorManagerEvents *This, ISensor *pSensor, SensorState state)87{88ConnectSensor(pSensor);89return S_OK;90}9192static ISensorManagerEventsVtbl sensor_manager_events_vtbl = {93ISensorManagerEventsVtbl_QueryInterface,94ISensorManagerEventsVtbl_AddRef,95ISensorManagerEventsVtbl_Release,96ISensorManagerEventsVtbl_OnSensorEnter97};98static ISensorManagerEvents sensor_manager_events = {99&sensor_manager_events_vtbl100};101102static HRESULT STDMETHODCALLTYPE ISensorEventsVtbl_QueryInterface(ISensorEvents *This, REFIID riid, void **ppvObject)103{104if (!ppvObject) {105return E_INVALIDARG;106}107108*ppvObject = NULL;109if (WIN_IsEqualIID(riid, &IID_IUnknown) || WIN_IsEqualIID(riid, &SDL_IID_SensorEvents)) {110*ppvObject = This;111return S_OK;112}113return E_NOINTERFACE;114}115116static ULONG STDMETHODCALLTYPE ISensorEventsVtbl_AddRef(ISensorEvents *This)117{118return 1;119}120121static ULONG STDMETHODCALLTYPE ISensorEventsVtbl_Release(ISensorEvents *This)122{123return 1;124}125126static HRESULT STDMETHODCALLTYPE ISensorEventsVtbl_OnStateChanged(ISensorEvents *This, ISensor *pSensor, SensorState state)127{128#ifdef DEBUG_SENSORS129int i;130131SDL_LockSensors();132for (i = 0; i < SDL_num_sensors; ++i) {133if (pSensor == SDL_sensors[i].sensor) {134SDL_Log("Sensor %s state changed to %d", SDL_sensors[i].name, state);135}136}137SDL_UnlockSensors();138#endif139return S_OK;140}141142static HRESULT STDMETHODCALLTYPE ISensorEventsVtbl_OnDataUpdated(ISensorEvents *This, ISensor *pSensor, ISensorDataReport *pNewData)143{144int i;145Uint64 timestamp = SDL_GetTicksNS();146147SDL_LockSensors();148for (i = 0; i < SDL_num_sensors; ++i) {149if (pSensor == SDL_sensors[i].sensor) {150if (SDL_sensors[i].sensor_opened) {151HRESULT hrX, hrY, hrZ;152PROPVARIANT valueX = { 0 }, valueY = { 0 }, valueZ = { 0 };153SYSTEMTIME sensor_systemtime;154FILETIME sensor_filetime;155Uint64 sensor_timestamp;156157#ifdef DEBUG_SENSORS158SDL_Log("Sensor %s data updated", SDL_sensors[i].name);159#endif160if (SUCCEEDED(ISensorDataReport_GetTimestamp(pNewData, &sensor_systemtime)) &&161SystemTimeToFileTime(&sensor_systemtime, &sensor_filetime)) {162ULARGE_INTEGER sensor_time;163sensor_time.u.HighPart = sensor_filetime.dwHighDateTime;164sensor_time.u.LowPart = sensor_filetime.dwLowDateTime;165sensor_timestamp = sensor_time.QuadPart * 100;166} else {167sensor_timestamp = timestamp;168}169170switch (SDL_sensors[i].type) {171case SDL_SENSOR_ACCEL:172hrX = ISensorDataReport_GetSensorValue(pNewData, &SENSOR_DATA_TYPE_ACCELERATION_X_G, &valueX);173hrY = ISensorDataReport_GetSensorValue(pNewData, &SENSOR_DATA_TYPE_ACCELERATION_Y_G, &valueY);174hrZ = ISensorDataReport_GetSensorValue(pNewData, &SENSOR_DATA_TYPE_ACCELERATION_Z_G, &valueZ);175if (SUCCEEDED(hrX) && SUCCEEDED(hrY) && SUCCEEDED(hrZ) &&176valueX.vt == VT_R8 && valueY.vt == VT_R8 && valueZ.vt == VT_R8) {177float values[3];178179values[0] = (float)valueX.dblVal * SDL_STANDARD_GRAVITY;180values[1] = (float)valueY.dblVal * SDL_STANDARD_GRAVITY;181values[2] = (float)valueZ.dblVal * SDL_STANDARD_GRAVITY;182SDL_SendSensorUpdate(timestamp, SDL_sensors[i].sensor_opened, sensor_timestamp, values, 3);183}184break;185case SDL_SENSOR_GYRO:186hrX = ISensorDataReport_GetSensorValue(pNewData, &SDL_SENSOR_DATA_TYPE_ANGULAR_VELOCITY_X_DEGREES_PER_SECOND, &valueX);187hrY = ISensorDataReport_GetSensorValue(pNewData, &SDL_SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Y_DEGREES_PER_SECOND, &valueY);188hrZ = ISensorDataReport_GetSensorValue(pNewData, &SDL_SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Z_DEGREES_PER_SECOND, &valueZ);189if (SUCCEEDED(hrX) && SUCCEEDED(hrY) && SUCCEEDED(hrZ) &&190valueX.vt == VT_R8 && valueY.vt == VT_R8 && valueZ.vt == VT_R8) {191const float DEGREES_TO_RADIANS = (SDL_PI_F / 180.0f);192float values[3];193194values[0] = (float)valueX.dblVal * DEGREES_TO_RADIANS;195values[1] = (float)valueY.dblVal * DEGREES_TO_RADIANS;196values[2] = (float)valueZ.dblVal * DEGREES_TO_RADIANS;197SDL_SendSensorUpdate(timestamp, SDL_sensors[i].sensor_opened, sensor_timestamp, values, 3);198}199break;200default:201// FIXME: Need to know how to interpret the data for this sensor202break;203}204}205break;206}207}208SDL_UnlockSensors();209210return S_OK;211}212213static HRESULT STDMETHODCALLTYPE ISensorEventsVtbl_OnEvent(ISensorEvents *This, ISensor *pSensor, REFGUID eventID, IPortableDeviceValues *pEventData)214{215#ifdef DEBUG_SENSORS216int i;217218SDL_LockSensors();219for (i = 0; i < SDL_num_sensors; ++i) {220if (pSensor == SDL_sensors[i].sensor) {221SDL_Log("Sensor %s event occurred", SDL_sensors[i].name);222}223}224SDL_UnlockSensors();225#endif226return S_OK;227}228229static HRESULT STDMETHODCALLTYPE ISensorEventsVtbl_OnLeave(ISensorEvents *This, REFSENSOR_ID ID)230{231int i;232233SDL_LockSensors();234for (i = 0; i < SDL_num_sensors; ++i) {235if (WIN_IsEqualIID(ID, &SDL_sensors[i].sensor_id)) {236#ifdef DEBUG_SENSORS237SDL_Log("Sensor %s disconnected", SDL_sensors[i].name);238#endif239DisconnectSensor(SDL_sensors[i].sensor);240}241}242SDL_UnlockSensors();243244return S_OK;245}246247static ISensorEventsVtbl sensor_events_vtbl = {248ISensorEventsVtbl_QueryInterface,249ISensorEventsVtbl_AddRef,250ISensorEventsVtbl_Release,251ISensorEventsVtbl_OnStateChanged,252ISensorEventsVtbl_OnDataUpdated,253ISensorEventsVtbl_OnEvent,254ISensorEventsVtbl_OnLeave255};256static ISensorEvents sensor_events = {257&sensor_events_vtbl258};259260static bool ConnectSensor(ISensor *sensor)261{262SDL_Windows_Sensor *new_sensor, *new_sensors;263HRESULT hr;264SENSOR_ID sensor_id;265SENSOR_TYPE_ID type_id;266SDL_SensorType type;267BSTR bstr_name = NULL;268char *name;269270hr = ISensor_GetID(sensor, &sensor_id);271if (FAILED(hr)) {272return WIN_SetErrorFromHRESULT("Couldn't get sensor ID", hr);273}274275hr = ISensor_GetType(sensor, &type_id);276if (FAILED(hr)) {277return WIN_SetErrorFromHRESULT("Couldn't get sensor type", hr);278}279280if (WIN_IsEqualIID(&type_id, &SENSOR_TYPE_ACCELEROMETER_3D)) {281type = SDL_SENSOR_ACCEL;282} else if (WIN_IsEqualIID(&type_id, &SENSOR_TYPE_GYROMETER_3D)) {283type = SDL_SENSOR_GYRO;284} else {285return SDL_SetError("Unknown sensor type");286}287288hr = ISensor_GetFriendlyName(sensor, &bstr_name);289if (SUCCEEDED(hr) && bstr_name) {290name = WIN_StringToUTF8W(bstr_name);291} else {292name = SDL_strdup("Unknown Sensor");293}294if (bstr_name != NULL) {295SysFreeString(bstr_name);296}297if (!name) {298return false;299}300301SDL_LockSensors();302new_sensors = (SDL_Windows_Sensor *)SDL_realloc(SDL_sensors, (SDL_num_sensors + 1) * sizeof(SDL_Windows_Sensor));303if (!new_sensors) {304SDL_UnlockSensors();305SDL_free(name);306return false;307}308309ISensor_AddRef(sensor);310ISensor_SetEventSink(sensor, &sensor_events);311312SDL_sensors = new_sensors;313new_sensor = &SDL_sensors[SDL_num_sensors];314++SDL_num_sensors;315316SDL_zerop(new_sensor);317new_sensor->id = SDL_GetNextObjectID();318new_sensor->sensor = sensor;319new_sensor->type = type;320new_sensor->name = name;321322SDL_UnlockSensors();323324return true;325}326327static bool DisconnectSensor(ISensor *sensor)328{329SDL_Windows_Sensor *old_sensor;330int i;331332SDL_LockSensors();333for (i = 0; i < SDL_num_sensors; ++i) {334old_sensor = &SDL_sensors[i];335if (sensor == old_sensor->sensor) {336/* This call hangs for some reason:337* https://github.com/libsdl-org/SDL/issues/5288338*/339// ISensor_SetEventSink(sensor, NULL);340ISensor_Release(sensor);341SDL_free(old_sensor->name);342--SDL_num_sensors;343if (i < SDL_num_sensors) {344SDL_memmove(&SDL_sensors[i], &SDL_sensors[i + 1], (SDL_num_sensors - i) * sizeof(SDL_sensors[i]));345}346break;347}348}349SDL_UnlockSensors();350351return true;352}353354static bool SDL_WINDOWS_SensorInit(void)355{356HRESULT hr;357ISensorCollection *sensor_collection = NULL;358359if (WIN_CoInitialize() == S_OK) {360SDL_windowscoinit = true;361}362363hr = CoCreateInstance(&SDL_CLSID_SensorManager, NULL, CLSCTX_INPROC_SERVER, &SDL_IID_SensorManager, (LPVOID *)&SDL_sensor_manager);364if (FAILED(hr)) {365// If we can't create a sensor manager (i.e. on Wine), we won't have any sensors, but don't fail the init366return true; // WIN_SetErrorFromHRESULT("Couldn't create the sensor manager", hr);367}368369hr = ISensorManager_SetEventSink(SDL_sensor_manager, &sensor_manager_events);370if (FAILED(hr)) {371ISensorManager_Release(SDL_sensor_manager);372SDL_sensor_manager = NULL;373return WIN_SetErrorFromHRESULT("Couldn't set the sensor manager event sink", hr);374}375376hr = ISensorManager_GetSensorsByCategory(SDL_sensor_manager, &SENSOR_CATEGORY_ALL, &sensor_collection);377if (SUCCEEDED(hr)) {378ULONG i, count;379380hr = ISensorCollection_GetCount(sensor_collection, &count);381if (SUCCEEDED(hr)) {382for (i = 0; i < count; ++i) {383ISensor *sensor;384385hr = ISensorCollection_GetAt(sensor_collection, i, &sensor);386if (SUCCEEDED(hr)) {387SensorState state;388389hr = ISensor_GetState(sensor, &state);390if (SUCCEEDED(hr)) {391ISensorManagerEventsVtbl_OnSensorEnter(&sensor_manager_events, sensor, state);392}393ISensorManager_Release(sensor);394}395}396}397ISensorCollection_Release(sensor_collection);398}399return true;400}401402static int SDL_WINDOWS_SensorGetCount(void)403{404return SDL_num_sensors;405}406407static void SDL_WINDOWS_SensorDetect(void)408{409}410411static const char *SDL_WINDOWS_SensorGetDeviceName(int device_index)412{413return SDL_sensors[device_index].name;414}415416static SDL_SensorType SDL_WINDOWS_SensorGetDeviceType(int device_index)417{418return SDL_sensors[device_index].type;419}420421static int SDL_WINDOWS_SensorGetDeviceNonPortableType(int device_index)422{423return -1;424}425426static SDL_SensorID SDL_WINDOWS_SensorGetDeviceInstanceID(int device_index)427{428return SDL_sensors[device_index].id;429}430431static bool SDL_WINDOWS_SensorOpen(SDL_Sensor *sensor, int device_index)432{433SDL_sensors[device_index].sensor_opened = sensor;434return true;435}436437static void SDL_WINDOWS_SensorUpdate(SDL_Sensor *sensor)438{439}440441static void SDL_WINDOWS_SensorClose(SDL_Sensor *sensor)442{443int i;444445for (i = 0; i < SDL_num_sensors; ++i) {446if (sensor == SDL_sensors[i].sensor_opened) {447SDL_sensors[i].sensor_opened = NULL;448break;449}450}451}452453static void SDL_WINDOWS_SensorQuit(void)454{455while (SDL_num_sensors > 0) {456DisconnectSensor(SDL_sensors[0].sensor);457}458459if (SDL_sensor_manager) {460ISensorManager_SetEventSink(SDL_sensor_manager, NULL);461ISensorManager_Release(SDL_sensor_manager);462SDL_sensor_manager = NULL;463}464465if (SDL_windowscoinit) {466WIN_CoUninitialize();467}468}469470SDL_SensorDriver SDL_WINDOWS_SensorDriver = {471SDL_WINDOWS_SensorInit,472SDL_WINDOWS_SensorGetCount,473SDL_WINDOWS_SensorDetect,474SDL_WINDOWS_SensorGetDeviceName,475SDL_WINDOWS_SensorGetDeviceType,476SDL_WINDOWS_SensorGetDeviceNonPortableType,477SDL_WINDOWS_SensorGetDeviceInstanceID,478SDL_WINDOWS_SensorOpen,479SDL_WINDOWS_SensorUpdate,480SDL_WINDOWS_SensorClose,481SDL_WINDOWS_SensorQuit,482};483484#endif // SDL_SENSOR_WINDOWS485486487