1 // Copyright 2013, Fredrik Hultin.
2 // Copyright 2013, Jakob Bornecrantz.
3 // SPDX-License-Identifier: BSL-1.0
4 /*
5 * OpenHMD - Free and Open Source API and drivers for immersive technology.
6 */
7
8 /* Main Lib Implementation */
9
10
11 #include "openhmdi.h"
12 #include "shaders.h"
13 #include <stdlib.h>
14 #include <string.h>
15 #include <stdio.h>
16
17 // Running automatic updates at 1000 Hz
18 #define AUTOMATIC_UPDATE_SLEEP (1.0 / 1000.0)
19
ohmd_ctx_create(void)20 OHMD_APIENTRYDLL ohmd_context* OHMD_APIENTRY ohmd_ctx_create(void)
21 {
22 ohmd_context* ctx = calloc(1, sizeof(ohmd_context));
23 if(!ctx){
24 LOGE("could not allocate RAM for context");
25 return NULL;
26 }
27
28 ohmd_monotonic_init(ctx);
29
30 #if DRIVER_OCULUS_RIFT
31 ctx->drivers[ctx->num_drivers++] = ohmd_create_oculus_rift_drv(ctx);
32 #endif
33
34 #if DRIVER_DEEPOON
35 ctx->drivers[ctx->num_drivers++] = ohmd_create_deepoon_drv(ctx);
36 #endif
37
38 #if DRIVER_HTC_VIVE
39 ctx->drivers[ctx->num_drivers++] = ohmd_create_htc_vive_drv(ctx);
40 #endif
41
42 #if DRIVER_WMR
43 ctx->drivers[ctx->num_drivers++] = ohmd_create_wmr_drv(ctx);
44 #endif
45
46 #if DRIVER_PSVR
47 ctx->drivers[ctx->num_drivers++] = ohmd_create_psvr_drv(ctx);
48 #endif
49
50 #if DRIVER_NOLO
51 ctx->drivers[ctx->num_drivers++] = ohmd_create_nolo_drv(ctx);
52 #endif
53
54 #if DRIVER_XGVR
55 ctx->drivers[ctx->num_drivers++] = ohmd_create_xgvr_drv(ctx);
56 #endif
57
58 #if DRIVER_VRTEK
59 ctx->drivers[ctx->num_drivers++] = ohmd_create_vrtek_drv(ctx);
60 #endif
61
62 #if DRIVER_ANDROID
63 ctx->drivers[ctx->num_drivers++] = ohmd_create_android_drv(ctx);
64 #endif
65
66 #if DRIVER_EXTERNAL
67 ctx->drivers[ctx->num_drivers++] = ohmd_create_external_drv(ctx);
68 #endif
69 // add dummy driver last to make it the lowest priority
70
71 ctx->update_request_quit = false;
72
73 return ctx;
74 }
75
ohmd_ctx_destroy(ohmd_context * ctx)76 OHMD_APIENTRYDLL void OHMD_APIENTRY ohmd_ctx_destroy(ohmd_context* ctx)
77 {
78 ctx->update_request_quit = true;
79
80 for(int i = 0; i < ctx->num_active_devices; i++){
81 ctx->active_devices[i]->close(ctx->active_devices[i]);
82 }
83
84 for(int i = 0; i < ctx->num_drivers; i++){
85 ctx->drivers[i]->destroy(ctx->drivers[i]);
86 }
87
88 if(ctx->update_thread){
89 ohmd_destroy_thread(ctx->update_thread);
90 ohmd_destroy_mutex(ctx->update_mutex);
91 }
92
93 free(ctx);
94 }
95
ohmd_ctx_update(ohmd_context * ctx)96 OHMD_APIENTRYDLL void OHMD_APIENTRY ohmd_ctx_update(ohmd_context* ctx)
97 {
98 for(int i = 0; i < ctx->num_active_devices; i++){
99 ohmd_device* dev = ctx->active_devices[i];
100 if(!dev->settings.automatic_update && dev->update)
101 dev->update(dev);
102
103 ohmd_lock_mutex(ctx->update_mutex);
104 dev->getf(dev, OHMD_POSITION_VECTOR, (float*)&dev->position);
105 dev->getf(dev, OHMD_ROTATION_QUAT, (float*)&dev->rotation);
106 ohmd_unlock_mutex(ctx->update_mutex);
107 }
108 }
109
ohmd_ctx_get_error(ohmd_context * ctx)110 OHMD_APIENTRYDLL const char* OHMD_APIENTRY ohmd_ctx_get_error(ohmd_context* ctx)
111 {
112 return ctx->error_msg;
113 }
114
ohmd_ctx_probe(ohmd_context * ctx)115 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_ctx_probe(ohmd_context* ctx)
116 {
117 memset(&ctx->list, 0, sizeof(ohmd_device_list));
118 for(int i = 0; i < ctx->num_drivers; i++){
119 ctx->drivers[i]->get_device_list(ctx->drivers[i], &ctx->list);
120 }
121
122 return ctx->list.num_devices;
123 }
124
ohmd_gets(ohmd_string_description type,const char ** out)125 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_gets(ohmd_string_description type, const char ** out)
126 {
127 switch(type){
128 case OHMD_GLSL_DISTORTION_VERT_SRC:
129 *out = distortion_vert;
130 return OHMD_S_OK;
131 case OHMD_GLSL_DISTORTION_FRAG_SRC:
132 *out = distortion_frag;
133 return OHMD_S_OK;
134 case OHMD_GLSL_330_DISTORTION_VERT_SRC:
135 *out = distortion_vert_330;
136 return OHMD_S_OK;
137 case OHMD_GLSL_330_DISTORTION_FRAG_SRC:
138 *out = distortion_frag_330;
139 return OHMD_S_OK;
140 case OHMD_GLSL_ES_DISTORTION_VERT_SRC:
141 *out = distortion_vert_es;
142 return OHMD_S_OK;
143 case OHMD_GLSL_ES_DISTORTION_FRAG_SRC:
144 *out = distortion_frag_es;
145 return OHMD_S_OK;
146 default:
147 return OHMD_S_UNSUPPORTED;
148 }
149 }
150
ohmd_list_gets(ohmd_context * ctx,int index,ohmd_string_value type)151 OHMD_APIENTRYDLL const char* OHMD_APIENTRY ohmd_list_gets(ohmd_context* ctx, int index, ohmd_string_value type)
152 {
153 if(index >= ctx->list.num_devices)
154 return NULL;
155
156 switch(type){
157 case OHMD_VENDOR:
158 return ctx->list.devices[index].vendor;
159 case OHMD_PRODUCT:
160 return ctx->list.devices[index].product;
161 case OHMD_PATH:
162 return ctx->list.devices[index].path;
163 default:
164 return NULL;
165 }
166 }
167
ohmd_list_geti(ohmd_context * ctx,int index,ohmd_int_value type,int * out)168 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_list_geti(ohmd_context* ctx, int index, ohmd_int_value type, int* out)
169 {
170 if(index >= ctx->list.num_devices)
171 return OHMD_S_INVALID_PARAMETER;
172
173 switch(type){
174 case OHMD_DEVICE_CLASS:
175 *out = ctx->list.devices[index].device_class;
176 return OHMD_S_OK;
177
178 case OHMD_DEVICE_FLAGS:
179 *out = ctx->list.devices[index].device_flags;
180 return OHMD_S_OK;
181
182 default:
183 return OHMD_S_INVALID_PARAMETER;
184 }
185 }
186
ohmd_update_thread(void * arg)187 static unsigned int ohmd_update_thread(void* arg)
188 {
189 ohmd_context* ctx = (ohmd_context*)arg;
190
191 while(!ctx->update_request_quit)
192 {
193 ohmd_lock_mutex(ctx->update_mutex);
194
195 for(int i = 0; i < ctx->num_active_devices; i++){
196 if(ctx->active_devices[i]->settings.automatic_update && ctx->active_devices[i]->update)
197 ctx->active_devices[i]->update(ctx->active_devices[i]);
198 }
199
200 ohmd_unlock_mutex(ctx->update_mutex);
201
202 ohmd_sleep(AUTOMATIC_UPDATE_SLEEP);
203 }
204
205 return 0;
206 }
207
ohmd_set_up_update_thread(ohmd_context * ctx)208 static void ohmd_set_up_update_thread(ohmd_context* ctx)
209 {
210 if(!ctx->update_thread){
211 ctx->update_mutex = ohmd_create_mutex(ctx);
212 ctx->update_thread = ohmd_create_thread(ctx, ohmd_update_thread, ctx);
213 }
214 }
215
ohmd_list_open_device_s(ohmd_context * ctx,int index,ohmd_device_settings * settings)216 OHMD_APIENTRYDLL ohmd_device* OHMD_APIENTRY ohmd_list_open_device_s(ohmd_context* ctx, int index, ohmd_device_settings* settings)
217 {
218 ohmd_lock_mutex(ctx->update_mutex);
219
220 if(index >= 0 && index < ctx->list.num_devices){
221
222 ohmd_device_desc* desc = &ctx->list.devices[index];
223 ohmd_driver* driver = (ohmd_driver*)desc->driver_ptr;
224 ohmd_device* device = driver->open_device(driver, desc);
225
226 if (device == NULL) {
227 ohmd_set_error(ctx, "Could not open device with index: %d, check device permissions?", index);
228 ohmd_unlock_mutex(ctx->update_mutex);
229 return NULL;
230 }
231
232 device->rotation_correction.w = 1;
233
234 device->settings = *settings;
235
236 device->ctx = ctx;
237 device->active_device_idx = ctx->num_active_devices;
238 ctx->active_devices[ctx->num_active_devices++] = device;
239
240 ohmd_unlock_mutex(ctx->update_mutex);
241
242 if(device->settings.automatic_update)
243 ohmd_set_up_update_thread(ctx);
244
245 return device;
246 }
247
248 ohmd_unlock_mutex(ctx->update_mutex);
249
250 ohmd_set_error(ctx, "no device with index: %d", index);
251 return NULL;
252 }
253
ohmd_list_open_device(ohmd_context * ctx,int index)254 OHMD_APIENTRYDLL ohmd_device* OHMD_APIENTRY ohmd_list_open_device(ohmd_context* ctx, int index)
255 {
256 ohmd_device_settings settings;
257
258 settings.automatic_update = true;
259
260 return ohmd_list_open_device_s(ctx, index, &settings);
261 }
262
ohmd_close_device(ohmd_device * device)263 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_close_device(ohmd_device* device)
264 {
265 ohmd_lock_mutex(device->ctx->update_mutex);
266
267 ohmd_context* ctx = device->ctx;
268 int idx = device->active_device_idx;
269
270 memmove(ctx->active_devices + idx, ctx->active_devices + idx + 1,
271 sizeof(ohmd_device*) * (ctx->num_active_devices - idx - 1));
272
273 device->close(device);
274
275 ctx->num_active_devices--;
276
277 for(int i = idx; i < ctx->num_active_devices; i++)
278 ctx->active_devices[i]->active_device_idx--;
279
280 ohmd_unlock_mutex(ctx->update_mutex);
281
282 return OHMD_S_OK;
283 }
284
ohmd_device_getf_unp(ohmd_device * device,ohmd_float_value type,float * out)285 static int ohmd_device_getf_unp(ohmd_device* device, ohmd_float_value type, float* out)
286 {
287 switch(type){
288 case OHMD_LEFT_EYE_GL_MODELVIEW_MATRIX: {
289 quatf rot = device->rotation;
290 oquatf_mult_me(&rot, &device->rotation_correction);
291 mat4x4f central_view, eye_shift, result;
292 omat4x4f_init_look_at(¢ral_view, &rot, &device->position);
293 omat4x4f_init_translate(&eye_shift, +(device->properties.ipd / 2.0f), 0.0f, 0.0f);
294 omat4x4f_mult(&eye_shift, ¢ral_view, &result);
295 omat4x4f_transpose(&result, (mat4x4f*)out);
296 return OHMD_S_OK;
297 }
298 case OHMD_RIGHT_EYE_GL_MODELVIEW_MATRIX: {
299 quatf rot = device->rotation;
300 oquatf_mult_me(&rot, &device->rotation_correction);
301 mat4x4f central_view, eye_shift, result;
302 omat4x4f_init_look_at(¢ral_view, &rot, &device->position);
303 omat4x4f_init_translate(&eye_shift, -(device->properties.ipd / 2.0f), 0.0f, 0.0f);
304 omat4x4f_mult(&eye_shift, ¢ral_view, &result);
305 omat4x4f_transpose(&result, (mat4x4f*)out);
306 return OHMD_S_OK;
307 }
308 case OHMD_LEFT_EYE_GL_PROJECTION_MATRIX:
309 omat4x4f_transpose(&device->properties.proj_left, (mat4x4f*)out);
310 return OHMD_S_OK;
311 case OHMD_RIGHT_EYE_GL_PROJECTION_MATRIX:
312 omat4x4f_transpose(&device->properties.proj_right, (mat4x4f*)out);
313 return OHMD_S_OK;
314
315 case OHMD_SCREEN_HORIZONTAL_SIZE:
316 *out = device->properties.hsize;
317 return OHMD_S_OK;
318 case OHMD_SCREEN_VERTICAL_SIZE:
319 *out = device->properties.vsize;
320 return OHMD_S_OK;
321
322 case OHMD_LENS_HORIZONTAL_SEPARATION:
323 *out = device->properties.lens_sep;
324 return OHMD_S_OK;
325 case OHMD_LENS_VERTICAL_POSITION:
326 *out = device->properties.lens_vpos;
327 return OHMD_S_OK;
328
329 case OHMD_RIGHT_EYE_FOV:
330 case OHMD_LEFT_EYE_FOV:
331 *out = device->properties.fov;
332 return OHMD_S_OK;
333 case OHMD_RIGHT_EYE_ASPECT_RATIO:
334 case OHMD_LEFT_EYE_ASPECT_RATIO:
335 *out = device->properties.ratio;
336 return OHMD_S_OK;
337
338 case OHMD_EYE_IPD:
339 *out = device->properties.ipd;
340 return OHMD_S_OK;
341
342 case OHMD_PROJECTION_ZFAR:
343 *out = device->properties.zfar;
344 return OHMD_S_OK;
345 case OHMD_PROJECTION_ZNEAR:
346 *out = device->properties.znear;
347 return OHMD_S_OK;
348
349 case OHMD_ROTATION_QUAT:
350 {
351 *(quatf*)out = device->rotation;
352
353 oquatf_mult_me((quatf*)out, &device->rotation_correction);
354 return OHMD_S_OK;
355 }
356 case OHMD_POSITION_VECTOR:
357 {
358 *(vec3f*)out = device->position;
359 for(int i = 0; i < 3; i++)
360 out[i] += device->position_correction.arr[i];
361
362 return OHMD_S_OK;
363 }
364 case OHMD_UNIVERSAL_DISTORTION_K: {
365 for (int i = 0; i < 4; i++) {
366 out[i] = device->properties.universal_distortion_k[i];
367 }
368 return OHMD_S_OK;
369 }
370 case OHMD_UNIVERSAL_ABERRATION_K: {
371 for (int i = 0; i < 3; i++) {
372 out[i] = device->properties.universal_aberration_k[i];
373 }
374 return OHMD_S_OK;
375 }
376 default:
377 return device->getf(device, type, out);
378 }
379 }
380
ohmd_device_getf(ohmd_device * device,ohmd_float_value type,float * out)381 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_getf(ohmd_device* device, ohmd_float_value type, float* out)
382 {
383 ohmd_lock_mutex(device->ctx->update_mutex);
384 int ret = ohmd_device_getf_unp(device, type, out);
385 ohmd_unlock_mutex(device->ctx->update_mutex);
386
387 return ret;
388 }
389
ohmd_device_setf_unp(ohmd_device * device,ohmd_float_value type,const float * in)390 int ohmd_device_setf_unp(ohmd_device* device, ohmd_float_value type, const float* in)
391 {
392 switch(type){
393 case OHMD_EYE_IPD:
394 device->properties.ipd = *in;
395 return OHMD_S_OK;
396 case OHMD_PROJECTION_ZFAR:
397 device->properties.zfar = *in;
398 return OHMD_S_OK;
399 case OHMD_PROJECTION_ZNEAR:
400 device->properties.znear = *in;
401 return OHMD_S_OK;
402 case OHMD_ROTATION_QUAT:
403 {
404 // adjust rotation correction
405 quatf q;
406 int ret = device->getf(device, OHMD_ROTATION_QUAT, (float*)&q);
407
408 if(ret != 0){
409 return ret;
410 }
411
412 oquatf_diff(&q, (quatf*)in, &device->rotation_correction);
413 return OHMD_S_OK;
414 }
415 case OHMD_POSITION_VECTOR:
416 {
417 // adjust position correction
418 vec3f v;
419 int ret = device->getf(device, OHMD_POSITION_VECTOR, (float*)&v);
420
421 if(ret != 0){
422 return ret;
423 }
424
425 for(int i = 0; i < 3; i++)
426 device->position_correction.arr[i] = in[i] - v.arr[i];
427
428 return OHMD_S_OK;
429 }
430 case OHMD_EXTERNAL_SENSOR_FUSION:
431 {
432 if(device->setf == NULL)
433 return OHMD_S_UNSUPPORTED;
434
435 return device->setf(device, type, in);
436 }
437 default:
438 return OHMD_S_INVALID_PARAMETER;
439 }
440 }
441
ohmd_device_setf(ohmd_device * device,ohmd_float_value type,const float * in)442 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_setf(ohmd_device* device, ohmd_float_value type, const float* in)
443 {
444 ohmd_lock_mutex(device->ctx->update_mutex);
445 int ret = ohmd_device_setf_unp(device, type, in);
446 ohmd_unlock_mutex(device->ctx->update_mutex);
447
448 return ret;
449 }
450
ohmd_device_geti(ohmd_device * device,ohmd_int_value type,int * out)451 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_geti(ohmd_device* device, ohmd_int_value type, int* out)
452 {
453 switch(type){
454 case OHMD_SCREEN_HORIZONTAL_RESOLUTION:
455 *out = device->properties.hres;
456 return OHMD_S_OK;
457
458 case OHMD_SCREEN_VERTICAL_RESOLUTION:
459 *out = device->properties.vres;
460 return OHMD_S_OK;
461
462 case OHMD_CONTROL_COUNT:
463 *out = device->properties.control_count;
464 return OHMD_S_OK;
465
466 case OHMD_CONTROLS_TYPES:
467 memcpy(out, device->properties.controls_types, device->properties.control_count * sizeof(int));
468 return OHMD_S_OK;
469
470 case OHMD_CONTROLS_HINTS:
471 memcpy(out, device->properties.controls_hints, device->properties.control_count * sizeof(int));
472 return OHMD_S_OK;
473
474 default:
475 return OHMD_S_INVALID_PARAMETER;
476 }
477 }
478
ohmd_device_seti(ohmd_device * device,ohmd_int_value type,const int * in)479 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_seti(ohmd_device* device, ohmd_int_value type, const int* in)
480 {
481 switch(type){
482 default:
483 return OHMD_S_INVALID_PARAMETER;
484 }
485 }
486
487
ohmd_device_set_data_unp(ohmd_device * device,ohmd_data_value type,const void * in)488 int ohmd_device_set_data_unp(ohmd_device* device, ohmd_data_value type, const void* in)
489 {
490 switch(type){
491 case OHMD_DRIVER_DATA:
492 device->set_data(device, OHMD_DRIVER_DATA, in);
493 return OHMD_S_OK;
494
495 case OHMD_DRIVER_PROPERTIES:
496 device->set_data(device, OHMD_DRIVER_PROPERTIES, in);
497 return OHMD_S_OK;
498
499 default:
500 return OHMD_S_INVALID_PARAMETER;
501 }
502 }
503
ohmd_device_set_data(ohmd_device * device,ohmd_data_value type,const void * in)504 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_set_data(ohmd_device* device, ohmd_data_value type, const void* in)
505 {
506 ohmd_lock_mutex(device->ctx->update_mutex);
507 int ret = ohmd_device_set_data_unp(device, type, in);
508 ohmd_unlock_mutex(device->ctx->update_mutex);
509
510 return ret;
511 }
512
ohmd_device_settings_seti(ohmd_device_settings * settings,ohmd_int_settings key,const int * val)513 OHMD_APIENTRYDLL ohmd_status OHMD_APIENTRY ohmd_device_settings_seti(ohmd_device_settings* settings, ohmd_int_settings key, const int* val)
514 {
515 switch(key){
516 case OHMD_IDS_AUTOMATIC_UPDATE:
517 settings->automatic_update = val[0] == 0 ? false : true;
518 return OHMD_S_OK;
519
520 default:
521 return OHMD_S_INVALID_PARAMETER;
522 }
523 }
524
ohmd_device_settings_create(ohmd_context * ctx)525 OHMD_APIENTRYDLL ohmd_device_settings* OHMD_APIENTRY ohmd_device_settings_create(ohmd_context* ctx)
526 {
527 return ohmd_alloc(ctx, sizeof(ohmd_device_settings));
528 }
529
ohmd_device_settings_destroy(ohmd_device_settings * settings)530 OHMD_APIENTRYDLL void OHMD_APIENTRY ohmd_device_settings_destroy(ohmd_device_settings* settings)
531 {
532 free(settings);
533 }
534
ohmd_allocfn(ohmd_context * ctx,const char * e_msg,size_t size)535 void* ohmd_allocfn(ohmd_context* ctx, const char* e_msg, size_t size)
536 {
537 void* ret = calloc(1, size);
538 if(!ret)
539 ohmd_set_error(ctx, "%s", e_msg);
540 return ret;
541 }
542
ohmd_set_default_device_properties(ohmd_device_properties * props)543 void ohmd_set_default_device_properties(ohmd_device_properties* props)
544 {
545 props->ipd = 0.061f;
546 props->znear = 0.1f;
547 props->zfar = 1000.0f;
548 ohmd_set_universal_distortion_k(props, 0, 0, 0, 1);
549 ohmd_set_universal_aberration_k(props, 1.0, 1.0, 1.0);
550 }
551
ohmd_calc_default_proj_matrices(ohmd_device_properties * props)552 void ohmd_calc_default_proj_matrices(ohmd_device_properties* props)
553 {
554 mat4x4f proj_base; // base projection matrix
555
556 // Calculate where the lens is on each screen,
557 // and with the given value offset the projection matrix.
558 float screen_center = props->hsize / 4.0f;
559 float lens_shift = screen_center - props->lens_sep / 2.0f;
560 // XXX: on CV1, props->hsize > props->lens_sep / 2.0,
561 // I am not sure about the implications, but just taking the absolute
562 // value of the offset seems to work.
563 float proj_offset = fabs(4.0f * lens_shift / props->hsize);
564
565 // Setup the base projection matrix. Each eye mostly have the
566 // same projection matrix with the exception of the offset.
567 omat4x4f_init_perspective(&proj_base, props->fov, props->ratio, props->znear, props->zfar);
568
569 // Setup the two adjusted projection matrices. Each is setup to deal
570 // with the fact that the lens is not in the center of the screen.
571 // These matrices only change of the hardware changes, so static.
572 mat4x4f translate;
573
574 omat4x4f_init_translate(&translate, proj_offset, 0, 0);
575 omat4x4f_mult(&translate, &proj_base, &props->proj_left);
576
577 omat4x4f_init_translate(&translate, -proj_offset, 0, 0);
578 omat4x4f_mult(&translate, &proj_base, &props->proj_right);
579 }
580
ohmd_set_universal_distortion_k(ohmd_device_properties * props,float a,float b,float c,float d)581 void ohmd_set_universal_distortion_k(ohmd_device_properties* props, float a, float b, float c, float d)
582 {
583 props->universal_distortion_k[0] = a;
584 props->universal_distortion_k[1] = b;
585 props->universal_distortion_k[2] = c;
586 props->universal_distortion_k[3] = d;
587 }
588
ohmd_set_universal_aberration_k(ohmd_device_properties * props,float r,float g,float b)589 void ohmd_set_universal_aberration_k(ohmd_device_properties* props, float r, float g, float b)
590 {
591 props->universal_aberration_k[0] = r;
592 props->universal_aberration_k[1] = g;
593 props->universal_aberration_k[2] = b;
594 }
595
ohmd_monotonic_per_sec(ohmd_context * ctx)596 uint64_t ohmd_monotonic_per_sec(ohmd_context* ctx)
597 {
598 return ctx->monotonic_ticks_per_sec;
599 }
600
601 /*
602 * Grabbed from druntime, good thing it's BOOST v1.0 as well.
603 */
ohmd_monotonic_conv(uint64_t ticks,uint64_t srcTicksPerSecond,uint64_t dstTicksPerSecond)604 uint64_t ohmd_monotonic_conv(uint64_t ticks, uint64_t srcTicksPerSecond, uint64_t dstTicksPerSecond)
605 {
606 // This would be more straightforward with floating point arithmetic,
607 // but we avoid it here in order to avoid the rounding errors that that
608 // introduces. Also, by splitting out the units in this way, we're able
609 // to deal with much larger values before running into problems with
610 // integer overflow.
611 return ticks / srcTicksPerSecond * dstTicksPerSecond +
612 ticks % srcTicksPerSecond * dstTicksPerSecond / srcTicksPerSecond;
613 }
614
ohmd_get_version(int * out_major,int * out_minor,int * out_patch)615 void ohmd_get_version(int* out_major, int* out_minor, int* out_patch)
616 {
617 *out_major = OHMD_VERSION_MAJOR;
618 *out_minor = OHMD_VERSION_MINOR;
619 *out_patch = OHMD_VERSION_PATCH;
620 }
621
ohmd_require_version(int major,int minor,int patch)622 ohmd_status ohmd_require_version(int major, int minor, int patch)
623 {
624 int curr_major, curr_minor, curr_patch;
625 ohmd_get_version(&curr_major, &curr_minor, &curr_patch);
626
627 if(curr_major != major){
628 // require same major version
629 return OHMD_S_UNSUPPORTED;
630 }
631
632 if(curr_minor == minor){
633 // check patch version if the required minor version matches current
634 if(curr_patch < patch){
635 // fail is patch is too low
636 return OHMD_S_UNSUPPORTED;
637 }
638 }
639 else if(curr_minor < minor)
640 {
641 // fail if minor is too low
642 return OHMD_S_UNSUPPORTED;
643 }
644
645 return OHMD_S_OK;
646 }
647