/* Copyright (c) 2013, Stanislaw Halik * Permission to use, copy, modify, and/or distribute this * software for any purpose with or without fee is hereby granted, * provided that the above copyright notice and this permission * notice appear in all copies. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef PLUGIN_API #define PLUGIN_API #endif #pragma GCC diagnostic ignored "-Wunused-parameter" /* using Wine name to ease things */ #define WINE_SHM_NAME "facetracknoir-wine-shm" #define WINE_MTX_NAME "facetracknoir-wine-mtx" #include "compat/linkage-macros.hpp" #ifndef MAP_FAILED # define MAP_FAILED ((void*)-1) #endif #ifdef __GNUC__ # pragma GCC diagnostic ignored "-Wimplicit-float-conversion" # pragma GCC diagnostic ignored "-Wdouble-promotion" # pragma GCC diagnostic ignored "-Wlanguage-extension-token" #endif enum Axis { TX = 0, TY, TZ, Yaw, Pitch, Roll }; typedef struct shm_wrapper { void* mem; int fd, size; } shm_wrapper; typedef struct WineSHM { double data[6]; int gameid, gameid2; unsigned char table[8]; bool stop; } volatile WineSHM; static shm_wrapper* lck_posix = NULL; static WineSHM* shm_posix = NULL; static void *view_x, *view_y, *view_z, *view_heading, *view_pitch, *view_roll; static float offset_x, offset_y, offset_z; static XPLMCommandRef track_toggle = NULL, translation_disable_toggle = NULL; static int track_disabled = 1; static int translation_disabled; static void reinit_offset() { offset_x = XPLMGetDataf(view_x); offset_y = XPLMGetDataf(view_y); offset_z = XPLMGetDataf(view_z); } shm_wrapper* shm_wrapper_init(const char *shm_name, const char *mutex_name, int mapSize) { (void)mutex_name; shm_wrapper* self = malloc(sizeof(shm_wrapper)); char shm_filename[NAME_MAX]; shm_filename[0] = '/'; strncpy(shm_filename+1, shm_name, NAME_MAX-2); shm_filename[NAME_MAX-1] = '\0'; /* (void) shm_unlink(shm_filename); */ self->fd = shm_open(shm_filename, O_RDWR | O_CREAT, 0600); (void) ftruncate(self->fd, mapSize); self->mem = mmap(NULL, mapSize, PROT_READ|PROT_WRITE, MAP_SHARED, self->fd, (off_t)0); return self; } void shm_wrapper_free(shm_wrapper* self) { /*(void) shm_unlink(shm_filename);*/ (void) munmap(self->mem, self->size); (void) close(self->fd); free(self); } void shm_wrapper_lock(shm_wrapper* self) { flock(self->fd, LOCK_SH); } void shm_wrapper_unlock(shm_wrapper* self) { flock(self->fd, LOCK_UN); } float write_head_position(float inElapsedSinceLastCall, float inElapsedTimeSinceLastFlightLoop, int inCounter, void* inRefcon) { if (lck_posix != NULL && shm_posix != NULL) { shm_wrapper_lock(lck_posix); if (!translation_disabled) { XPLMSetDataf(view_x, shm_posix->data[TX] * 1e-3 + offset_x); XPLMSetDataf(view_y, shm_posix->data[TY] * 1e-3 + offset_y); XPLMSetDataf(view_z, shm_posix->data[TZ] * 1e-3 + offset_z); } XPLMSetDataf(view_heading, shm_posix->data[Yaw] * 180 / M_PI); XPLMSetDataf(view_pitch, shm_posix->data[Pitch] * 180 / M_PI); XPLMSetDataf(view_roll, shm_posix->data[Roll] * 180 / M_PI); shm_wrapper_unlock(lck_posix); } return -1.0; } static int TrackToggleHandler(XPLMCommandRef inCommand, XPLMCommandPhase inPhase, void* inRefCon) { if (track_disabled) { //Enable XPLMRegisterFlightLoopCallback(write_head_position, -1, NULL); // Reinit the offsets when we re-enable the plugin if (!translation_disabled) reinit_offset(); } else { //Disable XPLMUnregisterFlightLoopCallback(write_head_position, NULL); } track_disabled = !track_disabled; return 0; } static int TranslationToggleHandler(XPLMCommandRef inCommand, XPLMCommandPhase inPhase, void* inRefCon) { translation_disabled = !translation_disabled; if (!translation_disabled) { // Reinit the offsets when we re-enable the translations so that we can "move around" reinit_offset(); } return 0; } static inline void volatile_explicit_bzero(void volatile* restrict ptr, size_t len) { for (size_t i = 0; i < len; i++) *((char volatile* restrict)ptr + i) = 0; asm volatile("" ::: "memory"); } PLUGIN_API OTR_GENERIC_EXPORT int XPluginStart (char* outName, char* outSignature, char* outDescription) { view_x = XPLMFindDataRef("sim/aircraft/view/acf_peX"); view_y = XPLMFindDataRef("sim/aircraft/view/acf_peY"); view_z = XPLMFindDataRef("sim/aircraft/view/acf_peZ"); view_heading = XPLMFindDataRef("sim/graphics/view/pilots_head_psi"); view_pitch = XPLMFindDataRef("sim/graphics/view/pilots_head_the"); view_roll = XPLMFindDataRef("sim/graphics/view/field_of_view_roll_deg"); track_toggle = XPLMCreateCommand("opentrack/toggle", "Disable/Enable head tracking"); translation_disable_toggle = XPLMCreateCommand("opentrack/toggle_translation", "Disable/Enable input translation from opentrack"); XPLMRegisterCommandHandler(track_toggle, TrackToggleHandler, 1, NULL); XPLMRegisterCommandHandler(translation_disable_toggle, TranslationToggleHandler, 1, NULL); if (view_x && view_y && view_z && view_heading && view_pitch && track_toggle && translation_disable_toggle) { lck_posix = shm_wrapper_init(WINE_SHM_NAME, WINE_MTX_NAME, sizeof(WineSHM)); if (lck_posix->mem == MAP_FAILED) { fprintf(stderr, "opentrack failed to init SHM!\n"); return 0; } shm_posix = lck_posix->mem; volatile_explicit_bzero(shm_posix, sizeof(WineSHM)); strcpy(outName, "opentrack"); strcpy(outSignature, "opentrack - freetrack lives!"); strcpy(outDescription, "head tracking view control"); fprintf(stderr, "opentrack init complete\n"); return 1; } return 0; } PLUGIN_API OTR_GENERIC_EXPORT void XPluginStop (void) { if (lck_posix) { shm_wrapper_free(lck_posix); lck_posix = NULL; shm_posix = NULL; } } PLUGIN_API OTR_GENERIC_EXPORT int XPluginEnable (void) { XPLMRegisterFlightLoopCallback(write_head_position, -1.0, NULL); track_disabled = 0; return 1; } PLUGIN_API OTR_GENERIC_EXPORT void XPluginDisable (void) { XPLMUnregisterFlightLoopCallback(write_head_position, NULL); track_disabled = 1; } PLUGIN_API OTR_GENERIC_EXPORT void XPluginReceiveMessage(XPLMPluginID inFromWho, int inMessage, void * inParam) { if (inMessage == XPLM_MSG_AIRPORT_LOADED) reinit_offset(); }