#include "FlashSender.h" // Copyright (C) 2009 Georg Kaindl // This file is part of Touché. // // Touché is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // Touché is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with Touché. If not, see . #include #include #include #include #if !defined(WIN32) #include #include #include #include #if defined(__APPLE__) #include #include #else #include #endif #endif #define FLASHLC_SHM_SIZE (64528) // This is what Flash always uses as size #define FLASHLC_SHM_LISTENERS_OFFSET (40976) // Another "magic number" #define MAX_LISTENER_NAME_LEN (64) #define MAX_LISTENER_METHOD_NAME (64) #define PROTOCOL_NAME ("localhost") #define WIN32_SEMAPHORE_NAME TEXT("MacromediaMutexOmega") #define WIN32_SHMEM_NAME TEXT("MacromediaFMOmega") #define POSIX_SEMAPHORE_NAME ("MacromediaSemaphoreDig") #define POSIX_SEMAPHORE_INITIAL_VALUE (10) #define AMF_TYPE_STRING (0x02) #define AMF_TYPE_AMF3OBJ (0x11) #define AMF3_TYPE_BYTEARRAY (0x0C) #define AMF_ENVELOPE_LEN (16) #define AMF_ENVELOPE_TIMESTAMP_POS (8) #define AMF_ENVELOPE_SIZE_POS (12) void _TFLCSLockSemaphore(TFLCSLocalConnection_t* connection); void _TFLCSUnlockSemaphore(TFLCSLocalConnection_t* connection); // returns non-zero if the given connection is in a usable state, zero otherwise int _TFLCSConnectionStructureIsValidForUse(TFLCSLocalConnection_t* connection); // On WIN32, we cannot create the shared memory and semaphore ourselves, because // we don't know the exact parameters. Therefore, if Flash is not running when we // try to connect, we'll try again whenever we should send data, until we finally // manage to connect. int _TFLCSDelayedConnect(TFLCSLocalConnection_t* connection); #define ENSURE_CONNECTION_UP(c, rv) do { if (!(c)->open && !_TFLCSDelayedConnect((c))) return (rv); } while (0) u_int32_t _TFLCSKnownDarwinKeys[] = { 0x53414e44, 0 }; TFLCSError_t TFLCSErrno = TFLCSErrorSuccess; TFLCSLocalConnection_t* TFLCSConnect(const char* listenerName, const char* listenerMethod, void* shmemKey, void* semaphoreKey) { TFLCSLocalConnection_t* connection = NULL; if (NULL == listenerName || NULL == listenerMethod || strlen(listenerName) > TFLCS_LISTENER_NAME_MAX_LEN || strlen(listenerMethod) > TFLCS_LISTENER_METHOD_MAX_LEN) { TFLCSErrno = TFLCSErrorInvalidArgument; return NULL; } connection = (TFLCSLocalConnection_t*)malloc(sizeof(TFLCSLocalConnection_t)); if (NULL == connection) { TFLCSErrno = TFLCSErrorOutOfMemory; goto errorReturn; } connection->open = 0; #if defined(WIN32) // initialize the structure connection->semaphore = NULL; connection->mapFile = NULL; connection->mapAddress = NULL; connection->data = NULL; _TFLCSDelayedConnect(connection); #else // initialize the structure connection->semaphore = (sem_t*)SEM_FAILED; connection->shmid = -1; connection->data = (char*)-1; // we don't need a delayed connect on MacOS X, so let's just connect now! key_t key; char* semName; if (NULL != shmemKey) key = *(key_t*)shmemKey; else { key_t* guessedKey = (key_t*)TFLCSGuessShmemKey(); if (NULL != guessedKey) key = *(key_t*)guessedKey; else { TFLCSErrno = TFLCSErrorShmemKeyNotFound; goto errorReturn; } } if (NULL != semaphoreKey) semName = (char*)semaphoreKey; else semName = (char*)POSIX_SEMAPHORE_NAME; connection->semaphore = sem_open(semName, O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, POSIX_SEMAPHORE_INITIAL_VALUE); if ((sem_t*)SEM_FAILED == connection->semaphore) { TFLCSErrno = TFLCSErrorSemaphoreCreationFailed; goto errorReturn; } if (-1 == (connection->shmid = shmget(key, FLASHLC_SHM_SIZE, 0666 | IPC_CREAT))) { TFLCSErrno = TFLCSErrorShmemIDNotFound; goto errorReturn; } connection->data = (char*)shmat(connection->shmid, NULL, 0); if ((char*)-1 == connection->data) { TFLCSErrno = TFLCSErrorShmemOpeningFailed; goto errorReturn; } connection->open = 1; // initialize the memory region the way Flash likes it :-) connection->data[0] = 1; connection->data[4] = 1; #endif strncpy(connection->listenerName, listenerName, TFLCS_LISTENER_NAME_MAX_LEN); strncpy(connection->listenerMethod, listenerMethod, TFLCS_LISTENER_METHOD_MAX_LEN); return connection; errorReturn: TFLCSDisconnect(connection); return NULL; } void TFLCSDisconnect(TFLCSLocalConnection_t* connection) { if (NULL != connection) { #if defined(WIN32) if (NULL != connection->semaphore) CloseHandle(connection->semaphore); if (NULL != connection->mapAddress) UnmapViewOfFile(connection->mapAddress); if (NULL != connection->mapFile) CloseHandle(connection->mapFile); #else if ((sem_t*)SEM_FAILED != connection->semaphore) sem_close(connection->semaphore); if ((char*)-1 != connection->data) (void)shmdt(connection->data); #endif free(connection); } } void TFLCSChangeListenerName(TFLCSLocalConnection_t* connection, const char* newListenerName) { if (NULL != connection && NULL != newListenerName) strncpy(connection->listenerName, newListenerName, TFLCS_LISTENER_NAME_MAX_LEN); } void TFLCSChangeMethodName(TFLCSLocalConnection_t* connection, const char* newMethodName) { if (NULL != connection && NULL != newMethodName) strncpy(connection->listenerMethod, newMethodName, TFLCS_LISTENER_METHOD_MAX_LEN); } int TFLCSConnectionHasConnectedClient(TFLCSLocalConnection_t* connection) { ENSURE_CONNECTION_UP(connection, 0); int retval = 0; if (_TFLCSConnectionStructureIsValidForUse(connection)) { _TFLCSLockSemaphore(connection); int c, l = strlen(connection->listenerName); char* p = &connection->data[FLASHLC_SHM_LISTENERS_OFFSET]; while (!retval && (char)0 != *p) { c = strlen(p) + 1; if (0 == strncmp(p, connection->listenerName, l)) retval = 1; p += c; } _TFLCSUnlockSemaphore(connection); } return retval; } int TFLCSGetConnectedConnectionNames(TFLCSLocalConnection_t* connection, char* dest, int destLen) { ENSURE_CONNECTION_UP(connection, 0); int retval = 0; if (NULL != dest && 0 < destLen && _TFLCSConnectionStructureIsValidForUse(connection)) { _TFLCSLockSemaphore(connection); int c, i=0; char* p = &connection->data[FLASHLC_SHM_LISTENERS_OFFSET]; while ((char)0 != *p && destLen > 0) { c = strlen(p) + 1; if (0 == (i % 3)) { strncpy(dest, p, destLen < c ? destLen : c); destLen -= c; dest += c; } p += c; i++; } retval = i/3; _TFLCSUnlockSemaphore(connection); } return retval; } void* TFLCSGuessShmemKey() { #if defined(WIN32) return (void*)WIN32_SHMEM_NAME; #else static int found = 0; static key_t key; // first, try our known keys int i = 0; while(!found && _TFLCSKnownDarwinKeys[i]) { if (-1 != shmget(_TFLCSKnownDarwinKeys[i], FLASHLC_SHM_SIZE, 0)) { found = 1; key = _TFLCSKnownDarwinKeys[i]; } i++; } /* // magic numbers for MacOS X 10.5, probably later and earlier versions too, dunno... int minid = 0xffff; int maxid = 0x500000; struct shmid_ds shm_info; for (i=minid; !found && i<=maxid; i++) { if (shmctl(i, IPC_STAT, &shm_info) < 0) continue; if (FLASHLC_SHM_SIZE == shm_info.shm_segsz) { found = 1; key = shm_info.shm_perm._key; } } */ // if we didn't find anything, set the key to a reasonable guess // this is necessary because the shared segment might not exist yet. if (!found) { key = _TFLCSKnownDarwinKeys[0]; found = 1; } return &key; #endif } u_int32_t TFLCSGetTickCount() { #if defined(WIN32) return (u_int32_t)GetTickCount(); #elif defined(__APPLE__) static mach_timebase_info_data_t timeBase; u_int64_t nanos = mach_absolute_time(); if (0 == timeBase.denom) { (void)mach_timebase_info(&timeBase); } return (u_int32_t)((nanos * (u_int64_t)timeBase.numer / (u_int64_t)timeBase.denom) / (u_int64_t)1000000); #else static struct timeval bt; static int initialized = 0; if (!initialized) { struct utmpx* ut; while (NULL != (ut = getutxent())) { if (BOOT_TIME == ut->ut_type) { memcpy(&bt, &ut->ut_tv, sizeof(struct timeval)); break; } } initialized = 1; } struct timeval nt, btc; (void)gettimeofday(&nt, NULL); memcpy(&btc, &bt, sizeof(struct timeval)); if (nt.tv_usec < btc.tv_usec) { u_int64_t n = (btc.tv_usec - nt.tv_usec) / 1000000 + 1; btc.tv_usec -= 1000000 * n; btc.tv_sec += n; } if (nt.tv_usec - btc.tv_usec > 1000000) { u_int64_t n = (btc.tv_usec - nt.tv_usec) / 1000000; btc.tv_usec += 1000000 * n; btc.tv_sec -= n; } nt.tv_usec = nt.tv_usec - btc.tv_usec; nt.tv_sec = nt.tv_sec - btc.tv_sec; return (u_int32_t)((nt.tv_sec * 1000) + (nt.tv_usec / 1000)); #endif } int TFLCSWriteAMF3Integer(char* buffer, int value, int pos) { if (value < 0) { buffer[pos++] = (0x80 | ((value >> 22) & 0xff)); buffer[pos++] = (0x80 | ((value >> 15) & 0x7f)); buffer[pos++] = (0x80 | ((value >> 8) & 0x7f)); buffer[pos++] = (value & 0xff); } else if (value <= 0x7f) { buffer[pos++] = value; } else if (value <= 0x3fff) { buffer[pos++] = (0x80 | ((value >> 7) & 0x7f)); buffer[pos++] = (value & 0x7f); } else if (value <= 0x1fffff) { buffer[pos++] = (0x80 | ((value >> 14) & 0x7f)); buffer[pos++] = (0x80 | ((value >> 7) & 0x7f)); buffer[pos++] = (value & 0x7f); } else { buffer[pos++] = (0x80 | ((value >> 22) & 0xff)); buffer[pos++] = (0x80 | ((value >> 15) & 0x7f)); buffer[pos++] = (0x80 | ((value >> 8) & 0x7f)); buffer[pos++] = (value & 0xff); } return pos; } int TFLCSWriteAMFString(char* buffer, const char * str, int pos) { int len = strlen(str); buffer[pos++] = AMF_TYPE_STRING; buffer[pos++] = 0; // TODO: string length is badly encoded here! buffer[pos++] = (char)(len & 0xff); strcpy((char*)&buffer[pos], str); pos += len; return pos; } int TFLCSWriteAMF3ByteArray(char* buffer, const char* bytes, int pos, int len) { // buffer[pos++] = AMF_TYPE_AMF3OBJ; buffer[pos++] = AMF3_TYPE_BYTEARRAY; // AMF3_TYPE_BYTE_ARRAY pos = TFLCSWriteAMF3Integer(buffer, ((len << 1) | 1), pos); int i=0; for(i=0; idata[AMF_ENVELOPE_TIMESTAMP_POS]; int pos = AMF_ENVELOPE_LEN; // check timestamp and size memset(connection->data, 0, AMF_ENVELOPE_LEN); connection->data[0] = 1; connection->data[4] = 1; *timestamp = TFLCSGetTickCount(); // write connection name pos = TFLCSWriteAMFString(connection->data, connection->listenerName, pos); // write protocol pos = TFLCSWriteAMFString(connection->data, PROTOCOL_NAME, pos); // I have no idea what this is, but we apparently need it... char weirdBytes[] = { 0x01, 0x01, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x24, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x08, 0, 0, 0, 0, 0, 0}; int i; for (i=0; i<31; i++) connection->data[pos++] = weirdBytes[i]; // write method name pos = TFLCSWriteAMFString(connection->data, connection->listenerMethod, pos); return pos; } int TFLCSWriteLCAMFEnvelopeTrailer(TFLCSLocalConnection_t* connection, int pos) { if (NULL == connection) return pos; u_int32_t* size = (u_int32_t*)&connection->data[AMF_ENVELOPE_SIZE_POS]; //pos = writeAMF3ByteArray(buffer, msg, pos, strlen(msg)); *size = pos-16; return pos; } int TFLCSSendByteArray(TFLCSLocalConnection_t* connection, const char* bytes, int len) { ENSURE_CONNECTION_UP(connection, 0); if (NULL == bytes || 0 == len || !_TFLCSConnectionStructureIsValidForUse(connection)) { TFLCSErrno = TFLCSErrorInvalidArgument; return 0; } _TFLCSLockSemaphore(connection); int pos = TFLCSWriteLCAMFEnvelopeHeader(connection); pos = TFLCSWriteAMF3ByteArray(connection->data, bytes, pos, len); (void)TFLCSWriteLCAMFEnvelopeTrailer(connection, pos); _TFLCSUnlockSemaphore(connection); return 1; } void TFLCSDumpMemory(char* buffer, int offset, int size) { int i = 0; int c = 0; char b; while (i < size) { while ((c < 16) && (i+c < size)) { b = buffer[offset+i+c]; printf("%X%X ", b/16 & 0x0f, b & 0x0f ); c++; } while (c++ < 16) printf(" "); c = 0; while ((c < 16) && (i+c < size)) { b = buffer[offset+i+c]; if (b > 31) printf("%c", (char)b); else printf("."); c++; } i += 16; c = 0; printf("\n"); } } void _TFLCSLockSemaphore(TFLCSLocalConnection_t* connection) { if (NULL != connection) { #if defined(WIN32) if (NULL != connection->semaphore) WaitForSingleObject(connection->semaphore, INFINITE); #else if ((sem_t*)SEM_FAILED != connection->semaphore) sem_wait(connection->semaphore); #endif } } void _TFLCSUnlockSemaphore(TFLCSLocalConnection_t* connection) { if (NULL != connection) { #if defined(WIN32) if (NULL != connection->semaphore) ReleaseMutex(connection->semaphore); #else if ((sem_t*)SEM_FAILED != connection->semaphore) sem_post(connection->semaphore); #endif } } int _TFLCSConnectionStructureIsValidForUse(TFLCSLocalConnection_t* connection) { return (NULL != connection && 0 != connection->open #if defined(WIN32) && NULL != connection->semaphore && NULL != connection->mapFile && NULL != connection->mapAddress && NULL != connection->data #else && (sem_t*)SEM_FAILED != connection->semaphore && (char*)-1 != connection->data #endif ); } int _TFLCSDelayedConnect(TFLCSLocalConnection_t* connection) { #if defined(WIN32) if (NULL == connection) return 0; if (connection->open) return 1; if (NULL == (connection->semaphore = OpenMutex(MUTEX_ALL_ACCESS, FALSE, WIN32_SEMAPHORE_NAME))) { TFLCSErrno = TFLCSErrorSemaphoreCreationFailed; goto errorReturn; } if (NULL == (connection->mapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, WIN32_SHMEM_NAME))) { TFLCSErrno = TFLCSErrorShmemIDNotFound; goto errorReturn; } if (NULL == (connection->mapAddress = MapViewOfFile(connection->mapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0))) { TFLCSErrno = TFLCSErrorShmemOpeningFailed; goto errorReturn; } connection->data = (char*)connection->mapAddress; connection->open = 1; errorReturn: #endif return connection->open; } /* TUIO C++ Library Copyright (c) 2005-2017 Martin Kaltenbrunner This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. */ using namespace TUIO; FlashSender::FlashSender() { local = true; buffer_size = MAX_FLASH_SIZE; lcConnection = TFLCSConnect(DEFAULT_LC_CONN_NAME,DEFAULT_LC_METH_NAME,NULL,NULL); std::cout << "TUIO/FLC "<< DEFAULT_LC_METH_NAME << "@" << DEFAULT_LC_CONN_NAME << std::endl; } FlashSender::FlashSender(const char *conn_name, const char *meth_name) { local = true; buffer_size = MAX_FLASH_SIZE; lcConnection = TFLCSConnect(conn_name,meth_name,NULL,NULL); std::cout << "TUIO/FLC "<< meth_name << "@" << conn_name << std::endl; } FlashSender::~FlashSender() { TFLCSDisconnect(lcConnection); } bool FlashSender::isConnected() { if (TFLCSConnectionHasConnectedClient(lcConnection)) return true; else return false; } bool FlashSender::sendOscPacket (osc::OutboundPacketStream *bundle) { if (lcConnection==NULL) return false; if (!TFLCSConnectionHasConnectedClient(lcConnection)) return false; if ( bundle->Size() > buffer_size ) return false; if ( bundle->Size() == 0 ) return false; TFLCSSendByteArray(lcConnection, bundle->Data(), bundle->Size()); return true; }