iup-stack/iup/srctuio/tuio/FlashSender.cpp

651 lines
16 KiB
C++
Raw Permalink Normal View History

2023-02-20 16:44:45 +00:00
#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 <http://www.gnu.org/licenses/>.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if !defined(WIN32)
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/time.h>
#include <fcntl.h>
#if defined(__APPLE__)
#include <mach/mach.h>
#include <mach/mach_time.h>
#else
#include <utmpx.h>
#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; i<len; i++)
buffer[pos++] = bytes[i];
return pos;
}
int TFLCSWriteLCAMFEnvelopeHeader(TFLCSLocalConnection_t* connection)
{
if (NULL == connection)
return 0;
u_int32_t* timestamp = (u_int32_t*)&connection->data[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 <martin@tuio.org>
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;
}