summaryrefslogtreecommitdiffstats
path: root/LibOVR/Src
diff options
context:
space:
mode:
Diffstat (limited to 'LibOVR/Src')
-rw-r--r--LibOVR/Src/Displays/OVR_OSX_Display.cpp356
-rw-r--r--LibOVR/Src/Displays/OVR_OSX_Display.h139
-rw-r--r--LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp842
-rw-r--r--LibOVR/Src/Net/OVR_Unix_Socket.cpp586
-rw-r--r--LibOVR/Src/Net/OVR_Unix_Socket.h152
5 files changed, 2075 insertions, 0 deletions
diff --git a/LibOVR/Src/Displays/OVR_OSX_Display.cpp b/LibOVR/Src/Displays/OVR_OSX_Display.cpp
new file mode 100644
index 0000000..4cd9307
--- /dev/null
+++ b/LibOVR/Src/Displays/OVR_OSX_Display.cpp
@@ -0,0 +1,356 @@
+/************************************************************************************
+
+Filename : OVR_OSX_Display.cpp
+Content : OSX-specific Display declarations
+Created : July 2, 2014
+Authors : James Hughes
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#include "OVR_OSX_Display.h"
+#include "../Kernel/OVR_Log.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFString.h>
+#include <IOKit/graphics/IOGraphicsLib.h>
+
+//-------------------------------------------------------------------------------------
+// ***** Display enumeration Helpers
+
+namespace OVR {
+
+// FIXME Code duplication with windows.
+#define EDID_LENGTH 0x80
+
+#define EDID_HEADER 0x00
+#define EDID_HEADER_END 0x07
+
+#define ID_MANUFACTURER_NAME 0x08
+#define ID_MANUFACTURER_NAME_END 0x09
+
+#define EDID_STRUCT_VERSION 0x12
+#define EDID_STRUCT_REVISION 0x13
+
+#define ESTABLISHED_TIMING_1 0x23
+#define ESTABLISHED_TIMING_2 0x24
+#define MANUFACTURERS_TIMINGS 0x25
+
+#define DETAILED_TIMING_DESCRIPTIONS_START 0x36
+#define DETAILED_TIMING_DESCRIPTION_SIZE 18
+#define NO_DETAILED_TIMING_DESCRIPTIONS 4
+
+#define DETAILED_TIMING_DESCRIPTION_1 0x36
+#define DETAILED_TIMING_DESCRIPTION_2 0x48
+#define DETAILED_TIMING_DESCRIPTION_3 0x5a
+#define DETAILED_TIMING_DESCRIPTION_4 0x6c
+
+#define MONITOR_NAME 0xfc
+#define MONITOR_LIMITS 0xfd
+#define MONITOR_SERIAL 0xff
+
+#define UNKNOWN_DESCRIPTOR -1
+#define DETAILED_TIMING_BLOCK -2
+
+#define DESCRIPTOR_DATA 5
+
+const UByte edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x00 };
+
+const UByte edid_v1_descriptor_flag[] = { 0x00, 0x00 };
+
+// FIXME Code duplication with windows. Refactor.
+static int blockType( UByte* block )
+{
+ if ( !strncmp( (const char*)edid_v1_descriptor_flag, (const char*)block, 2 ) )
+ {
+ // descriptor
+ if ( block[ 2 ] != 0 )
+ return UNKNOWN_DESCRIPTOR;
+ return block[ 3 ];
+ }
+ else
+ {
+ return DETAILED_TIMING_BLOCK;
+ }
+}
+
+static char* getMonitorName( UByte const* block )
+{
+ static char name[ 13 ];
+ unsigned i;
+ UByte const* ptr = block + DESCRIPTOR_DATA;
+
+ for( i = 0; i < 13; i++, ptr++ )
+ {
+ if ( *ptr == 0xa )
+ {
+ name[ i ] = 0;
+ return name;
+ }
+
+ name[ i ] = *ptr;
+ }
+
+ return name;
+}
+
+// FIXME Code duplication with windows. Refactor.
+static bool parseEdid( UByte* edid, OVR::OSX::DisplayEDID& edidResult )
+{
+ unsigned i;
+ UByte* block;
+ const char* monitor_name = "Unknown";
+ UByte checksum = 0;
+
+ for( i = 0; i < EDID_LENGTH; i++ )
+ checksum += edid[ i ];
+
+ // Bad checksum, fail EDID
+ if ( checksum != 0 )
+ return false;
+
+ if ( strncmp( (const char*)edid+EDID_HEADER, (const char*)edid_v1_header, EDID_HEADER_END+1 ) )
+ {
+ // First bytes don't match EDID version 1 header
+ return false;
+ }
+
+
+ // OVR_DEBUG_LOG_TEXT(( "\n# EDID version %d revision %d\n",
+ // (int)edid[EDID_STRUCT_VERSION],(int)edid[EDID_STRUCT_REVISION] ));
+
+ // Monitor name and timings
+
+ char serialNumber[14];
+ memset( serialNumber, 0, 14 );
+
+ block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+
+ for( i = 0; i < NO_DETAILED_TIMING_DESCRIPTIONS; i++,
+ block += DETAILED_TIMING_DESCRIPTION_SIZE )
+ {
+
+ if ( blockType( block ) == MONITOR_NAME )
+ {
+ monitor_name = getMonitorName( block );
+ }
+
+ if( blockType( block ) == MONITOR_SERIAL )
+ {
+ memcpy( serialNumber, block + 5, 13 );
+ break;
+ }
+ }
+
+ UByte vendorString[4] = {0};
+
+ vendorString[0] = (edid[8] >> 2 & 31) + 64;
+ vendorString[1] = ((edid[8] & 3) << 3) | (edid[9] >> 5) + 64;
+ vendorString[2] = (edid[9] & 31) + 64;
+
+ edidResult.ModelNumber = *(UInt16*)&edid[10];
+ edidResult.MonitorName = OVR::String(monitor_name);
+ edidResult.VendorName = OVR::String((const char*)vendorString);
+ edidResult.SerialNumber = OVR::String(serialNumber);
+
+ // printf( "\tIdentifier \"%s\"\n", monitor_name );
+ // printf( "\tVendorName \"%s\"\n", vendorString );
+ // printf( "\tModelName \"%s\"\n", monitor_name );
+ // printf( "\tModelNumber %d\n", edidResult.ModelNumber );
+ // printf( "\tSerialNumber \"%s\"\n", edidResult.SerialNumber.ToCStr() );
+
+ // FIXME: Get timings as well, though they aren't very useful here
+ // except for the vertical refresh rate, presumably
+
+ return true;
+}
+
+static int discoverExtendedRifts(OVR::OSX::DisplayDesc* descriptorArray, int inputArraySize, bool edidInfo)
+{
+ OVR_UNUSED(edidInfo);
+ int result = 0;
+
+ static bool reportDiscovery = true;
+ OVR_UNUSED(reportDiscovery);
+
+ CGDirectDisplayID Displays[32];
+ uint32_t NDisplays = 0;
+ CGGetOnlineDisplayList(32, Displays, &NDisplays);
+
+ for (unsigned int i = 0; i < NDisplays; i++)
+ {
+ io_service_t port = CGDisplayIOServicePort(Displays[i]);
+ CFDictionaryRef DispInfo = IODisplayCreateInfoDictionary(port, kNilOptions);
+
+ // Display[i]
+
+ uint32_t vendor = CGDisplayVendorNumber(Displays[i]);
+ uint32_t product = CGDisplayModelNumber(Displays[i]);
+
+ CGRect desktop = CGDisplayBounds(Displays[i]);
+ Vector2i desktopOffset(desktop.origin.x, desktop.origin.y);
+
+ if (vendor == 16082 && ( (product == 1)||(product == 2)||(product == 3) ) ) // 7" or HD
+ {
+ if( result >= inputArraySize ) { return result; }
+
+ Sizei monitorResolution(1280, 800);
+
+ // Obtain and parse EDID data.
+ CFDataRef data =
+ (CFDataRef)CFDictionaryGetValue(DispInfo, CFSTR(kIODisplayEDIDKey));
+ if (!data)
+ {
+ CFRelease(DispInfo);
+ OVR::LogError("[OSX Display] Unable to obtain EDID for Oculus product %d", product);
+ continue;
+ }
+ UByte* edid = (UByte*)CFDataGetBytePtr(data);
+ OSX::DisplayEDID edidResult;
+ parseEdid( edid, edidResult );
+
+ OVR::OSX::DisplayDesc& desc = descriptorArray[result++];
+ desc.DisplayID = Displays[i];
+ desc.ModelName = edidResult.MonitorName; // User friendly string.
+ desc.EdidSerialNumber = edidResult.SerialNumber;
+ desc.LogicalResolutionInPixels = monitorResolution;
+ desc.DesktopDisplayOffset = desktopOffset;
+
+ switch (product)
+ {
+ case 3: desc.DeviceTypeGuess = HmdType_DK2; break;
+ case 2: desc.DeviceTypeGuess = HmdType_DKHDProto; break;
+ case 1: desc.DeviceTypeGuess = HmdType_DK1; break;
+
+ default:
+ case 0: desc.DeviceTypeGuess = HmdType_Unknown; break;
+ }
+
+ // Hard-coded defaults in case the device doesn't have the data itself.
+ // DK2 prototypes (0003) or DK HD Prototypes (0002)
+ if (product == 3 || product == 2)
+ {
+ desc.LogicalResolutionInPixels = Sizei(1920, 1080);
+ desc.NativeResolutionInPixels = Sizei(1080, 1920);
+ }
+ else
+ {
+ desc.LogicalResolutionInPixels = monitorResolution;
+ desc.NativeResolutionInPixels = monitorResolution;
+ }
+
+ //OVR_DEBUG_LOG_TEXT(("Display Found %x:%x\n", vendor, product));
+ }
+ CFRelease(DispInfo);
+ }
+
+ return result;
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** Display
+
+bool Display::Initialize()
+{
+ // Nothing to initialize. OS X only supports compatibility mode.
+ return true;
+}
+
+DisplaySearchHandle* Display::GetDisplaySearchHandle()
+{
+ return new OSX::OSXDisplaySearchHandle();
+}
+
+bool Display::InCompatibilityMode( bool displaySearch )
+{
+ OVR_UNUSED( displaySearch );
+ return true;
+}
+
+int Display::GetDisplayCount( DisplaySearchHandle* handle, bool extended, bool applicationOnly, bool edidInfo )
+{
+ OVR_UNUSED(applicationOnly);
+
+ static int extendedCount = -1;
+
+ OSX::OSXDisplaySearchHandle* localHandle = (OSX::OSXDisplaySearchHandle*)handle;
+ if (localHandle == NULL)
+ {
+ OVR::LogError("[OSX Display] No search handle passed into GetDisplayCount. Return 0 rifts.");
+ return 0;
+ }
+
+ if (extendedCount == -1 || extended)
+ {
+ extendedCount = discoverExtendedRifts(localHandle->cachedDescriptorArray, OSX::OSXDisplaySearchHandle::DescArraySize, edidInfo);
+ }
+
+ localHandle->extended = true;
+ localHandle->extendedDisplayCount = extendedCount;
+ int totalCount = extendedCount;
+
+ /// FIXME: Implement application mode for OS X.
+ localHandle->application = false;
+ localHandle->applicationDisplayCount = 0;
+
+ localHandle->displayCount = totalCount;
+
+ return totalCount;
+}
+
+Ptr<Display> Display::GetDisplay( int index, DisplaySearchHandle* handle )
+{
+ Ptr<Display> result = NULL;
+
+ if (index < 0)
+ {
+ OVR::LogError("[OSX Display] Invalid index given to GetDisplay.");
+ return NULL;
+ }
+
+ OSX::OSXDisplaySearchHandle* localHandle = (OSX::OSXDisplaySearchHandle*)handle;
+ if (localHandle == NULL)
+ {
+ OVR::LogError("[OSX Display] No search handle passed into GetDisplay. Return 0 rifts.");
+ return NULL;
+ }
+
+ if (localHandle->extended)
+ {
+ if (index >= 0 && index < (int)localHandle->extendedDisplayCount)
+ {
+ return *new OSX::OSXDisplayGeneric(localHandle->cachedDescriptorArray[index]);
+ }
+
+ index -= localHandle->extendedDisplayCount;
+ }
+
+ if (localHandle->application)
+ {
+ OVR::LogError("[OSX Display] Mac does not support application displays.");
+ }
+
+ return result;
+}
+
+
+} // namespace OVR
diff --git a/LibOVR/Src/Displays/OVR_OSX_Display.h b/LibOVR/Src/Displays/OVR_OSX_Display.h
new file mode 100644
index 0000000..1401121
--- /dev/null
+++ b/LibOVR/Src/Displays/OVR_OSX_Display.h
@@ -0,0 +1,139 @@
+/************************************************************************************
+
+Filename : OVR_OSX_Display.h
+Content : OSX-specific Display declarations
+Created : July 2, 2014
+Authors : James Hughes
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#ifndef OVR_OSX_Display_h
+#define OVR_OSX_Display_h
+
+#include "OVR_Display.h"
+
+namespace OVR { namespace OSX {
+
+
+//-------------------------------------------------------------------------------------
+// DisplayDesc
+
+// Display information enumerable through OS .
+// TBD: Should we just move this to public header, so it's a const member of Display?
+struct DisplayDesc
+{
+ DisplayDesc() :
+ DeviceTypeGuess(HmdType_None),
+ DisplayID(0),
+ LogicalResolutionInPixels(0),
+ NativeResolutionInPixels(0)
+ {}
+
+ HmdTypeEnum DeviceTypeGuess;
+ uint32_t DisplayID; // This is the device identifier string from MONITORINFO (for app usage)
+ String ModelName; // This is a "DK2" type string
+ String EdidSerialNumber;
+ Sizei LogicalResolutionInPixels;
+ Sizei NativeResolutionInPixels;
+ Vector2i DesktopDisplayOffset;
+};
+
+
+//-------------------------------------------------------------------------------------
+// DisplayEDID
+
+// Describes EDID information as reported from our display driver.
+struct DisplayEDID
+{
+ DisplayEDID() :
+ ModelNumber(0)
+ {}
+
+ String MonitorName;
+ UInt16 ModelNumber;
+ String VendorName;
+ String SerialNumber;
+};
+
+//-------------------------------------------------------------------------------------
+// OSX Display Search Handle
+class OSXDisplaySearchHandle : public DisplaySearchHandle
+{
+public:
+ OSXDisplaySearchHandle() :
+ extended(false),
+ application(false),
+ extendedDisplayCount(0),
+ applicationDisplayCount(0),
+ displayCount(0)
+ {}
+ virtual ~OSXDisplaySearchHandle() {}
+
+ static const int DescArraySize = 16;
+
+ OSX::DisplayDesc cachedDescriptorArray[DescArraySize];
+ bool extended;
+ bool application;
+ int extendedDisplayCount;
+ int applicationDisplayCount;
+ int displayCount;
+};
+
+//-------------------------------------------------------------------------------------
+// OSXDisplayGeneric
+
+// Describes OSX display in Compatibility mode, containing basic data
+class OSXDisplayGeneric : public Display
+{
+public:
+ OSXDisplayGeneric( const DisplayDesc& dd ) :
+ Display(dd.DeviceTypeGuess,
+ dd.DisplayID,
+ dd.ModelName,
+ dd.EdidSerialNumber,
+ dd.LogicalResolutionInPixels,
+ dd.NativeResolutionInPixels,
+ dd.DesktopDisplayOffset,
+ 0,
+ 0,
+ false)
+ {
+ }
+
+ virtual ~OSXDisplayGeneric()
+ {
+ }
+
+ virtual bool InCompatibilityMode() const
+ {
+ return true;
+ }
+
+ // Generic displays are not capable of mirroring
+ virtual MirrorMode SetMirrorMode( MirrorMode newMode )
+ {
+ OVR_UNUSED( newMode );
+ return MirrorDisabled;
+ }
+};
+
+}} // namespace OVR::OSX
+
+#endif // OVR_OSX_Display_h
diff --git a/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp b/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp
new file mode 100644
index 0000000..83d3606
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp
@@ -0,0 +1,842 @@
+/************************************************************************************
+
+Filename : OVR_ThreadsPthread.cpp
+Content :
+Created :
+Notes :
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "OVR_Threads.h"
+#include "OVR_Hash.h"
+
+#ifdef OVR_ENABLE_THREADS
+
+#include "OVR_Timer.h"
+#include "OVR_Log.h"
+
+#include <pthread.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <errno.h>
+
+
+namespace OVR {
+
+// ***** Mutex implementation
+
+
+// *** Internal Mutex implementation structure
+
+class MutexImpl : public NewOverrideBase
+{
+ // System mutex or semaphore
+ pthread_mutex_t SMutex;
+ bool Recursive;
+ unsigned LockCount;
+ pthread_t LockedBy;
+
+ friend class WaitConditionImpl;
+
+public:
+ // Constructor/destructor
+ MutexImpl(Mutex* pmutex, bool recursive = 1);
+ ~MutexImpl();
+
+ // Locking functions
+ void DoLock();
+ bool TryLock();
+ void Unlock(Mutex* pmutex);
+ // Returns 1 if the mutes is currently locked
+ bool IsLockedByAnotherThread(Mutex* pmutex);
+ bool IsSignaled() const;
+};
+
+pthread_mutexattr_t Lock::RecursiveAttr;
+bool Lock::RecursiveAttrInit = 0;
+
+// *** Constructor/destructor
+MutexImpl::MutexImpl(Mutex* pmutex, bool recursive)
+{
+ OVR_UNUSED(pmutex);
+ Recursive = recursive;
+ LockCount = 0;
+
+ if (Recursive)
+ {
+ if (!Lock::RecursiveAttrInit)
+ {
+ pthread_mutexattr_init(&Lock::RecursiveAttr);
+ pthread_mutexattr_settype(&Lock::RecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
+ Lock::RecursiveAttrInit = 1;
+ }
+
+ pthread_mutex_init(&SMutex, &Lock::RecursiveAttr);
+ }
+ else
+ pthread_mutex_init(&SMutex, 0);
+}
+
+MutexImpl::~MutexImpl()
+{
+ pthread_mutex_destroy(&SMutex);
+}
+
+
+// Lock and try lock
+void MutexImpl::DoLock()
+{
+ while (pthread_mutex_lock(&SMutex))
+ ;
+ LockCount++;
+ LockedBy = pthread_self();
+}
+
+bool MutexImpl::TryLock()
+{
+ if (!pthread_mutex_trylock(&SMutex))
+ {
+ LockCount++;
+ LockedBy = pthread_self();
+ return 1;
+ }
+
+ return 0;
+}
+
+void MutexImpl::Unlock(Mutex* pmutex)
+{
+ OVR_UNUSED(pmutex);
+ OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0);
+
+ unsigned lockCount;
+ LockCount--;
+ lockCount = LockCount;
+
+ pthread_mutex_unlock(&SMutex);
+}
+
+bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex)
+{
+ OVR_UNUSED(pmutex);
+ // There could be multiple interpretations of IsLocked with respect to current thread
+ if (LockCount == 0)
+ return 0;
+ if (pthread_self() != LockedBy)
+ return 1;
+ return 0;
+}
+
+bool MutexImpl::IsSignaled() const
+{
+ // An mutex is signaled if it is not locked ANYWHERE
+ // Note that this is different from IsLockedByAnotherThread function,
+ // that takes current thread into account
+ return LockCount == 0;
+}
+
+
+// *** Actual Mutex class implementation
+
+Mutex::Mutex(bool recursive)
+{
+ // NOTE: RefCount mode already thread-safe for all waitables.
+ pImpl = new MutexImpl(this, recursive);
+}
+
+Mutex::~Mutex()
+{
+ delete pImpl;
+}
+
+// Lock and try lock
+void Mutex::DoLock()
+{
+ pImpl->DoLock();
+}
+bool Mutex::TryLock()
+{
+ return pImpl->TryLock();
+}
+void Mutex::Unlock()
+{
+ pImpl->Unlock(this);
+}
+bool Mutex::IsLockedByAnotherThread()
+{
+ return pImpl->IsLockedByAnotherThread(this);
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Event
+
+bool Event::Wait(unsigned delay)
+{
+ Mutex::Locker lock(&StateMutex);
+
+ // Do the correct amount of waiting
+ if (delay == OVR_WAIT_INFINITE)
+ {
+ while(!State)
+ StateWaitCondition.Wait(&StateMutex);
+ }
+ else if (delay)
+ {
+ if (!State)
+ StateWaitCondition.Wait(&StateMutex, delay);
+ }
+
+ bool state = State;
+ // Take care of temporary 'pulsing' of a state
+ if (Temporary)
+ {
+ Temporary = false;
+ State = false;
+ }
+ return state;
+}
+
+void Event::updateState(bool newState, bool newTemp, bool mustNotify)
+{
+ Mutex::Locker lock(&StateMutex);
+ State = newState;
+ Temporary = newTemp;
+ if (mustNotify)
+ StateWaitCondition.NotifyAll();
+}
+
+
+
+// ***** Wait Condition Implementation
+
+// Internal implementation class
+class WaitConditionImpl : public NewOverrideBase
+{
+ pthread_mutex_t SMutex;
+ pthread_cond_t Condv;
+
+public:
+
+ // Constructor/destructor
+ WaitConditionImpl();
+ ~WaitConditionImpl();
+
+ // Release mutex and wait for condition. The mutex is re-aqured after the wait.
+ bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE);
+
+ // Notify a condition, releasing at one object waiting
+ void Notify();
+ // Notify a condition, releasing all objects waiting
+ void NotifyAll();
+};
+
+
+WaitConditionImpl::WaitConditionImpl()
+{
+ pthread_mutex_init(&SMutex, 0);
+ pthread_cond_init(&Condv, 0);
+}
+
+WaitConditionImpl::~WaitConditionImpl()
+{
+ pthread_mutex_destroy(&SMutex);
+ pthread_cond_destroy(&Condv);
+}
+
+bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay)
+{
+ bool result = 1;
+ unsigned lockCount = pmutex->pImpl->LockCount;
+
+ // Mutex must have been locked
+ if (lockCount == 0)
+ return 0;
+
+ pthread_mutex_lock(&SMutex);
+
+ // Finally, release a mutex or semaphore
+ if (pmutex->pImpl->Recursive)
+ {
+ // Release the recursive mutex N times
+ pmutex->pImpl->LockCount = 0;
+ for(unsigned i=0; i<lockCount; i++)
+ pthread_mutex_unlock(&pmutex->pImpl->SMutex);
+ }
+ else
+ {
+ pmutex->pImpl->LockCount = 0;
+ pthread_mutex_unlock(&pmutex->pImpl->SMutex);
+ }
+
+ // Note that there is a gap here between mutex.Unlock() and Wait().
+ // The other mutex protects this gap.
+
+ if (delay == OVR_WAIT_INFINITE)
+ pthread_cond_wait(&Condv,&SMutex);
+ else
+ {
+ timespec ts;
+
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+
+ ts.tv_sec = tv.tv_sec + (delay / 1000);
+ ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000;
+
+ if (ts.tv_nsec > 999999999)
+ {
+ ts.tv_sec++;
+ ts.tv_nsec -= 1000000000;
+ }
+ int r = pthread_cond_timedwait(&Condv,&SMutex, &ts);
+ OVR_ASSERT(r == 0 || r == ETIMEDOUT);
+ if (r)
+ result = 0;
+ }
+
+ pthread_mutex_unlock(&SMutex);
+
+ // Re-aquire the mutex
+ for(unsigned i=0; i<lockCount; i++)
+ pmutex->DoLock();
+
+ // Return the result
+ return result;
+}
+
+// Notify a condition, releasing the least object in a queue
+void WaitConditionImpl::Notify()
+{
+ pthread_mutex_lock(&SMutex);
+ pthread_cond_signal(&Condv);
+ pthread_mutex_unlock(&SMutex);
+}
+
+// Notify a condition, releasing all objects waiting
+void WaitConditionImpl::NotifyAll()
+{
+ pthread_mutex_lock(&SMutex);
+ pthread_cond_broadcast(&Condv);
+ pthread_mutex_unlock(&SMutex);
+}
+
+
+
+// *** Actual implementation of WaitCondition
+
+WaitCondition::WaitCondition()
+{
+ pImpl = new WaitConditionImpl;
+}
+WaitCondition::~WaitCondition()
+{
+ delete pImpl;
+}
+
+bool WaitCondition::Wait(Mutex *pmutex, unsigned delay)
+{
+ return pImpl->Wait(pmutex, delay);
+}
+// Notification
+void WaitCondition::Notify()
+{
+ pImpl->Notify();
+}
+void WaitCondition::NotifyAll()
+{
+ pImpl->NotifyAll();
+}
+
+
+// ***** Current thread
+
+// Per-thread variable
+/*
+static __thread Thread* pCurrentThread = 0;
+
+// Static function to return a pointer to the current thread
+void Thread::InitCurrentThread(Thread *pthread)
+{
+ pCurrentThread = pthread;
+}
+
+// Static function to return a pointer to the current thread
+Thread* Thread::GetThread()
+{
+ return pCurrentThread;
+}
+*/
+
+
+// *** Thread constructors.
+
+Thread::Thread(UPInt stackSize, int processor)
+{
+ // NOTE: RefCount mode already thread-safe for all Waitable objects.
+ CreateParams params;
+ params.stackSize = stackSize;
+ params.processor = processor;
+ Init(params);
+}
+
+Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize,
+ int processor, Thread::ThreadState initialState)
+{
+ CreateParams params(threadFunction, userHandle, stackSize, processor, initialState);
+ Init(params);
+}
+
+Thread::Thread(const CreateParams& params)
+{
+ Init(params);
+}
+
+void Thread::Init(const CreateParams& params)
+{
+ // Clear the variables
+ ThreadFlags = 0;
+ ThreadHandle = 0;
+ ExitCode = 0;
+ SuspendCount = 0;
+ StackSize = params.stackSize;
+ Processor = params.processor;
+ Priority = params.priority;
+
+ // Clear Function pointers
+ ThreadFunction = params.threadFunction;
+ UserHandle = params.userHandle;
+ if (params.initialState != NotRunning)
+ Start(params.initialState);
+}
+
+Thread::~Thread()
+{
+ // Thread should not running while object is being destroyed,
+ // this would indicate ref-counting issue.
+ //OVR_ASSERT(IsRunning() == 0);
+
+ // Clean up thread.
+ ThreadHandle = 0;
+}
+
+
+
+// *** Overridable User functions.
+
+// Default Run implementation
+int Thread::Run()
+{
+ // Call pointer to function, if available.
+ return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0;
+}
+void Thread::OnExit()
+{
+}
+
+
+// Finishes the thread and releases internal reference to it.
+void Thread::FinishAndRelease()
+{
+ // Note: thread must be US.
+ ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED);
+ ThreadFlags |= OVR_THREAD_FINISHED;
+
+ // Release our reference; this is equivalent to 'delete this'
+ // from the point of view of our thread.
+ Release();
+}
+
+
+
+// *** ThreadList - used to track all created threads
+
+class ThreadList : public NewOverrideBase
+{
+ //------------------------------------------------------------------------
+ struct ThreadHashOp
+ {
+ size_t operator()(const Thread* ptr)
+ {
+ return (((size_t)ptr) >> 6) ^ (size_t)ptr;
+ }
+ };
+
+ HashSet<Thread*, ThreadHashOp> ThreadSet;
+ Mutex ThreadMutex;
+ WaitCondition ThreadsEmpty;
+ // Track the root thread that created us.
+ pthread_t RootThreadId;
+
+ static ThreadList* volatile pRunningThreads;
+
+ void addThread(Thread *pthread)
+ {
+ Mutex::Locker lock(&ThreadMutex);
+ ThreadSet.Add(pthread);
+ }
+
+ void removeThread(Thread *pthread)
+ {
+ Mutex::Locker lock(&ThreadMutex);
+ ThreadSet.Remove(pthread);
+ if (ThreadSet.GetSize() == 0)
+ ThreadsEmpty.Notify();
+ }
+
+ void finishAllThreads()
+ {
+ // Only original root thread can call this.
+ OVR_ASSERT(pthread_self() == RootThreadId);
+
+ Mutex::Locker lock(&ThreadMutex);
+ while (ThreadSet.GetSize() != 0)
+ ThreadsEmpty.Wait(&ThreadMutex);
+ }
+
+public:
+
+ ThreadList()
+ {
+ RootThreadId = pthread_self();
+ }
+ ~ThreadList() { }
+
+
+ static void AddRunningThread(Thread *pthread)
+ {
+ // Non-atomic creation ok since only the root thread
+ if (!pRunningThreads)
+ {
+ pRunningThreads = new ThreadList;
+ OVR_ASSERT(pRunningThreads);
+ }
+ pRunningThreads->addThread(pthread);
+ }
+
+ // NOTE: 'pthread' might be a dead pointer when this is
+ // called so it should not be accessed; it is only used
+ // for removal.
+ static void RemoveRunningThread(Thread *pthread)
+ {
+ OVR_ASSERT(pRunningThreads);
+ pRunningThreads->removeThread(pthread);
+ }
+
+ static void FinishAllThreads()
+ {
+ // This is ok because only root thread can wait for other thread finish.
+ if (pRunningThreads)
+ {
+ pRunningThreads->finishAllThreads();
+ delete pRunningThreads;
+ pRunningThreads = 0;
+ }
+ }
+};
+
+// By default, we have no thread list.
+ThreadList* volatile ThreadList::pRunningThreads = 0;
+
+
+// FinishAllThreads - exposed publicly in Thread.
+void Thread::FinishAllThreads()
+{
+ ThreadList::FinishAllThreads();
+}
+
+// *** Run override
+
+int Thread::PRun()
+{
+ // Suspend us on start, if requested
+ if (ThreadFlags & OVR_THREAD_START_SUSPENDED)
+ {
+ Suspend();
+ ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED;
+ }
+
+ // Call the virtual run function
+ ExitCode = Run();
+ return ExitCode;
+}
+
+
+
+
+// *** User overridables
+
+bool Thread::GetExitFlag() const
+{
+ return (ThreadFlags & OVR_THREAD_EXIT) != 0;
+}
+
+void Thread::SetExitFlag(bool exitFlag)
+{
+ // The below is atomic since ThreadFlags is AtomicInt.
+ if (exitFlag)
+ ThreadFlags |= OVR_THREAD_EXIT;
+ else
+ ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT;
+}
+
+
+// Determines whether the thread was running and is now finished
+bool Thread::IsFinished() const
+{
+ return (ThreadFlags & OVR_THREAD_FINISHED) != 0;
+}
+// Determines whether the thread is suspended
+bool Thread::IsSuspended() const
+{
+ return SuspendCount > 0;
+}
+// Returns current thread state
+Thread::ThreadState Thread::GetThreadState() const
+{
+ if (IsSuspended())
+ return Suspended;
+ if (ThreadFlags & OVR_THREAD_STARTED)
+ return Running;
+ return NotRunning;
+}
+
+// Join thread
+bool Thread::Join(int maxWaitMs) const
+{
+ // If polling,
+ if (maxWaitMs == 0)
+ {
+ // Just return if finished
+ return IsFinished();
+ }
+ // If waiting forever,
+ else if (maxWaitMs > 0)
+ {
+ UInt32 t0 = Timer::GetTicksMs();
+
+ while (!IsFinished())
+ {
+ UInt32 t1 = Timer::GetTicksMs();
+
+ // If the wait has expired,
+ int delta = (int)(t1 - t0);
+ if (delta >= maxWaitMs)
+ {
+ return false;
+ }
+
+ Thread::MSleep(10);
+ }
+
+ return true;
+ }
+ else
+ {
+ while (!IsFinished())
+ {
+ pthread_join(ThreadHandle, NULL);
+ }
+ }
+
+ return true;
+}
+
+/*
+static const char* mapsched_policy(int policy)
+{
+ switch(policy)
+ {
+ case SCHED_OTHER:
+ return "SCHED_OTHER";
+ case SCHED_RR:
+ return "SCHED_RR";
+ case SCHED_FIFO:
+ return "SCHED_FIFO";
+
+ }
+ return "UNKNOWN";
+}
+ int policy;
+ sched_param sparam;
+ pthread_getschedparam(pthread_self(), &policy, &sparam);
+ int max_prior = sched_get_priority_max(policy);
+ int min_prior = sched_get_priority_min(policy);
+ printf(" !!!! policy: %s, priority: %d, max priority: %d, min priority: %d\n", mapsched_policy(policy), sparam.sched_priority, max_prior, min_prior);
+#include <stdio.h>
+*/
+// ***** Thread management
+
+// The actual first function called on thread start
+void* Thread_PthreadStartFn(void* phandle)
+{
+ Thread* pthread = (Thread*)phandle;
+ int result = pthread->PRun();
+ // Signal the thread as done and release it atomically.
+ pthread->FinishAndRelease();
+ // At this point Thread object might be dead; however we can still pass
+ // it to RemoveRunningThread since it is only used as a key there.
+ ThreadList::RemoveRunningThread(pthread);
+ return reinterpret_cast<void*>(result);
+}
+
+int Thread::InitAttr = 0;
+pthread_attr_t Thread::Attr;
+
+/* static */
+int Thread::GetOSPriority(ThreadPriority p)
+//static inline int MapToSystemPrority(Thread::ThreadPriority p)
+{
+ OVR_UNUSED(p);
+ return -1;
+}
+
+bool Thread::Start(ThreadState initialState)
+{
+ if (initialState == NotRunning)
+ return 0;
+ if (GetThreadState() != NotRunning)
+ {
+ OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this));
+ return 0;
+ }
+
+ if (!InitAttr)
+ {
+ pthread_attr_init(&Attr);
+ pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setstacksize(&Attr, 128 * 1024);
+ sched_param sparam;
+ sparam.sched_priority = Thread::GetOSPriority(NormalPriority);
+ pthread_attr_setschedparam(&Attr, &sparam);
+ InitAttr = 1;
+ }
+
+ ExitCode = 0;
+ SuspendCount = 0;
+ ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED;
+
+ // AddRef to us until the thread is finished
+ AddRef();
+ ThreadList::AddRunningThread(this);
+
+ int result;
+ if (StackSize != 128 * 1024 || Priority != NormalPriority)
+ {
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setstacksize(&attr, StackSize);
+ sched_param sparam;
+ sparam.sched_priority = Thread::GetOSPriority(Priority);
+ pthread_attr_setschedparam(&attr, &sparam);
+ result = pthread_create(&ThreadHandle, &attr, Thread_PthreadStartFn, this);
+ pthread_attr_destroy(&attr);
+ }
+ else
+ result = pthread_create(&ThreadHandle, &Attr, Thread_PthreadStartFn, this);
+
+ if (result)
+ {
+ ThreadFlags = 0;
+ Release();
+ ThreadList::RemoveRunningThread(this);
+ return 0;
+ }
+ return 1;
+}
+
+
+// Suspend the thread until resumed
+bool Thread::Suspend()
+{
+ OVR_DEBUG_LOG(("Thread::Suspend - cannot suspend threads on this system"));
+ return 0;
+}
+
+// Resumes currently suspended thread
+bool Thread::Resume()
+{
+ return 0;
+}
+
+
+// Quits with an exit code
+void Thread::Exit(int exitCode)
+{
+ // Can only exist the current thread
+ // if (GetThread() != this)
+ // return;
+
+ // Call the virtual OnExit function
+ OnExit();
+
+ // Signal this thread object as done and release it's references.
+ FinishAndRelease();
+ ThreadList::RemoveRunningThread(this);
+
+ pthread_exit(reinterpret_cast<void*>(exitCode));
+}
+
+ThreadId GetCurrentThreadId()
+{
+ return (void*)pthread_self();
+}
+
+// *** Sleep functions
+
+/* static */
+bool Thread::Sleep(unsigned secs)
+{
+ sleep(secs);
+ return 1;
+}
+/* static */
+bool Thread::MSleep(unsigned msecs)
+{
+ usleep(msecs*1000);
+ return 1;
+}
+
+/* static */
+int Thread::GetCPUCount()
+{
+ return 1;
+}
+
+
+#if defined (OVR_OS_MAC)
+void Thread::SetThreadName( const char* name )
+{
+ pthread_setname_np( name );
+}
+#else
+void Thread::SetThreadName( const char* name )
+{
+ pthread_setname_np( pthread_self(), name );
+}
+#endif
+
+}
+
+#endif // OVR_ENABLE_THREADS
diff --git a/LibOVR/Src/Net/OVR_Unix_Socket.cpp b/LibOVR/Src/Net/OVR_Unix_Socket.cpp
new file mode 100644
index 0000000..4320a7c
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_Unix_Socket.cpp
@@ -0,0 +1,586 @@
+/************************************************************************************
+
+Filename : OVR_Unix_Socket.cpp
+Content : Berkley sockets networking implementation
+Created : July 1, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "OVR_Unix_Socket.h"
+#include "../Kernel/OVR_Std.h"
+#include "../Kernel/OVR_Allocator.h"
+#include "../Kernel/OVR_Threads.h" // Thread::MSleep
+#include "../Kernel/OVR_Log.h"
+
+#include <errno.h>
+
+namespace OVR { namespace Net {
+
+//-----------------------------------------------------------------------------
+// BerkleySocket
+
+void BerkleySocket::Close()
+{
+ if (TheSocket != INVALID_SOCKET)
+ {
+ close(TheSocket);
+ TheSocket = INVALID_SOCKET;
+ }
+}
+
+SInt32 BerkleySocket::GetSockname(SockAddr *pSockAddrOut)
+{
+ struct sockaddr_in6 sa;
+ memset(&sa,0,sizeof(sa));
+ socklen_t size = sizeof(sa);
+ SInt32 i = getsockname(TheSocket, (sockaddr*)&sa, &size);
+ if (i>=0)
+ {
+ pSockAddrOut->Set(&sa);
+ }
+ return i;
+}
+
+
+//-----------------------------------------------------------------------------
+// BitStream overloads for SockAddr
+
+BitStream& operator<<(BitStream& out, SockAddr& in)
+{
+ out.WriteBits((const unsigned char*) &in.Addr6, sizeof(in.Addr6)*8, true);
+ return out;
+}
+
+BitStream& operator>>(BitStream& in, SockAddr& out)
+{
+ bool success = in.ReadBits((unsigned char*) &out.Addr6, sizeof(out.Addr6)*8, true);
+ OVR_ASSERT(success);
+ OVR_UNUSED(success);
+ return in;
+}
+
+
+//-----------------------------------------------------------------------------
+// SockAddr
+
+SockAddr::SockAddr()
+{
+}
+
+SockAddr::SockAddr(SockAddr* address)
+{
+ Set(&address->Addr6);
+}
+
+SockAddr::SockAddr(sockaddr_storage* storage)
+{
+ Set(storage);
+}
+
+SockAddr::SockAddr(sockaddr_in6* address)
+{
+ Set(address);
+}
+
+SockAddr::SockAddr(const char* hostAddress, UInt16 port, int sockType)
+{
+ Set(hostAddress, port, sockType);
+}
+
+void SockAddr::Set(const sockaddr_storage* storage)
+{
+ memcpy(&Addr6, storage, sizeof(Addr6));
+}
+
+void SockAddr::Set(const sockaddr_in6* address)
+{
+ memcpy(&Addr6, address, sizeof(Addr6));
+}
+
+void SockAddr::Set(const char* hostAddress, UInt16 port, int sockType)
+{
+ memset(&Addr6, 0, sizeof(Addr6));
+
+ struct addrinfo* servinfo = 0; // will point to the results
+ struct addrinfo hints;
+
+ // make sure the struct is empty
+ memset(&hints, 0, sizeof (addrinfo));
+
+ hints.ai_socktype = sockType; // SOCK_DGRAM or SOCK_STREAM
+ hints.ai_flags = AI_PASSIVE; // fill in my IP for me
+ hints.ai_family = AF_UNSPEC ;
+
+ if (SOCK_DGRAM == sockType)
+ {
+ hints.ai_protocol = IPPROTO_UDP;
+ }
+ else if (SOCK_STREAM == sockType)
+ {
+ hints.ai_protocol = IPPROTO_TCP;
+ }
+
+ char portStr[32];
+ OVR_itoa(port, portStr, sizeof(portStr), 10);
+ int errcode = getaddrinfo(hostAddress, portStr, &hints, &servinfo);
+
+ if (0 != errcode)
+ {
+ OVR::LogError("getaddrinfo error: %s", gai_strerror(errcode));
+ }
+
+ OVR_ASSERT(0 != servinfo);
+
+ memcpy(&Addr6, servinfo->ai_addr, sizeof(Addr6));
+
+ freeaddrinfo(servinfo);
+}
+
+UInt16 SockAddr::GetPort()
+{
+ return htons(Addr6.sin6_port);
+}
+
+String SockAddr::ToString(bool writePort, char portDelineator) const
+{
+ char dest[INET6_ADDRSTRLEN + 1];
+
+ int ret = getnameinfo((struct sockaddr*)&Addr6,
+ sizeof(struct sockaddr_in6),
+ dest,
+ INET6_ADDRSTRLEN,
+ NULL,
+ 0,
+ NI_NUMERICHOST);
+ if (ret != 0)
+ {
+ dest[0] = '\0';
+ }
+
+ if (writePort)
+ {
+ unsigned char ch[2];
+ ch[0]=portDelineator;
+ ch[1]=0;
+ OVR_strcat(dest, 16, (const char*) ch);
+ OVR_itoa(ntohs(Addr6.sin6_port), dest+strlen(dest), 16, 10);
+ }
+
+ return String(dest);
+}
+bool SockAddr::IsLocalhost() const
+{
+ static const unsigned char localhost_bytes[] =
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+
+ return memcmp(Addr6.sin6_addr.s6_addr, localhost_bytes, 16) == 0;
+}
+bool SockAddr::operator==( const SockAddr& right ) const
+{
+ return memcmp(&Addr6, &right.Addr6, sizeof(Addr6)) == 0;
+}
+
+bool SockAddr::operator!=( const SockAddr& right ) const
+{
+ return !(*this == right);
+}
+
+bool SockAddr::operator>( const SockAddr& right ) const
+{
+ return memcmp(&Addr6, &right.Addr6, sizeof(Addr6)) > 0;
+}
+
+bool SockAddr::operator<( const SockAddr& right ) const
+{
+ return memcmp(&Addr6, &right.Addr6, sizeof(Addr6)) < 0;
+}
+
+
+// Returns true on success
+static bool SetSocketOptions(SocketHandle sock)
+{
+ bool failed = false;
+ int sock_opt;
+ int sockError = 0;
+
+ // This doubles the max throughput rate
+ sock_opt=1024*256;
+ sockError = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) );
+ if (sockError != 0)
+ {
+ int errsv = errno;
+ OVR::LogError("[Socket] Failed SO_RCVBUF setsockopt, errno: %d", errsv);
+ failed = true;
+ }
+
+ // This doesn't make much difference: 10% maybe
+ // Not supported on console 2
+ sock_opt=1024*16;
+ sockError = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) );
+ if (sockError != 0)
+ {
+ int errsv = errno;
+ OVR::LogError("[Socket] Failed SO_SNDBUF setsockopt, errno: %d", errsv);
+ failed = true;
+ }
+
+ int value = 1;
+ sockError = setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value));
+ if (sockError != 0)
+ {
+ int errsv = errno;
+ OVR::LogError("[Socket] Failed SO_NOSIGPIPE setsockopt, errno: %d", errsv);
+ failed = true;
+ }
+
+ // Reuse address is only needed for posix platforms, as it is the default
+ // on Windows platforms.
+ int optval = 1;
+ sockError = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int));
+ if (sockError != 0)
+ {
+ int errsv = errno;
+ OVR::LogError("[Socket] Failed SO_REUSEADDR setsockopt, errno: %d", errsv);
+ failed = true;
+ }
+
+ return !failed;
+}
+
+void _Ioctlsocket(SocketHandle sock, unsigned long nonblocking)
+{
+ int flags = fcntl(sock, F_GETFL, 0);
+ if (flags < 0) return; // return false
+ if (nonblocking == 0) { flags &= ~O_NONBLOCK; }
+ else { flags |= O_NONBLOCK; }
+ fcntl(sock, F_SETFL, flags);
+}
+
+static SocketHandle BindShared(int ai_family, int ai_socktype, BerkleyBindParameters *pBindParameters)
+{
+ SocketHandle sock;
+
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof (addrinfo)); // make sure the struct is empty
+ hints.ai_family = ai_family;
+ hints.ai_socktype = ai_socktype;
+ hints.ai_flags = AI_PASSIVE; // fill in my IP for me
+ struct addrinfo *servinfo=0, *aip; // will point to the results
+ char portStr[32];
+ OVR_itoa(pBindParameters->Port, portStr, sizeof(portStr), 10);
+
+ int errcode = 0;
+ if (!pBindParameters->Address.IsEmpty())
+ errcode = getaddrinfo(pBindParameters->Address.ToCStr(), portStr, &hints, &servinfo);
+ else
+ errcode = getaddrinfo(0, portStr, &hints, &servinfo);
+
+ if (0 != errcode)
+ {
+ OVR::LogError("getaddrinfo error: %s", gai_strerror(errcode));
+ }
+
+ for (aip = servinfo; aip != NULL; aip = aip->ai_next)
+ {
+ // Open socket. The address type depends on what
+ // getaddrinfo() gave us.
+ sock = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
+ if (sock != 0)
+ {
+ SetSocketOptions(sock);
+ int ret = bind( sock, aip->ai_addr, (int) aip->ai_addrlen );
+ if (ret>=0)
+ {
+ // The actual socket is always non-blocking
+ // I control blocking or not using WSAEventSelect
+ _Ioctlsocket(sock, 1);
+ freeaddrinfo(servinfo);
+ return sock;
+ }
+ else
+ {
+ close(sock);
+ }
+ }
+ }
+
+ if (servinfo) { freeaddrinfo(servinfo); }
+ return INVALID_SOCKET;
+}
+
+
+//-----------------------------------------------------------------------------
+// UDPSocket
+
+UDPSocket::UDPSocket()
+{
+ RecvBuf = new UByte[RecvBufSize];
+}
+
+UDPSocket::~UDPSocket()
+{
+ delete[] RecvBuf;
+}
+
+SocketHandle UDPSocket::Bind(BerkleyBindParameters *pBindParameters)
+{
+ SocketHandle s = BindShared(AF_INET6, SOCK_DGRAM, pBindParameters);
+ if (s < 0)
+ return s;
+
+ Close();
+ TheSocket = s;
+
+ return TheSocket;
+}
+
+void UDPSocket::OnRecv(SocketEvent_UDP* eventHandler, UByte* pData, int bytesRead, SockAddr* address)
+{
+ eventHandler->UDP_OnRecv(this, pData, bytesRead, address);
+}
+
+int UDPSocket::Send(const void* pData, int bytes, SockAddr* address)
+{
+ return (int)sendto(TheSocket, (const char*)pData, bytes, 0, (const sockaddr*)&address->Addr6, sizeof(address->Addr6));
+}
+
+void UDPSocket::Poll(SocketEvent_UDP *eventHandler)
+{
+ struct sockaddr_storage win32_addr;
+ socklen_t fromlen;
+ int bytesRead;
+
+ // FIXME: Implement blocking poll wait for UDP
+
+ // While some bytes are read,
+ while (fromlen = sizeof(win32_addr), // Must set fromlen each time
+ bytesRead = (int)recvfrom(TheSocket, (char*)RecvBuf, RecvBufSize, 0, (sockaddr*)&win32_addr, &fromlen),
+ bytesRead > 0)
+ {
+ SockAddr address(&win32_addr); // Wrap address
+
+ OnRecv(eventHandler, RecvBuf, bytesRead, &address);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// TCPSocket
+
+TCPSocket::TCPSocket()
+{
+ IsConnecting = false;
+ IsListenSocket = false;
+}
+TCPSocket::TCPSocket(SocketHandle boundHandle, bool isListenSocket)
+{
+ TheSocket = boundHandle;
+ IsListenSocket = isListenSocket;
+ IsConnecting = false;
+ SetSocketOptions(TheSocket);
+
+ // The actual socket is always non-blocking
+ _Ioctlsocket(TheSocket, 1);
+}
+
+TCPSocket::~TCPSocket()
+{
+}
+
+void TCPSocket::OnRecv(SocketEvent_TCP* eventHandler, UByte* pData, int bytesRead)
+{
+ eventHandler->TCP_OnRecv(this, pData, bytesRead);
+}
+
+SocketHandle TCPSocket::Bind(BerkleyBindParameters* pBindParameters)
+{
+ SocketHandle s = BindShared(AF_INET6, SOCK_STREAM, pBindParameters);
+ if (s < 0)
+ return s;
+
+ Close();
+
+ SetBlockingTimeout(pBindParameters->blockingTimeout);
+ TheSocket = s;
+
+ return TheSocket;
+}
+
+int TCPSocket::Listen()
+{
+ if (IsListenSocket)
+ {
+ return 0;
+ }
+
+ int i = listen(TheSocket, SOMAXCONN);
+ if (i >= 0)
+ {
+ IsListenSocket = true;
+ }
+
+ return i;
+}
+
+int TCPSocket::Connect(SockAddr* address)
+{
+ int retval;
+
+ retval = connect(TheSocket, (struct sockaddr *) &address->Addr6, sizeof(address->Addr6));
+ if (retval < 0)
+ {
+ int errsv = errno;
+ // EINPROGRESS should not be checked on windows but should
+ // be checked on POSIX platforms.
+ if (errsv == EWOULDBLOCK || errsv == EINPROGRESS)
+ {
+ IsConnecting = true;
+ return 0;
+ }
+
+ OVR::LogText( "TCPSocket::Connect failed:Error code - %d\n", errsv );
+ }
+
+ return retval;
+}
+
+int TCPSocket::Send(const void* pData, int bytes)
+{
+ if (bytes <= 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return (int)send(TheSocket, (const char*)pData, bytes, 0);
+ }
+}
+
+
+//// TCPSocketPollState
+
+TCPSocketPollState::TCPSocketPollState()
+{
+ FD_ZERO(&readFD);
+ FD_ZERO(&exceptionFD);
+ FD_ZERO(&writeFD);
+ largestDescriptor = INVALID_SOCKET;
+}
+
+bool TCPSocketPollState::IsValid() const
+{
+ return largestDescriptor != INVALID_SOCKET;
+}
+
+void TCPSocketPollState::Add(TCPSocket* tcpSocket)
+{
+ if (!tcpSocket)
+ {
+ return;
+ }
+
+ SocketHandle handle = tcpSocket->GetSocketHandle();
+
+ if (handle == INVALID_SOCKET)
+ {
+ return;
+ }
+
+ if (largestDescriptor == INVALID_SOCKET ||
+ largestDescriptor < handle)
+ {
+ largestDescriptor = handle;
+ }
+
+ FD_SET(handle, &readFD);
+ FD_SET(handle, &exceptionFD);
+
+ if (tcpSocket->IsConnecting)
+ {
+ FD_SET(handle, &writeFD);
+ }
+}
+
+bool TCPSocketPollState::Poll(long usec, long seconds)
+{
+ timeval tv;
+ tv.tv_sec = seconds;
+ tv.tv_usec = (int)usec;
+
+ return select(largestDescriptor + 1, &readFD, &writeFD, &exceptionFD, &tv) > 0;
+}
+
+void TCPSocketPollState::HandleEvent(TCPSocket* tcpSocket, SocketEvent_TCP* eventHandler)
+{
+ if (!tcpSocket || !eventHandler)
+ {
+ return;
+ }
+
+ SocketHandle handle = tcpSocket->GetSocketHandle();
+
+ if (tcpSocket->IsConnecting && FD_ISSET(handle, &writeFD))
+ {
+ tcpSocket->IsConnecting = false;
+ eventHandler->TCP_OnConnected(tcpSocket);
+ }
+
+ if (FD_ISSET(handle, &readFD))
+ {
+ if (!tcpSocket->IsListenSocket)
+ {
+ static const int BUFF_SIZE = 8096;
+ char data[BUFF_SIZE];
+
+ int bytesRead = (int)recv(handle, data, BUFF_SIZE, 0);
+ if (bytesRead > 0)
+ {
+ tcpSocket->OnRecv(eventHandler, (UByte*)data, bytesRead);
+ }
+ else // Disconnection event:
+ {
+ tcpSocket->IsConnecting = false;
+ eventHandler->TCP_OnClosed(tcpSocket);
+ }
+ }
+ else
+ {
+ struct sockaddr_storage sockAddr;
+ socklen_t sockAddrSize = sizeof(sockAddr);
+
+ SocketHandle newSock = accept(handle, (sockaddr*)&sockAddr, (socklen_t*)&sockAddrSize);
+ if (newSock > 0)
+ {
+ SockAddr sa(&sockAddr);
+ eventHandler->TCP_OnAccept(tcpSocket, &sa, newSock);
+ }
+ }
+ }
+
+ if (FD_ISSET(handle, &exceptionFD))
+ {
+ tcpSocket->IsConnecting = false;
+ eventHandler->TCP_OnClosed(tcpSocket);
+ }
+}
+
+
+}} // namespace OVR::Net
diff --git a/LibOVR/Src/Net/OVR_Unix_Socket.h b/LibOVR/Src/Net/OVR_Unix_Socket.h
new file mode 100644
index 0000000..faec464
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_Unix_Socket.h
@@ -0,0 +1,152 @@
+/************************************************************************************
+
+PublicHeader: n/a
+Filename : OVR_Unix_Socket.h
+Content : Berkley sockets networking implementation
+Created : July 1, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Unix_Socket_h
+#define OVR_Unix_Socket_h
+
+#include "OVR_Socket.h"
+#include "OVR_BitStream.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <fcntl.h>
+
+namespace OVR { namespace Net {
+
+//-----------------------------------------------------------------------------
+// SockAddr
+
+// Abstraction for IPV6 socket address, with various convenience functions
+class SockAddr
+{
+public:
+ SockAddr();
+ SockAddr(SockAddr* sa);
+ SockAddr(sockaddr_storage* sa);
+ SockAddr(sockaddr_in6* sa);
+ SockAddr(const char* hostAddress, UInt16 port, int sockType);
+
+public:
+ void Set(const sockaddr_storage* sa);
+ void Set(const sockaddr_in6* sa);
+ void Set(const char* hostAddress, UInt16 port, int sockType); // SOCK_DGRAM or SOCK_STREAM
+
+ UInt16 GetPort();
+
+ String ToString(bool writePort, char portDelineator) const;
+
+ bool IsLocalhost() const;
+
+ void Serialize(BitStream* bs);
+ bool Deserialize(BitStream);
+
+ bool operator==( const SockAddr& right ) const;
+ bool operator!=( const SockAddr& right ) const;
+ bool operator >( const SockAddr& right ) const;
+ bool operator <( const SockAddr& right ) const;
+
+public:
+ sockaddr_in6 Addr6;
+};
+
+
+//-----------------------------------------------------------------------------
+// UDP Socket
+
+// Windows version of TCP socket
+class UDPSocket : public UDPSocketBase
+{
+public:
+ UDPSocket();
+ virtual ~UDPSocket();
+
+public:
+ virtual SocketHandle Bind(BerkleyBindParameters* pBindParameters);
+ virtual int Send(const void* pData, int bytes, SockAddr* address);
+ virtual void Poll(SocketEvent_UDP* eventHandler);
+
+protected:
+ static const int RecvBufSize = 1048576;
+ UByte* RecvBuf;
+
+ virtual void OnRecv(SocketEvent_UDP* eventHandler, UByte* pData,
+ int bytesRead, SockAddr* address);
+};
+
+
+//-----------------------------------------------------------------------------
+// TCP Socket
+
+// Windows version of TCP socket
+class TCPSocket : public TCPSocketBase
+{
+ friend class TCPSocketPollState;
+
+public:
+ TCPSocket();
+ TCPSocket(SocketHandle boundHandle, bool isListenSocket);
+ virtual ~TCPSocket();
+
+public:
+ virtual SocketHandle Bind(BerkleyBindParameters* pBindParameters);
+ virtual int Listen();
+ virtual int Connect(SockAddr* address);
+ virtual int Send(const void* pData, int bytes);
+
+protected:
+ virtual void OnRecv(SocketEvent_TCP* eventHandler, UByte* pData,
+ int bytesRead);
+
+public:
+ bool IsConnecting; // Is in the process of connecting?
+};
+
+
+//-----------------------------------------------------------------------------
+// TCPSocketPollState
+
+// Polls multiple blocking TCP sockets at once
+class TCPSocketPollState
+{
+ fd_set readFD, exceptionFD, writeFD;
+ SocketHandle largestDescriptor;
+
+public:
+ TCPSocketPollState();
+ bool IsValid() const;
+ void Add(TCPSocket* tcpSocket);
+ bool Poll(long usec = 30000, long seconds = 0);
+ void HandleEvent(TCPSocket* tcpSocket, SocketEvent_TCP* eventHandler);
+};
+
+
+}} // OVR::Net
+
+#endif