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 | |
30 | static 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 |
52 | static |
53 | #endif |
54 | SDL_Mutex *SDL_sensor_lock = NULL; // This needs to support recursive locks |
55 | static SDL_AtomicInt SDL_sensor_lock_pending; |
56 | static int SDL_sensors_locked; |
57 | static bool SDL_sensors_initialized; |
58 | static 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 | |
67 | bool SDL_SensorsInitialized(void) |
68 | { |
69 | return SDL_sensors_initialized; |
70 | } |
71 | |
72 | void 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 | |
81 | void 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 | |
113 | bool SDL_SensorsLocked(void) |
114 | { |
115 | return (SDL_sensors_locked > 0); |
116 | } |
117 | |
118 | void SDL_AssertSensorsLocked(void) |
119 | { |
120 | SDL_assert(SDL_SensorsLocked()); |
121 | } |
122 | |
123 | bool 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 | |
157 | bool 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 | |
174 | SDL_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 | */ |
218 | static 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 | */ |
242 | const 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 | |
257 | SDL_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 | |
272 | int 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 | */ |
294 | SDL_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 | */ |
365 | SDL_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 | */ |
382 | SDL_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 | */ |
403 | const 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 | */ |
421 | SDL_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 | */ |
439 | int 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 | */ |
457 | SDL_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 | */ |
475 | bool 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 | */ |
492 | void 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 | |
536 | void 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 | |
562 | void 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 | |
588 | void 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 | |
599 | void 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 | |