1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23// This is the sensor API for Simple DirectMedia Layer
24
25#include "SDL_syssensor.h"
26
27#include "../events/SDL_events_c.h"
28#include "../joystick/SDL_gamepad_c.h"
29
30static SDL_SensorDriver *SDL_sensor_drivers[] = {
31#ifdef SDL_SENSOR_ANDROID
32 &SDL_ANDROID_SensorDriver,
33#endif
34#ifdef SDL_SENSOR_COREMOTION
35 &SDL_COREMOTION_SensorDriver,
36#endif
37#ifdef SDL_SENSOR_WINDOWS
38 &SDL_WINDOWS_SensorDriver,
39#endif
40#ifdef SDL_SENSOR_VITA
41 &SDL_VITA_SensorDriver,
42#endif
43#ifdef SDL_SENSOR_N3DS
44 &SDL_N3DS_SensorDriver,
45#endif
46#if defined(SDL_SENSOR_DUMMY) || defined(SDL_SENSOR_DISABLED)
47 &SDL_DUMMY_SensorDriver
48#endif
49};
50
51#ifndef SDL_THREAD_SAFETY_ANALYSIS
52static
53#endif
54SDL_Mutex *SDL_sensor_lock = NULL; // This needs to support recursive locks
55static SDL_AtomicInt SDL_sensor_lock_pending;
56static int SDL_sensors_locked;
57static bool SDL_sensors_initialized;
58static SDL_Sensor *SDL_sensors SDL_GUARDED_BY(SDL_sensor_lock) = NULL;
59
60#define CHECK_SENSOR_MAGIC(sensor, result) \
61 if (!SDL_ObjectValid(sensor, SDL_OBJECT_TYPE_SENSOR)) { \
62 SDL_InvalidParamError("sensor"); \
63 SDL_UnlockSensors(); \
64 return result; \
65 }
66
67bool SDL_SensorsInitialized(void)
68{
69 return SDL_sensors_initialized;
70}
71
72void SDL_LockSensors(void)
73{
74 (void)SDL_AtomicIncRef(&SDL_sensor_lock_pending);
75 SDL_LockMutex(SDL_sensor_lock);
76 (void)SDL_AtomicDecRef(&SDL_sensor_lock_pending);
77
78 ++SDL_sensors_locked;
79}
80
81void SDL_UnlockSensors(void)
82{
83 bool last_unlock = false;
84
85 --SDL_sensors_locked;
86
87 if (!SDL_sensors_initialized) {
88 // NOTE: There's a small window here where another thread could lock the mutex after we've checked for pending locks
89 if (!SDL_sensors_locked && SDL_GetAtomicInt(&SDL_sensor_lock_pending) == 0) {
90 last_unlock = true;
91 }
92 }
93
94 /* The last unlock after sensors are uninitialized will cleanup the mutex,
95 * allowing applications to lock sensors while reinitializing the system.
96 */
97 if (last_unlock) {
98 SDL_Mutex *sensor_lock = SDL_sensor_lock;
99
100 SDL_LockMutex(sensor_lock);
101 {
102 SDL_UnlockMutex(SDL_sensor_lock);
103
104 SDL_sensor_lock = NULL;
105 }
106 SDL_UnlockMutex(sensor_lock);
107 SDL_DestroyMutex(sensor_lock);
108 } else {
109 SDL_UnlockMutex(SDL_sensor_lock);
110 }
111}
112
113bool SDL_SensorsLocked(void)
114{
115 return (SDL_sensors_locked > 0);
116}
117
118void SDL_AssertSensorsLocked(void)
119{
120 SDL_assert(SDL_SensorsLocked());
121}
122
123bool SDL_InitSensors(void)
124{
125 int i;
126 bool status;
127
128 // Create the sensor list lock
129 if (SDL_sensor_lock == NULL) {
130 SDL_sensor_lock = SDL_CreateMutex();
131 }
132
133 if (!SDL_InitSubSystem(SDL_INIT_EVENTS)) {
134 return false;
135 }
136
137 SDL_LockSensors();
138
139 SDL_sensors_initialized = true;
140
141 status = false;
142 for (i = 0; i < SDL_arraysize(SDL_sensor_drivers); ++i) {
143 if (SDL_sensor_drivers[i]->Init()) {
144 status = true;
145 }
146 }
147
148 SDL_UnlockSensors();
149
150 if (!status) {
151 SDL_QuitSensors();
152 }
153
154 return status;
155}
156
157bool SDL_SensorsOpened(void)
158{
159 bool opened;
160
161 SDL_LockSensors();
162 {
163 if (SDL_sensors != NULL) {
164 opened = true;
165 } else {
166 opened = false;
167 }
168 }
169 SDL_UnlockSensors();
170
171 return opened;
172}
173
174SDL_SensorID *SDL_GetSensors(int *count)
175{
176 int i, num_sensors, device_index;
177 int sensor_index = 0, total_sensors = 0;
178 SDL_SensorID *sensors;
179
180 SDL_LockSensors();
181 {
182 for (i = 0; i < SDL_arraysize(SDL_sensor_drivers); ++i) {
183 total_sensors += SDL_sensor_drivers[i]->GetCount();
184 }
185
186 sensors = (SDL_SensorID *)SDL_malloc((total_sensors + 1) * sizeof(*sensors));
187 if (sensors) {
188 if (count) {
189 *count = total_sensors;
190 }
191
192 for (i = 0; i < SDL_arraysize(SDL_sensor_drivers); ++i) {
193 num_sensors = SDL_sensor_drivers[i]->GetCount();
194 for (device_index = 0; device_index < num_sensors; ++device_index) {
195 SDL_assert(sensor_index < total_sensors);
196 sensors[sensor_index] = SDL_sensor_drivers[i]->GetDeviceInstanceID(device_index);
197 SDL_assert(sensors[sensor_index] > 0);
198 ++sensor_index;
199 }
200 }
201 SDL_assert(sensor_index == total_sensors);
202 sensors[sensor_index] = 0;
203 } else {
204 if (count) {
205 *count = 0;
206 }
207 }
208 }
209 SDL_UnlockSensors();
210
211 return sensors;
212}
213
214/*
215 * Get the driver and device index for a sensor instance ID
216 * This should be called while the sensor lock is held, to prevent another thread from updating the list
217 */
218static bool SDL_GetDriverAndSensorIndex(SDL_SensorID instance_id, SDL_SensorDriver **driver, int *driver_index)
219{
220 int i, num_sensors, device_index;
221
222 if (instance_id > 0) {
223 for (i = 0; i < SDL_arraysize(SDL_sensor_drivers); ++i) {
224 num_sensors = SDL_sensor_drivers[i]->GetCount();
225 for (device_index = 0; device_index < num_sensors; ++device_index) {
226 SDL_SensorID sensor_id = SDL_sensor_drivers[i]->GetDeviceInstanceID(device_index);
227 if (sensor_id == instance_id) {
228 *driver = SDL_sensor_drivers[i];
229 *driver_index = device_index;
230 return true;
231 }
232 }
233 }
234 }
235 SDL_SetError("Sensor %" SDL_PRIu32 " not found", instance_id);
236 return false;
237}
238
239/*
240 * Get the implementation dependent name of a sensor
241 */
242const char *SDL_GetSensorNameForID(SDL_SensorID instance_id)
243{
244 SDL_SensorDriver *driver;
245 int device_index;
246 const char *name = NULL;
247
248 SDL_LockSensors();
249 if (SDL_GetDriverAndSensorIndex(instance_id, &driver, &device_index)) {
250 name = SDL_GetPersistentString(driver->GetDeviceName(device_index));
251 }
252 SDL_UnlockSensors();
253
254 return name;
255}
256
257SDL_SensorType SDL_GetSensorTypeForID(SDL_SensorID instance_id)
258{
259 SDL_SensorDriver *driver;
260 int device_index;
261 SDL_SensorType type = SDL_SENSOR_INVALID;
262
263 SDL_LockSensors();
264 if (SDL_GetDriverAndSensorIndex(instance_id, &driver, &device_index)) {
265 type = driver->GetDeviceType(device_index);
266 }
267 SDL_UnlockSensors();
268
269 return type;
270}
271
272int SDL_GetSensorNonPortableTypeForID(SDL_SensorID instance_id)
273{
274 SDL_SensorDriver *driver;
275 int device_index;
276 int type = -1;
277
278 SDL_LockSensors();
279 if (SDL_GetDriverAndSensorIndex(instance_id, &driver, &device_index)) {
280 type = driver->GetDeviceNonPortableType(device_index);
281 }
282 SDL_UnlockSensors();
283
284 return type;
285}
286
287/*
288 * Open a sensor for use - the index passed as an argument refers to
289 * the N'th sensor on the system. This index is the value which will
290 * identify this sensor in future sensor events.
291 *
292 * This function returns a sensor identifier, or NULL if an error occurred.
293 */
294SDL_Sensor *SDL_OpenSensor(SDL_SensorID instance_id)
295{
296 SDL_SensorDriver *driver;
297 int device_index;
298 SDL_Sensor *sensor;
299 SDL_Sensor *sensorlist;
300 const char *sensorname = NULL;
301
302 SDL_LockSensors();
303
304 if (!SDL_GetDriverAndSensorIndex(instance_id, &driver, &device_index)) {
305 SDL_UnlockSensors();
306 return NULL;
307 }
308
309 sensorlist = SDL_sensors;
310 /* If the sensor is already open, return it
311 * it is important that we have a single sensor * for each instance id
312 */
313 while (sensorlist) {
314 if (instance_id == sensorlist->instance_id) {
315 sensor = sensorlist;
316 ++sensor->ref_count;
317 SDL_UnlockSensors();
318 return sensor;
319 }
320 sensorlist = sensorlist->next;
321 }
322
323 // Create and initialize the sensor
324 sensor = (SDL_Sensor *)SDL_calloc(1, sizeof(*sensor));
325 if (!sensor) {
326 SDL_UnlockSensors();
327 return NULL;
328 }
329 SDL_SetObjectValid(sensor, SDL_OBJECT_TYPE_SENSOR, true);
330 sensor->driver = driver;
331 sensor->instance_id = instance_id;
332 sensor->type = driver->GetDeviceType(device_index);
333 sensor->non_portable_type = driver->GetDeviceNonPortableType(device_index);
334
335 if (!driver->Open(sensor, device_index)) {
336 SDL_SetObjectValid(sensor, SDL_OBJECT_TYPE_SENSOR, false);
337 SDL_free(sensor);
338 SDL_UnlockSensors();
339 return NULL;
340 }
341
342 sensorname = driver->GetDeviceName(device_index);
343 if (sensorname) {
344 sensor->name = SDL_strdup(sensorname);
345 } else {
346 sensor->name = NULL;
347 }
348
349 // Add sensor to list
350 ++sensor->ref_count;
351 // Link the sensor in the list
352 sensor->next = SDL_sensors;
353 SDL_sensors = sensor;
354
355 driver->Update(sensor);
356
357 SDL_UnlockSensors();
358
359 return sensor;
360}
361
362/*
363 * Find the SDL_Sensor that owns this instance id
364 */
365SDL_Sensor *SDL_GetSensorFromID(SDL_SensorID instance_id)
366{
367 SDL_Sensor *sensor;
368
369 SDL_LockSensors();
370 for (sensor = SDL_sensors; sensor; sensor = sensor->next) {
371 if (sensor->instance_id == instance_id) {
372 break;
373 }
374 }
375 SDL_UnlockSensors();
376 return sensor;
377}
378
379/*
380 * Get the properties associated with a sensor.
381 */
382SDL_PropertiesID SDL_GetSensorProperties(SDL_Sensor *sensor)
383{
384 SDL_PropertiesID result;
385
386 SDL_LockSensors();
387 {
388 CHECK_SENSOR_MAGIC(sensor, 0);
389
390 if (sensor->props == 0) {
391 sensor->props = SDL_CreateProperties();
392 }
393 result = sensor->props;
394 }
395 SDL_UnlockSensors();
396
397 return result;
398}
399
400/*
401 * Get the friendly name of this sensor
402 */
403const char *SDL_GetSensorName(SDL_Sensor *sensor)
404{
405 const char *result;
406
407 SDL_LockSensors();
408 {
409 CHECK_SENSOR_MAGIC(sensor, NULL);
410
411 result = SDL_GetPersistentString(sensor->name);
412 }
413 SDL_UnlockSensors();
414
415 return result;
416}
417
418/*
419 * Get the type of this sensor
420 */
421SDL_SensorType SDL_GetSensorType(SDL_Sensor *sensor)
422{
423 SDL_SensorType result;
424
425 SDL_LockSensors();
426 {
427 CHECK_SENSOR_MAGIC(sensor, SDL_SENSOR_INVALID);
428
429 result = sensor->type;
430 }
431 SDL_UnlockSensors();
432
433 return result;
434}
435
436/*
437 * Get the platform dependent type of this sensor
438 */
439int SDL_GetSensorNonPortableType(SDL_Sensor *sensor)
440{
441 int result;
442
443 SDL_LockSensors();
444 {
445 CHECK_SENSOR_MAGIC(sensor, -1);
446
447 result = sensor->non_portable_type;
448 }
449 SDL_UnlockSensors();
450
451 return result;
452}
453
454/*
455 * Get the instance id for this opened sensor
456 */
457SDL_SensorID SDL_GetSensorID(SDL_Sensor *sensor)
458{
459 SDL_SensorID result;
460
461 SDL_LockSensors();
462 {
463 CHECK_SENSOR_MAGIC(sensor, 0);
464
465 result = sensor->instance_id;
466 }
467 SDL_UnlockSensors();
468
469 return result;
470}
471
472/*
473 * Get the current state of this sensor
474 */
475bool SDL_GetSensorData(SDL_Sensor *sensor, float *data, int num_values)
476{
477 SDL_LockSensors();
478 {
479 CHECK_SENSOR_MAGIC(sensor, false);
480
481 num_values = SDL_min(num_values, SDL_arraysize(sensor->data));
482 SDL_memcpy(data, sensor->data, num_values * sizeof(*data));
483 }
484 SDL_UnlockSensors();
485
486 return true;
487}
488
489/*
490 * Close a sensor previously opened with SDL_OpenSensor()
491 */
492void SDL_CloseSensor(SDL_Sensor *sensor)
493{
494 SDL_Sensor *sensorlist;
495 SDL_Sensor *sensorlistprev;
496
497 SDL_LockSensors();
498 {
499 CHECK_SENSOR_MAGIC(sensor,);
500
501 // First decrement ref count
502 if (--sensor->ref_count > 0) {
503 SDL_UnlockSensors();
504 return;
505 }
506
507 SDL_DestroyProperties(sensor->props);
508
509 sensor->driver->Close(sensor);
510 sensor->hwdata = NULL;
511 SDL_SetObjectValid(sensor, SDL_OBJECT_TYPE_SENSOR, false);
512
513 sensorlist = SDL_sensors;
514 sensorlistprev = NULL;
515 while (sensorlist) {
516 if (sensor == sensorlist) {
517 if (sensorlistprev) {
518 // unlink this entry
519 sensorlistprev->next = sensorlist->next;
520 } else {
521 SDL_sensors = sensor->next;
522 }
523 break;
524 }
525 sensorlistprev = sensorlist;
526 sensorlist = sensorlist->next;
527 }
528
529 // Free the data associated with this sensor
530 SDL_free(sensor->name);
531 SDL_free(sensor);
532 }
533 SDL_UnlockSensors();
534}
535
536void SDL_QuitSensors(void)
537{
538 int i;
539
540 SDL_LockSensors();
541
542 // Stop the event polling
543 while (SDL_sensors) {
544 SDL_sensors->ref_count = 1;
545 SDL_CloseSensor(SDL_sensors);
546 }
547
548 // Quit the sensor setup
549 for (i = 0; i < SDL_arraysize(SDL_sensor_drivers); ++i) {
550 SDL_sensor_drivers[i]->Quit();
551 }
552
553 SDL_QuitSubSystem(SDL_INIT_EVENTS);
554
555 SDL_sensors_initialized = false;
556
557 SDL_UnlockSensors();
558}
559
560// These are global for SDL_syssensor.c and SDL_events.c
561
562void SDL_SendSensorUpdate(Uint64 timestamp, SDL_Sensor *sensor, Uint64 sensor_timestamp, float *data, int num_values)
563{
564 SDL_AssertSensorsLocked();
565
566 // Allow duplicate events, for things like steps and heartbeats
567
568 // Update internal sensor state
569 num_values = SDL_min(num_values, SDL_arraysize(sensor->data));
570 SDL_memcpy(sensor->data, data, num_values * sizeof(*data));
571
572 // Post the event, if desired
573 if (SDL_EventEnabled(SDL_EVENT_SENSOR_UPDATE)) {
574 SDL_Event event;
575 event.type = SDL_EVENT_SENSOR_UPDATE;
576 event.common.timestamp = timestamp;
577 event.sensor.which = sensor->instance_id;
578 num_values = SDL_min(num_values, SDL_arraysize(event.sensor.data));
579 SDL_memset(event.sensor.data, 0, sizeof(event.sensor.data));
580 SDL_memcpy(event.sensor.data, data, num_values * sizeof(*data));
581 event.sensor.sensor_timestamp = sensor_timestamp;
582 SDL_PushEvent(&event);
583 }
584
585 SDL_GamepadSensorWatcher(timestamp, sensor->instance_id, sensor_timestamp, data, num_values);
586}
587
588void SDL_UpdateSensor(SDL_Sensor *sensor)
589{
590 SDL_LockSensors();
591 {
592 CHECK_SENSOR_MAGIC(sensor,);
593
594 sensor->driver->Update(sensor);
595 }
596 SDL_UnlockSensors();
597}
598
599void SDL_UpdateSensors(void)
600{
601 int i;
602 SDL_Sensor *sensor;
603
604 if (!SDL_WasInit(SDL_INIT_SENSOR)) {
605 return;
606 }
607
608 SDL_LockSensors();
609
610 for (sensor = SDL_sensors; sensor; sensor = sensor->next) {
611 sensor->driver->Update(sensor);
612 }
613
614 /* this needs to happen AFTER walking the sensor list above, so that any
615 dangling hardware data from removed devices can be free'd
616 */
617 for (i = 0; i < SDL_arraysize(SDL_sensor_drivers); ++i) {
618 SDL_sensor_drivers[i]->Detect();
619 }
620
621 SDL_UnlockSensors();
622}
623