# HG changeset patch # User John Tsiombikas # Date 1379164499 -10800 # Node ID e2f9e460312951118e5c5d500ce21f3ad400ef14 # Parent c7b50cd7184c901b46f054c822c80bc1cd947bde added LibOVR and started a simple vr wrapper. diff -r c7b50cd7184c -r e2f9e4603129 .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,4 @@ +\.o$ +\.d$ +\.swp$ +^oculus1$ diff -r c7b50cd7184c -r e2f9e4603129 Makefile --- a/Makefile Fri Aug 30 06:08:34 2013 +0300 +++ b/Makefile Sat Sep 14 16:14:59 2013 +0300 @@ -1,16 +1,26 @@ src = $(wildcard src/*.cc) -obj = $(src:.cc=.o) +ovr_src = $(wildcard libovr/Src/*.cpp) \ + $(wildcard libovr/Src/Kernel/*.cpp) \ + $(wildcard libovr/Src/Util/*.cpp) + +obj = $(src:.cc=.o) $(ovr_src:.cpp=.o) $(ovr_sys_src:.cpp=.o) bin = oculus1 -CXXFLAGS = -pedantic -Wall -g -I/usr/local/include + +CXXFLAGS = -Wall -g -I/usr/local/include $(ovr_include) -DUSE_OVR LDFLAGS = -L/usr/local/lib $(libgl) $(ovrlibs) -lm ifeq ($(shell uname -s), Darwin) libgl = -framework OpenGL -framework GLUT -lGLEW - ovrlibs = -lovr -framework CoreFoundation -framework ApplicationServices -framework IOKit + ovrlibs = -framework CoreFoundation -framework ApplicationServices -framework IOKit + + ovr_include = -Ilibovr/Include -Ilibovr/Src -Ilibovr/Src/osx + ovr_sys_src = $(wildcard libovr/Src/osx/*.cpp) else libgl = -lGL -lGLU -lglut -lGLEW - ovrlibs = -lovr + + ovr_include = -Ilibovr/Include -Ilibovr/Src -Ilibovr/Src/linux + ovr_sys_src = $(wildcard libovr/Src/linux/*.cpp) endif $(bin): $(obj) diff -r c7b50cd7184c -r e2f9e4603129 libovr/Include/OVR.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Include/OVR.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,34 @@ +/************************************************************************************ + +Filename : OVR.h +Content : This contains references to all OVR-specific headers in Src folder. + Should be generated automatically based on PublicHeader tags. + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_h +#define OVR_h + +#include "../Src/Kernel/OVR_Allocator.h" +#include "../Src/Kernel/OVR_Log.h" +#include "../Src/Kernel/OVR_Math.h" +#include "../Src/Kernel/OVR_System.h" +#include "../Src/Kernel/OVR_Types.h" +#include "../Src/OVR_Device.h" +#include "../Src/OVR_DeviceConstants.h" +#include "../Src/OVR_DeviceHandle.h" +#include "../Src/OVR_DeviceMessages.h" +#include "../Src/OVR_SensorFusion.h" +#include "../Src/OVR_Profile.h" +#include "../Src/Util/Util_LatencyTest.h" +#include "../Src/Util/Util_Render_Stereo.h" +#include "../Src/Util/Util_MagCalibration.h" + +#endif + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Include/OVRVersion.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Include/OVRVersion.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,22 @@ +/************************************************************************************ + +Filename : OVRVersion.h +Content : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef _OVR_VERSION_H +#define _OVR_VERSION_H + +#define OVR_MAJOR_VERSION 0 +#define OVR_MINOR_VERSION 2 +#define OVR_BUILD_VERSION 4 +#define OVR_VERSION_STRING "0.2.4" + +#endif diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Alg.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Alg.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : OVR_Alg.cpp Content : Static lookup tables for Alg functions Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_Types.h" namespace OVR { namespace Alg { //------------------------------------------------------------------------ extern const UByte UpperBitTable[256] = { 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 }; extern const UByte LowerBitTable[256] = { 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 }; }} // OVE::Alg \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Alg.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Alg.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Alg.h Content : Simple general purpose algorithms: Sort, Binary Search, etc. Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Alg_h #define OVR_Alg_h #include "OVR_Types.h" #include namespace OVR { namespace Alg { //----------------------------------------------------------------------------------- // ***** Operator extensions template OVR_FORCE_INLINE void Swap(T &a, T &b) { T temp(a); a = b; b = temp; } // ***** min/max are not implemented in Visual Studio 6 standard STL template OVR_FORCE_INLINE const T Min(const T a, const T b) { return (a < b) ? a : b; } template OVR_FORCE_INLINE const T Max(const T a, const T b) { return (b < a) ? a : b; } template OVR_FORCE_INLINE const T Clamp(const T v, const T minVal, const T maxVal) { return Max(minVal, Min(v, maxVal)); } template OVR_FORCE_INLINE int Chop(T f) { return (int)f; } template OVR_FORCE_INLINE T Lerp(T a, T b, T f) { return (b - a) * f + a; } // These functions stand to fix a stupid VC++ warning (with /Wp64 on): // "warning C4267: 'argument' : conversion from 'size_t' to 'const unsigned', possible loss of data" // Use these functions instead of gmin/gmax if the argument has size // of the pointer to avoid the warning. Though, functionally they are // absolutelly the same as regular gmin/gmax. template OVR_FORCE_INLINE const T PMin(const T a, const T b) { OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt)); return (a < b) ? a : b; } template OVR_FORCE_INLINE const T PMax(const T a, const T b) { OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt)); return (b < a) ? a : b; } template OVR_FORCE_INLINE const T Abs(const T v) { return (v>=0) ? v : -v; } //----------------------------------------------------------------------------------- // ***** OperatorLess // template struct OperatorLess { static bool Compare(const T& a, const T& b) { return a < b; } }; //----------------------------------------------------------------------------------- // ***** QuickSortSliced // // Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. // The range is specified with start, end, where "end" is exclusive! // The comparison predicate must be specified. template void QuickSortSliced(Array& arr, UPInt start, UPInt end, Less less) { enum { Threshold = 9 }; if(end - start < 2) return; SPInt stack[80]; SPInt* top = stack; SPInt base = (SPInt)start; SPInt limit = (SPInt)end; for(;;) { SPInt len = limit - base; SPInt i, j, pivot; if(len > Threshold) { // we use base + len/2 as the pivot pivot = base + len / 2; Swap(arr[base], arr[pivot]); i = base + 1; j = limit - 1; // now ensure that *i <= *base <= *j if(less(arr[j], arr[i])) Swap(arr[j], arr[i]); if(less(arr[base], arr[i])) Swap(arr[base], arr[i]); if(less(arr[j], arr[base])) Swap(arr[j], arr[base]); for(;;) { do i++; while( less(arr[i], arr[base]) ); do j--; while( less(arr[base], arr[j]) ); if( i > j ) { break; } Swap(arr[i], arr[j]); } Swap(arr[base], arr[j]); // now, push the largest sub-array if(j - base > limit - i) { top[0] = base; top[1] = j; base = i; } else { top[0] = i; top[1] = limit; limit = j; } top += 2; } else { // the sub-array is small, perform insertion sort j = base; i = j + 1; for(; i < limit; j = i, i++) { for(; less(arr[j + 1], arr[j]); j--) { Swap(arr[j + 1], arr[j]); if(j == base) { break; } } } if(top > stack) { top -= 2; base = top[0]; limit = top[1]; } else { break; } } } } //----------------------------------------------------------------------------------- // ***** QuickSortSliced // // Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. // The range is specified with start, end, where "end" is exclusive! // The data type must have a defined "<" operator. template void QuickSortSliced(Array& arr, UPInt start, UPInt end) { typedef typename Array::ValueType ValueType; QuickSortSliced(arr, start, end, OperatorLess::Compare); } // Same as corresponding G_QuickSortSliced but with checking array limits to avoid // crash in the case of wrong comparator functor. template bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end, Less less) { enum { Threshold = 9 }; if(end - start < 2) return true; SPInt stack[80]; SPInt* top = stack; SPInt base = (SPInt)start; SPInt limit = (SPInt)end; for(;;) { SPInt len = limit - base; SPInt i, j, pivot; if(len > Threshold) { // we use base + len/2 as the pivot pivot = base + len / 2; Swap(arr[base], arr[pivot]); i = base + 1; j = limit - 1; // now ensure that *i <= *base <= *j if(less(arr[j], arr[i])) Swap(arr[j], arr[i]); if(less(arr[base], arr[i])) Swap(arr[base], arr[i]); if(less(arr[j], arr[base])) Swap(arr[j], arr[base]); for(;;) { do { i++; if (i >= limit) return false; } while( less(arr[i], arr[base]) ); do { j--; if (j < 0) return false; } while( less(arr[base], arr[j]) ); if( i > j ) { break; } Swap(arr[i], arr[j]); } Swap(arr[base], arr[j]); // now, push the largest sub-array if(j - base > limit - i) { top[0] = base; top[1] = j; base = i; } else { top[0] = i; top[1] = limit; limit = j; } top += 2; } else { // the sub-array is small, perform insertion sort j = base; i = j + 1; for(; i < limit; j = i, i++) { for(; less(arr[j + 1], arr[j]); j--) { Swap(arr[j + 1], arr[j]); if(j == base) { break; } } } if(top > stack) { top -= 2; base = top[0]; limit = top[1]; } else { break; } } } return true; } template bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end) { typedef typename Array::ValueType ValueType; return QuickSortSlicedSafe(arr, start, end, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** QuickSort // // Sort an array Array, ArrayPaged, ArrayUnsafe. // The array must have GetSize() function. // The comparison predicate must be specified. template void QuickSort(Array& arr, Less less) { QuickSortSliced(arr, 0, arr.GetSize(), less); } // checks for boundaries template bool QuickSortSafe(Array& arr, Less less) { return QuickSortSlicedSafe(arr, 0, arr.GetSize(), less); } //----------------------------------------------------------------------------------- // ***** QuickSort // // Sort an array Array, ArrayPaged, ArrayUnsafe. // The array must have GetSize() function. // The data type must have a defined "<" operator. template void QuickSort(Array& arr) { typedef typename Array::ValueType ValueType; QuickSortSliced(arr, 0, arr.GetSize(), OperatorLess::Compare); } template bool QuickSortSafe(Array& arr) { typedef typename Array::ValueType ValueType; return QuickSortSlicedSafe(arr, 0, arr.GetSize(), OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** InsertionSortSliced // // Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. // The range is specified with start, end, where "end" is exclusive! // The comparison predicate must be specified. // Unlike Quick Sort, the Insertion Sort works much slower in average, // but may be much faster on almost sorted arrays. Besides, it guarantees // that the elements will not be swapped if not necessary. For example, // an array with all equal elements will remain "untouched", while // Quick Sort will considerably shuffle the elements in this case. template void InsertionSortSliced(Array& arr, UPInt start, UPInt end, Less less) { UPInt j = start; UPInt i = j + 1; UPInt limit = end; for(; i < limit; j = i, i++) { for(; less(arr[j + 1], arr[j]); j--) { Swap(arr[j + 1], arr[j]); if(j <= start) { break; } } } } //----------------------------------------------------------------------------------- // ***** InsertionSortSliced // // Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. // The range is specified with start, end, where "end" is exclusive! // The data type must have a defined "<" operator. template void InsertionSortSliced(Array& arr, UPInt start, UPInt end) { typedef typename Array::ValueType ValueType; InsertionSortSliced(arr, start, end, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** InsertionSort // // Sort an array Array, ArrayPaged, ArrayUnsafe. // The array must have GetSize() function. // The comparison predicate must be specified. template void InsertionSort(Array& arr, Less less) { InsertionSortSliced(arr, 0, arr.GetSize(), less); } //----------------------------------------------------------------------------------- // ***** InsertionSort // // Sort an array Array, ArrayPaged, ArrayUnsafe. // The array must have GetSize() function. // The data type must have a defined "<" operator. template void InsertionSort(Array& arr) { typedef typename Array::ValueType ValueType; InsertionSortSliced(arr, 0, arr.GetSize(), OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** LowerBoundSliced // template UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less) { SPInt first = (SPInt)start; SPInt len = (SPInt)(end - start); SPInt half; SPInt middle; while(len > 0) { half = len >> 1; middle = first + half; if(less(arr[middle], val)) { first = middle + 1; len = len - half - 1; } else { len = half; } } return (UPInt)first; } //----------------------------------------------------------------------------------- // ***** LowerBoundSliced // template UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val) { return LowerBoundSliced(arr, start, end, val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** LowerBoundSized // template UPInt LowerBoundSized(const Array& arr, UPInt size, const Value& val) { return LowerBoundSliced(arr, 0, size, val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** LowerBound // template UPInt LowerBound(const Array& arr, const Value& val, Less less) { return LowerBoundSliced(arr, 0, arr.GetSize(), val, less); } //----------------------------------------------------------------------------------- // ***** LowerBound // template UPInt LowerBound(const Array& arr, const Value& val) { return LowerBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** UpperBoundSliced // template UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less) { SPInt first = (SPInt)start; SPInt len = (SPInt)(end - start); SPInt half; SPInt middle; while(len > 0) { half = len >> 1; middle = first + half; if(less(val, arr[middle])) { len = half; } else { first = middle + 1; len = len - half - 1; } } return (UPInt)first; } //----------------------------------------------------------------------------------- // ***** UpperBoundSliced // template UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val) { return UpperBoundSliced(arr, start, end, val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** UpperBoundSized // template UPInt UpperBoundSized(const Array& arr, UPInt size, const Value& val) { return UpperBoundSliced(arr, 0, size, val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** UpperBound // template UPInt UpperBound(const Array& arr, const Value& val, Less less) { return UpperBoundSliced(arr, 0, arr.GetSize(), val, less); } //----------------------------------------------------------------------------------- // ***** UpperBound // template UPInt UpperBound(const Array& arr, const Value& val) { return UpperBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** ReverseArray // template void ReverseArray(Array& arr) { SPInt from = 0; SPInt to = arr.GetSize() - 1; while(from < to) { Swap(arr[from], arr[to]); ++from; --to; } } // ***** AppendArray // template void AppendArray(CDst& dst, const CSrc& src) { UPInt i; for(i = 0; i < src.GetSize(); i++) dst.PushBack(src[i]); } //----------------------------------------------------------------------------------- // ***** ArrayAdaptor // // A simple adapter that provides the GetSize() method and overloads // operator []. Used to wrap plain arrays in QuickSort and such. template class ArrayAdaptor { public: typedef T ValueType; ArrayAdaptor() : Data(0), Size(0) {} ArrayAdaptor(T* ptr, UPInt size) : Data(ptr), Size(size) {} UPInt GetSize() const { return Size; } const T& operator [] (UPInt i) const { return Data[i]; } T& operator [] (UPInt i) { return Data[i]; } private: T* Data; UPInt Size; }; //----------------------------------------------------------------------------------- // ***** GConstArrayAdaptor // // A simple const adapter that provides the GetSize() method and overloads // operator []. Used to wrap plain arrays in LowerBound and such. template class ConstArrayAdaptor { public: typedef T ValueType; ConstArrayAdaptor() : Data(0), Size(0) {} ConstArrayAdaptor(const T* ptr, UPInt size) : Data(ptr), Size(size) {} UPInt GetSize() const { return Size; } const T& operator [] (UPInt i) const { return Data[i]; } private: const T* Data; UPInt Size; }; //----------------------------------------------------------------------------------- extern const UByte UpperBitTable[256]; extern const UByte LowerBitTable[256]; //----------------------------------------------------------------------------------- inline UByte UpperBit(UPInt val) { #ifndef OVR_64BIT_POINTERS if (val & 0xFFFF0000) { return (val & 0xFF000000) ? UpperBitTable[(val >> 24) ] + 24: UpperBitTable[(val >> 16) & 0xFF] + 16; } return (val & 0xFF00) ? UpperBitTable[(val >> 8) & 0xFF] + 8: UpperBitTable[(val ) & 0xFF]; #else if (val & 0xFFFFFFFF00000000) { if (val & 0xFFFF000000000000) { return (val & 0xFF00000000000000) ? UpperBitTable[(val >> 56) ] + 56: UpperBitTable[(val >> 48) & 0xFF] + 48; } return (val & 0xFF0000000000) ? UpperBitTable[(val >> 40) & 0xFF] + 40: UpperBitTable[(val >> 32) & 0xFF] + 32; } else { if (val & 0xFFFF0000) { return (val & 0xFF000000) ? UpperBitTable[(val >> 24) ] + 24: UpperBitTable[(val >> 16) & 0xFF] + 16; } return (val & 0xFF00) ? UpperBitTable[(val >> 8) & 0xFF] + 8: UpperBitTable[(val ) & 0xFF]; } #endif } //----------------------------------------------------------------------------------- inline UByte LowerBit(UPInt val) { #ifndef OVR_64BIT_POINTERS if (val & 0xFFFF) { return (val & 0xFF) ? LowerBitTable[ val & 0xFF]: LowerBitTable[(val >> 8) & 0xFF] + 8; } return (val & 0xFF0000) ? LowerBitTable[(val >> 16) & 0xFF] + 16: LowerBitTable[(val >> 24) & 0xFF] + 24; #else if (val & 0xFFFFFFFF) { if (val & 0xFFFF) { return (val & 0xFF) ? LowerBitTable[ val & 0xFF]: LowerBitTable[(val >> 8) & 0xFF] + 8; } return (val & 0xFF0000) ? LowerBitTable[(val >> 16) & 0xFF] + 16: LowerBitTable[(val >> 24) & 0xFF] + 24; } else { if (val & 0xFFFF00000000) { return (val & 0xFF00000000) ? LowerBitTable[(val >> 32) & 0xFF] + 32: LowerBitTable[(val >> 40) & 0xFF] + 40; } return (val & 0xFF000000000000) ? LowerBitTable[(val >> 48) & 0xFF] + 48: LowerBitTable[(val >> 56) & 0xFF] + 56; } #endif } // ******* Special (optimized) memory routines // Note: null (bad) pointer is not tested class MemUtil { public: // Memory compare static int Cmp (const void* p1, const void* p2, UPInt byteCount) { return memcmp(p1, p2, byteCount); } static int Cmp16(const void* p1, const void* p2, UPInt int16Count); static int Cmp32(const void* p1, const void* p2, UPInt int32Count); static int Cmp64(const void* p1, const void* p2, UPInt int64Count); }; // ** Inline Implementation inline int MemUtil::Cmp16(const void* p1, const void* p2, UPInt int16Count) { SInt16* pa = (SInt16*)p1; SInt16* pb = (SInt16*)p2; unsigned ic = 0; if (int16Count == 0) return 0; while (pa[ic] == pb[ic]) if (++ic==int16Count) return 0; return pa[ic] > pb[ic] ? 1 : -1; } inline int MemUtil::Cmp32(const void* p1, const void* p2, UPInt int32Count) { SInt32* pa = (SInt32*)p1; SInt32* pb = (SInt32*)p2; unsigned ic = 0; if (int32Count == 0) return 0; while (pa[ic] == pb[ic]) if (++ic==int32Count) return 0; return pa[ic] > pb[ic] ? 1 : -1; } inline int MemUtil::Cmp64(const void* p1, const void* p2, UPInt int64Count) { SInt64* pa = (SInt64*)p1; SInt64* pb = (SInt64*)p2; unsigned ic = 0; if (int64Count == 0) return 0; while (pa[ic] == pb[ic]) if (++ic==int64Count) return 0; return pa[ic] > pb[ic] ? 1 : -1; } // ** End Inline Implementation //----------------------------------------------------------------------------------- // ******* Byte Order Conversions namespace ByteUtil { // *** Swap Byte Order // Swap the byte order of a byte array inline void SwapOrder(void* pv, int size) { UByte* pb = (UByte*)pv; UByte temp; for (int i = 0; i < size>>1; i++) { temp = pb[size-1-i]; pb[size-1-i] = pb[i]; pb[i] = temp; } } // Swap the byte order of primitive types inline UByte SwapOrder(UByte v) { return v; } inline SByte SwapOrder(SByte v) { return v; } inline UInt16 SwapOrder(UInt16 v) { return UInt16(v>>8)|UInt16(v<<8); } inline SInt16 SwapOrder(SInt16 v) { return SInt16((UInt16(v)>>8)|(v<<8)); } inline UInt32 SwapOrder(UInt32 v) { return (v>>24)|((v&0x00FF0000)>>8)|((v&0x0000FF00)<<8)|(v<<24); } inline SInt32 SwapOrder(SInt32 p) { return (SInt32)SwapOrder(UInt32(p)); } inline UInt64 SwapOrder(UInt64 v) { return (v>>56) | ((v&UInt64(0x00FF000000000000))>>40) | ((v&UInt64(0x0000FF0000000000))>>24) | ((v&UInt64(0x000000FF00000000))>>8) | ((v&UInt64(0x00000000FF000000))<<8) | ((v&UInt64(0x0000000000FF0000))<<24) | ((v&UInt64(0x000000000000FF00))<<40) | (v<<56); } inline SInt64 SwapOrder(SInt64 v) { return (SInt64)SwapOrder(UInt64(v)); } inline float SwapOrder(float p) { union { float p; UInt32 v; } u; u.p = p; u.v = SwapOrder(u.v); return u.p; } inline double SwapOrder(double p) { union { double p; UInt64 v; } u; u.p = p; u.v = SwapOrder(u.v); return u.p; } // *** Byte-order conversion #if (OVR_BYTE_ORDER == OVR_LITTLE_ENDIAN) // Little Endian to System (LE) inline UByte LEToSystem(UByte v) { return v; } inline SByte LEToSystem(SByte v) { return v; } inline UInt16 LEToSystem(UInt16 v) { return v; } inline SInt16 LEToSystem(SInt16 v) { return v; } inline UInt32 LEToSystem(UInt32 v) { return v; } inline SInt32 LEToSystem(SInt32 v) { return v; } inline UInt64 LEToSystem(UInt64 v) { return v; } inline SInt64 LEToSystem(SInt64 v) { return v; } inline float LEToSystem(float v) { return v; } inline double LEToSystem(double v) { return v; } // Big Endian to System (LE) inline UByte BEToSystem(UByte v) { return SwapOrder(v); } inline SByte BEToSystem(SByte v) { return SwapOrder(v); } inline UInt16 BEToSystem(UInt16 v) { return SwapOrder(v); } inline SInt16 BEToSystem(SInt16 v) { return SwapOrder(v); } inline UInt32 BEToSystem(UInt32 v) { return SwapOrder(v); } inline SInt32 BEToSystem(SInt32 v) { return SwapOrder(v); } inline UInt64 BEToSystem(UInt64 v) { return SwapOrder(v); } inline SInt64 BEToSystem(SInt64 v) { return SwapOrder(v); } inline float BEToSystem(float v) { return SwapOrder(v); } inline double BEToSystem(double v) { return SwapOrder(v); } // System (LE) to Little Endian inline UByte SystemToLE(UByte v) { return v; } inline SByte SystemToLE(SByte v) { return v; } inline UInt16 SystemToLE(UInt16 v) { return v; } inline SInt16 SystemToLE(SInt16 v) { return v; } inline UInt32 SystemToLE(UInt32 v) { return v; } inline SInt32 SystemToLE(SInt32 v) { return v; } inline UInt64 SystemToLE(UInt64 v) { return v; } inline SInt64 SystemToLE(SInt64 v) { return v; } inline float SystemToLE(float v) { return v; } inline double SystemToLE(double v) { return v; } // System (LE) to Big Endian inline UByte SystemToBE(UByte v) { return SwapOrder(v); } inline SByte SystemToBE(SByte v) { return SwapOrder(v); } inline UInt16 SystemToBE(UInt16 v) { return SwapOrder(v); } inline SInt16 SystemToBE(SInt16 v) { return SwapOrder(v); } inline UInt32 SystemToBE(UInt32 v) { return SwapOrder(v); } inline SInt32 SystemToBE(SInt32 v) { return SwapOrder(v); } inline UInt64 SystemToBE(UInt64 v) { return SwapOrder(v); } inline SInt64 SystemToBE(SInt64 v) { return SwapOrder(v); } inline float SystemToBE(float v) { return SwapOrder(v); } inline double SystemToBE(double v) { return SwapOrder(v); } #elif (OVR_BYTE_ORDER == OVR_BIG_ENDIAN) // Little Endian to System (BE) inline UByte LEToSystem(UByte v) { return SwapOrder(v); } inline SByte LEToSystem(SByte v) { return SwapOrder(v); } inline UInt16 LEToSystem(UInt16 v) { return SwapOrder(v); } inline SInt16 LEToSystem(SInt16 v) { return SwapOrder(v); } inline UInt32 LEToSystem(UInt32 v) { return SwapOrder(v); } inline SInt32 LEToSystem(SInt32 v) { return SwapOrder(v); } inline UInt64 LEToSystem(UInt64 v) { return SwapOrder(v); } inline SInt64 LEToSystem(SInt64 v) { return SwapOrder(v); } inline float LEToSystem(float v) { return SwapOrder(v); } inline double LEToSystem(double v) { return SwapOrder(v); } // Big Endian to System (BE) inline UByte BEToSystem(UByte v) { return v; } inline SByte BEToSystem(SByte v) { return v; } inline UInt16 BEToSystem(UInt16 v) { return v; } inline SInt16 BEToSystem(SInt16 v) { return v; } inline UInt32 BEToSystem(UInt32 v) { return v; } inline SInt32 BEToSystem(SInt32 v) { return v; } inline UInt64 BEToSystem(UInt64 v) { return v; } inline SInt64 BEToSystem(SInt64 v) { return v; } inline float BEToSystem(float v) { return v; } inline double BEToSystem(double v) { return v; } // System (BE) to Little Endian inline UByte SystemToLE(UByte v) { return SwapOrder(v); } inline SByte SystemToLE(SByte v) { return SwapOrder(v); } inline UInt16 SystemToLE(UInt16 v) { return SwapOrder(v); } inline SInt16 SystemToLE(SInt16 v) { return SwapOrder(v); } inline UInt32 SystemToLE(UInt32 v) { return SwapOrder(v); } inline SInt32 SystemToLE(SInt32 v) { return SwapOrder(v); } inline UInt64 SystemToLE(UInt64 v) { return SwapOrder(v); } inline SInt64 SystemToLE(SInt64 v) { return SwapOrder(v); } inline float SystemToLE(float v) { return SwapOrder(v); } inline double SystemToLE(double v) { return SwapOrder(v); } // System (BE) to Big Endian inline UByte SystemToBE(UByte v) { return v; } inline SByte SystemToBE(SByte v) { return v; } inline UInt16 SystemToBE(UInt16 v) { return v; } inline SInt16 SystemToBE(SInt16 v) { return v; } inline UInt32 SystemToBE(UInt32 v) { return v; } inline SInt32 SystemToBE(SInt32 v) { return v; } inline UInt64 SystemToBE(UInt64 v) { return v; } inline SInt64 SystemToBE(SInt64 v) { return v; } inline float SystemToBE(float v) { return v; } inline double SystemToBE(double v) { return v; } #else #error "OVR_BYTE_ORDER must be defined to OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN" #endif } // namespace ByteUtil }} // OVR::Alg #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Allocator.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Allocator.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : OVR_Allocator.cpp Content : Installable memory allocator implementation Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_Allocator.h" #ifdef OVR_OS_MAC #include #else #include #endif namespace OVR { //----------------------------------------------------------------------------------- // ***** Allocator Allocator* Allocator::pInstance = 0; // Default AlignedAlloc implementation will delegate to Alloc/Free after doing rounding. void* Allocator::AllocAligned(UPInt size, UPInt align) { OVR_ASSERT((align & (align-1)) == 0); align = (align > sizeof(UPInt)) ? align : sizeof(UPInt); UPInt p = (UPInt)Alloc(size+align); UPInt aligned = 0; if (p) { aligned = (UPInt(p) + align-1) & ~(align-1); if (aligned == p) aligned += align; *(((UPInt*)aligned)-1) = aligned-p; } return (void*)aligned; } void Allocator::FreeAligned(void* p) { UPInt src = UPInt(p) - *(((UPInt*)p)-1); Free((void*)src); } //------------------------------------------------------------------------ // ***** Default Allocator // This allocator is created and used if no other allocator is installed. // Default allocator delegates to system malloc. void* DefaultAllocator::Alloc(UPInt size) { return malloc(size); } void* DefaultAllocator::AllocDebug(UPInt size, const char* file, unsigned line) { #if defined(OVR_CC_MSVC) && defined(_CRTDBG_MAP_ALLOC) return _malloc_dbg(size, _NORMAL_BLOCK, file, line); #else OVR_UNUSED2(file, line); return malloc(size); #endif } void* DefaultAllocator::Realloc(void* p, UPInt newSize) { return realloc(p, newSize); } void DefaultAllocator::Free(void *p) { return free(p); } } // OVR \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Allocator.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Allocator.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Allocator.h Content : Installable memory allocator Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Allocator_h #define OVR_Allocator_h #include "OVR_Types.h" //----------------------------------------------------------------------------------- // ***** Disable template-unfriendly MS VC++ warnings #if defined(OVR_CC_MSVC) // Pragma to prevent long name warnings in in VC++ #pragma warning(disable : 4503) #pragma warning(disable : 4786) // In MSVC 7.1, warning about placement new POD default initializer #pragma warning(disable : 4345) #endif // Un-define new so that placement constructors work #undef new //----------------------------------------------------------------------------------- // ***** Placement new overrides // Calls constructor on own memory created with "new(ptr) type" #ifndef __PLACEMENT_NEW_INLINE #define __PLACEMENT_NEW_INLINE # if defined(OVR_CC_MWERKS) || defined(OVR_CC_BORLAND) || defined(OVR_CC_GNU) # include # else // Useful on MSVC OVR_FORCE_INLINE void* operator new (OVR::UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } OVR_FORCE_INLINE void operator delete (void *, void *) { } # endif #endif // __PLACEMENT_NEW_INLINE //------------------------------------------------------------------------ // ***** Macros to redefine class new/delete operators // Types specifically declared to allow disambiguation of address in // class member operator new. #define OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, check_delete) \ void* operator new(UPInt sz) \ { void *p = OVR_ALLOC_DEBUG(sz, __FILE__, __LINE__); return p; } \ void* operator new(UPInt sz, const char* file, int line) \ { void* p = OVR_ALLOC_DEBUG(sz, file, line); OVR_UNUSED2(file, line); return p; } \ void operator delete(void *p) \ { check_delete(class_name, p); OVR_FREE(p); } \ void operator delete(void *p, const char*, int) \ { check_delete(class_name, p); OVR_FREE(p); } #define OVR_MEMORY_DEFINE_PLACEMENT_NEW \ void* operator new (UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } \ void operator delete (void *ptr, void *ptr2) { OVR_UNUSED2(ptr,ptr2); } #define OVR_MEMORY_CHECK_DELETE_NONE(class_name, p) // Redefined all delete/new operators in a class without custom memory initialization #define OVR_MEMORY_REDEFINE_NEW(class_name) \ OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, OVR_MEMORY_CHECK_DELETE_NONE) namespace OVR { //----------------------------------------------------------------------------------- // ***** Construct / Destruct // Construct/Destruct functions are useful when new is redefined, as they can // be called instead of placement new constructors. template OVR_FORCE_INLINE T* Construct(void *p) { return ::new(p) T; } template OVR_FORCE_INLINE T* Construct(void *p, const T& source) { return ::new(p) T(source); } // Same as above, but allows for a different type of constructor. template OVR_FORCE_INLINE T* ConstructAlt(void *p, const S& source) { return ::new(p) T(source); } template OVR_FORCE_INLINE T* ConstructAlt(void *p, const S1& src1, const S2& src2) { return ::new(p) T(src1, src2); } template OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count) { UByte *pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) { Construct(pdata); } } template OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count, const T& source) { UByte *pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) { Construct(pdata, source); } } template OVR_FORCE_INLINE void Destruct(T *pobj) { pobj->~T(); OVR_UNUSED1(pobj); // Fix incorrect 'unused variable' MSVC warning. } template OVR_FORCE_INLINE void DestructArray(T *pobj, UPInt count) { for (UPInt i=0; i~T(); } //----------------------------------------------------------------------------------- // ***** Allocator // Allocator defines a memory allocation interface that developers can override // to to provide memory for OVR; an instance of this class is typically created on // application startup and passed into System or OVR::System constructor. // // // Users implementing this interface must provide three functions: Alloc, Free, // and Realloc. Implementations of these functions must honor the requested alignment. // Although arbitrary alignment requests are possible, requested alignment will // typically be small, such as 16 bytes or less. class Allocator { friend class System; public: // *** Standard Alignment Alloc/Free // Allocate memory of specified size with default alignment. // Alloc of size==0 will allocate a tiny block & return a valid pointer; // this makes it suitable for new operator. virtual void* Alloc(UPInt size) = 0; // Same as Alloc, but provides an option of passing debug data. virtual void* AllocDebug(UPInt size, const char* file, unsigned line) { OVR_UNUSED2(file, line); return Alloc(size); } // Reallocate memory block to a new size, copying data if necessary. Returns the pointer to // new memory block, which may be the same as original pointer. Will return 0 if reallocation // failed, in which case previous memory is still valid. // Realloc to decrease size will never fail. // Realloc of pointer == 0 is equivalent to Alloc // Realloc to size == 0, shrinks to the minimal size, pointer remains valid and requires Free(). virtual void* Realloc(void* p, UPInt newSize) = 0; // Frees memory allocated by Alloc/Realloc. // Free of null pointer is valid and will do nothing. virtual void Free(void *p) = 0; // *** Standard Alignment Alloc/Free // Allocate memory of specified alignment. // Memory allocated with AllocAligned MUST be freed with FreeAligned. // Default implementation will delegate to Alloc/Free after doing rounding. virtual void* AllocAligned(UPInt size, UPInt align); // Frees memory allocated with AllocAligned. virtual void FreeAligned(void* p); // Returns the pointer to the current globally installed Allocator instance. // This pointer is used for most of the memory allocations. static Allocator* GetInstance() { return pInstance; } protected: // onSystemShutdown is called on the allocator during System::Shutdown. // At this point, all allocations should've been freed. virtual void onSystemShutdown() { } public: static void setInstance(Allocator* palloc) { OVR_ASSERT((pInstance == 0) || (palloc == 0)); pInstance = palloc; } private: static Allocator* pInstance; }; //------------------------------------------------------------------------ // ***** Allocator_SingletonSupport // Allocator_SingletonSupport is a Allocator wrapper class that implements // the InitSystemSingleton static function, used to create a global singleton // used for the OVR::System default argument initialization. // // End users implementing custom Allocator interface don't need to make use of this base // class; they can just create an instance of their own class on stack and pass it to System. template class Allocator_SingletonSupport : public Allocator { struct AllocContainer { UPInt Data[(sizeof(D) + sizeof(UPInt)-1) / sizeof(UPInt)]; bool Initialized; AllocContainer() : Initialized(0) { } }; AllocContainer* pContainer; public: Allocator_SingletonSupport() : pContainer(0) { } // Creates a singleton instance of this Allocator class used // on OVR_DEFAULT_ALLOCATOR during System initialization. static D* InitSystemSingleton() { static AllocContainer Container; OVR_ASSERT(Container.Initialized == false); Allocator_SingletonSupport *presult = Construct((void*)Container.Data); presult->pContainer = &Container; Container.Initialized = true; return (D*)presult; } protected: virtual void onSystemShutdown() { Allocator::onSystemShutdown(); if (pContainer) { pContainer->Initialized = false; Destruct((D*)this); pContainer = 0; } } }; //------------------------------------------------------------------------ // ***** Default Allocator // This allocator is created and used if no other allocator is installed. // Default allocator delegates to system malloc. class DefaultAllocator : public Allocator_SingletonSupport { public: virtual void* Alloc(UPInt size); virtual void* AllocDebug(UPInt size, const char* file, unsigned line); virtual void* Realloc(void* p, UPInt newSize); virtual void Free(void *p); }; //------------------------------------------------------------------------ // ***** Memory Allocation Macros // These macros should be used for global allocation. In the future, these // macros will allows allocation to be extended with debug file/line information // if necessary. #define OVR_REALLOC(p,s) OVR::Allocator::GetInstance()->Realloc((p),(s)) #define OVR_FREE(p) OVR::Allocator::GetInstance()->Free((p)) #define OVR_ALLOC_ALIGNED(s,a) OVR::Allocator::GetInstance()->AllocAligned((s),(a)) #define OVR_FREE_ALIGNED(p) OVR::Allocator::GetInstance()->FreeAligned((p)) #ifdef OVR_BUILD_DEBUG #define OVR_ALLOC(s) OVR::Allocator::GetInstance()->AllocDebug((s), __FILE__, __LINE__) #define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->AllocDebug((s), f, l) #else #define OVR_ALLOC(s) OVR::Allocator::GetInstance()->Alloc((s)) #define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->Alloc((s)) #endif //------------------------------------------------------------------------ // Base class that overrides the new and delete operators. // Deriving from this class, even as a multiple base, incurs no space overhead. class NewOverrideBase { public: // Redefine all new & delete operators. OVR_MEMORY_REDEFINE_NEW(NewOverrideBase) }; } // OVR // Redefine operator 'new' if necessary. #if defined(OVR_DEFINE_NEW) #define new OVR_DEFINE_NEW #endif #endif // OVR_Memory \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Array.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Array.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Array.h Content : Template implementation for Array Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Array_h #define OVR_Array_h #include "OVR_ContainerAllocator.h" namespace OVR { //----------------------------------------------------------------------------------- // ***** ArrayDefaultPolicy // // Default resize behavior. No minimal capacity, Granularity=4, // Shrinking as needed. ArrayConstPolicy actually is the same as // ArrayDefaultPolicy, but parametrized with constants. // This struct is used only in order to reduce the template "matroska". struct ArrayDefaultPolicy { ArrayDefaultPolicy() : Capacity(0) {} ArrayDefaultPolicy(const ArrayDefaultPolicy&) : Capacity(0) {} UPInt GetMinCapacity() const { return 0; } UPInt GetGranularity() const { return 4; } bool NeverShrinking() const { return 0; } UPInt GetCapacity() const { return Capacity; } void SetCapacity(UPInt capacity) { Capacity = capacity; } private: UPInt Capacity; }; //----------------------------------------------------------------------------------- // ***** ArrayConstPolicy // // Statically parametrized resizing behavior: // MinCapacity, Granularity, and Shrinking flag. template struct ArrayConstPolicy { typedef ArrayConstPolicy SelfType; ArrayConstPolicy() : Capacity(0) {} ArrayConstPolicy(const SelfType&) : Capacity(0) {} UPInt GetMinCapacity() const { return MinCapacity; } UPInt GetGranularity() const { return Granularity; } bool NeverShrinking() const { return NeverShrink; } UPInt GetCapacity() const { return Capacity; } void SetCapacity(UPInt capacity) { Capacity = capacity; } private: UPInt Capacity; }; //----------------------------------------------------------------------------------- // ***** ArrayDataBase // // Basic operations with array data: Reserve, Resize, Free, ArrayPolicy. // For internal use only: ArrayData,ArrayDataCC and others. template struct ArrayDataBase { typedef T ValueType; typedef Allocator AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayDataBase SelfType; ArrayDataBase() : Data(0), Size(0), Policy() {} ArrayDataBase(const SizePolicy& p) : Data(0), Size(0), Policy(p) {} ~ArrayDataBase() { Allocator::DestructArray(Data, Size); Allocator::Free(Data); } UPInt GetCapacity() const { return Policy.GetCapacity(); } void ClearAndRelease() { Allocator::DestructArray(Data, Size); Allocator::Free(Data); Data = 0; Size = 0; Policy.SetCapacity(0); } void Reserve(UPInt newCapacity) { if (Policy.NeverShrinking() && newCapacity < GetCapacity()) return; if (newCapacity < Policy.GetMinCapacity()) newCapacity = Policy.GetMinCapacity(); // Resize the buffer. if (newCapacity == 0) { if (Data) { Allocator::Free(Data); Data = 0; } Policy.SetCapacity(0); } else { UPInt gran = Policy.GetGranularity(); newCapacity = (newCapacity + gran - 1) / gran * gran; if (Data) { if (Allocator::IsMovable()) { Data = (T*)Allocator::Realloc(Data, sizeof(T) * newCapacity); } else { T* newData = (T*)Allocator::Alloc(sizeof(T) * newCapacity); UPInt i, s; s = (Size < newCapacity) ? Size : newCapacity; for (i = 0; i < s; ++i) { Allocator::Construct(&newData[i], Data[i]); Allocator::Destruct(&Data[i]); } for (i = s; i < Size; ++i) { Allocator::Destruct(&Data[i]); } Allocator::Free(Data); Data = newData; } } else { Data = (T*)Allocator::Alloc(sizeof(T) * newCapacity); //memset(Buffer, 0, (sizeof(ValueType) * newSize)); // Do we need this? } Policy.SetCapacity(newCapacity); // OVR_ASSERT(Data); // need to throw (or something) on alloc failure! } } // This version of Resize DOES NOT construct the elements. // It's done to optimize PushBack, which uses a copy constructor // instead of the default constructor and assignment void ResizeNoConstruct(UPInt newSize) { UPInt oldSize = Size; if (newSize < oldSize) { Allocator::DestructArray(Data + newSize, oldSize - newSize); if (newSize < (Policy.GetCapacity() >> 1)) { Reserve(newSize); } } else if(newSize >= Policy.GetCapacity()) { Reserve(newSize + (newSize >> 2)); } //! IMPORTANT to modify Size only after Reserve completes, because garbage collectable // array may use this array and may traverse it during Reserve (in the case, if // collection occurs because of heap limit exceeded). Size = newSize; } ValueType* Data; UPInt Size; SizePolicy Policy; }; //----------------------------------------------------------------------------------- // ***** ArrayData // // General purpose array data. // For internal use only in Array, ArrayLH, ArrayPOD and so on. template struct ArrayData : ArrayDataBase { typedef T ValueType; typedef Allocator AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayDataBase BaseType; typedef ArrayData SelfType; ArrayData() : BaseType() { } ArrayData(int size) : BaseType() { Resize(size); } ArrayData(const SelfType& a) : BaseType(a.Policy) { Append(a.Data, a.Size); } void Resize(UPInt newSize) { UPInt oldSize = this->Size; BaseType::ResizeNoConstruct(newSize); if(newSize > oldSize) Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize); } void PushBack(const ValueType& val) { BaseType::ResizeNoConstruct(this->Size + 1); Allocator::Construct(this->Data + this->Size - 1, val); } template void PushBackAlt(const S& val) { BaseType::ResizeNoConstruct(this->Size + 1); Allocator::ConstructAlt(this->Data + this->Size - 1, val); } // Append the given data to the array. void Append(const ValueType other[], UPInt count) { if (count) { UPInt oldSize = this->Size; BaseType::ResizeNoConstruct(this->Size + count); Allocator::ConstructArray(this->Data + oldSize, count, other); } } }; //----------------------------------------------------------------------------------- // ***** ArrayDataCC // // A modification of ArrayData that always copy-constructs new elements // using a specified DefaultValue. For internal use only in ArrayCC. template struct ArrayDataCC : ArrayDataBase { typedef T ValueType; typedef Allocator AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayDataBase BaseType; typedef ArrayDataCC SelfType; ArrayDataCC(const ValueType& defval) : BaseType(), DefaultValue(defval) { } ArrayDataCC(const ValueType& defval, int size) : BaseType(), DefaultValue(defval) { Resize(size); } ArrayDataCC(const SelfType& a) : BaseType(a.Policy), DefaultValue(a.DefaultValue) { Append(a.Data, a.Size); } void Resize(UPInt newSize) { UPInt oldSize = this->Size; BaseType::ResizeNoConstruct(newSize); if(newSize > oldSize) Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize, DefaultValue); } void PushBack(const ValueType& val) { BaseType::ResizeNoConstruct(this->Size + 1); Allocator::Construct(this->Data + this->Size - 1, val); } template void PushBackAlt(const S& val) { BaseType::ResizeNoConstruct(this->Size + 1); Allocator::ConstructAlt(this->Data + this->Size - 1, val); } // Append the given data to the array. void Append(const ValueType other[], UPInt count) { if (count) { UPInt oldSize = this->Size; BaseType::ResizeNoConstruct(this->Size + count); Allocator::ConstructArray(this->Data + oldSize, count, other); } } ValueType DefaultValue; }; //----------------------------------------------------------------------------------- // ***** ArrayBase // // Resizable array. The behavior can be POD (suffix _POD) and // Movable (no suffix) depending on the allocator policy. // In case of _POD the constructors and destructors are not called. // // Arrays can't handle non-movable objects! Don't put anything in here // that can't be moved around by bitwise copy. // // The addresses of elements are not persistent! Don't keep the address // of an element; the array contents will move around as it gets resized. template class ArrayBase { public: typedef typename ArrayData::ValueType ValueType; typedef typename ArrayData::AllocatorType AllocatorType; typedef typename ArrayData::SizePolicyType SizePolicyType; typedef ArrayBase SelfType; #undef new OVR_MEMORY_REDEFINE_NEW(ArrayBase) // Redefine operator 'new' if necessary. #if defined(OVR_DEFINE_NEW) #define new OVR_DEFINE_NEW #endif ArrayBase() : Data() {} ArrayBase(int size) : Data(size) {} ArrayBase(const SelfType& a) : Data(a.Data) {} ArrayBase(const ValueType& defval) : Data(defval) {} ArrayBase(const ValueType& defval, int size) : Data(defval, size) {} SizePolicyType* GetSizePolicy() const { return Data.Policy; } void SetSizePolicy(const SizePolicyType& p) { Data.Policy = p; } bool NeverShrinking()const { return Data.Policy.NeverShrinking(); } UPInt GetSize() const { return Data.Size; } bool IsEmpty() const { return Data.Size == 0; } UPInt GetCapacity() const { return Data.GetCapacity(); } UPInt GetNumBytes() const { return Data.GetCapacity() * sizeof(ValueType); } void ClearAndRelease() { Data.ClearAndRelease(); } void Clear() { Data.Resize(0); } void Resize(UPInt newSize) { Data.Resize(newSize); } // Reserve can only increase the capacity void Reserve(UPInt newCapacity) { if (newCapacity > Data.GetCapacity()) Data.Reserve(newCapacity); } // Basic access. ValueType& At(UPInt index) { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } const ValueType& At(UPInt index) const { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } ValueType ValueAt(UPInt index) const { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } // Basic access. ValueType& operator [] (UPInt index) { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } const ValueType& operator [] (UPInt index) const { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } // Raw pointer to the data. Use with caution! const ValueType* GetDataPtr() const { return Data.Data; } ValueType* GetDataPtr() { return Data.Data; } // Insert the given element at the end of the array. void PushBack(const ValueType& val) { // DO NOT pass elements of your own vector into // push_back()! Since we're using references, // resize() may munge the element storage! // OVR_ASSERT(&val < &Buffer[0] || &val > &Buffer[BufferSize]); Data.PushBack(val); } template void PushBackAlt(const S& val) { Data.PushBackAlt(val); } // Remove the last element. void PopBack(UPInt count = 1) { OVR_ASSERT(Data.Size >= count); Data.Resize(Data.Size - count); } ValueType& PushDefault() { Data.PushBack(ValueType()); return Back(); } ValueType Pop() { ValueType t = Back(); PopBack(); return t; } // Access the first element. ValueType& Front() { return At(0); } const ValueType& Front() const { return At(0); } // Access the last element. ValueType& Back() { return At(Data.Size - 1); } const ValueType& Back() const { return At(Data.Size - 1); } // Array copy. Copies the contents of a into this array. const SelfType& operator = (const SelfType& a) { Resize(a.GetSize()); for (UPInt i = 0; i < Data.Size; i++) { *(Data.Data + i) = a[i]; } return *this; } // Removing multiple elements from the array. void RemoveMultipleAt(UPInt index, UPInt num) { OVR_ASSERT(index + num <= Data.Size); if (Data.Size == num) { Clear(); } else { AllocatorType::DestructArray(Data.Data + index, num); AllocatorType::CopyArrayForward( Data.Data + index, Data.Data + index + num, Data.Size - num - index); Data.Size -= num; } } // Removing an element from the array is an expensive operation! // It compacts only after removing the last element. void RemoveAt(UPInt index) { OVR_ASSERT(index < Data.Size); if (Data.Size == 1) { Clear(); } else { AllocatorType::Destruct(Data.Data + index); AllocatorType::CopyArrayForward( Data.Data + index, Data.Data + index + 1, Data.Size - 1 - index); --Data.Size; } } // Insert the given object at the given index shifting all the elements up. void InsertAt(UPInt index, const ValueType& val = ValueType()) { OVR_ASSERT(index <= Data.Size); Data.Resize(Data.Size + 1); if (index < Data.Size - 1) { AllocatorType::CopyArrayBackward( Data.Data + index + 1, Data.Data + index, Data.Size - 1 - index); } AllocatorType::Construct(Data.Data + index, val); } // Insert the given object at the given index shifting all the elements up. void InsertMultipleAt(UPInt index, UPInt num, const ValueType& val = ValueType()) { OVR_ASSERT(index <= Data.Size); Data.Resize(Data.Size + num); if (index < Data.Size - num) { AllocatorType::CopyArrayBackward( Data.Data + index + num, Data.Data + index, Data.Size - num - index); } for (UPInt i = 0; i < num; ++i) AllocatorType::Construct(Data.Data + index + i, val); } // Append the given data to the array. void Append(const SelfType& other) { Append(other.Data.Data, other.GetSize()); } // Append the given data to the array. void Append(const ValueType other[], UPInt count) { Data.Append(other, count); } class Iterator { SelfType* pArray; SPInt CurIndex; public: Iterator() : pArray(0), CurIndex(-1) {} Iterator(SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {} bool operator==(const Iterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; } bool operator!=(const Iterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; } Iterator& operator++() { if (pArray) { if (CurIndex < (SPInt)pArray->GetSize()) ++CurIndex; } return *this; } Iterator operator++(int) { Iterator it(*this); operator++(); return it; } Iterator& operator--() { if (pArray) { if (CurIndex >= 0) --CurIndex; } return *this; } Iterator operator--(int) { Iterator it(*this); operator--(); return it; } Iterator operator+(int delta) const { return Iterator(pArray, CurIndex + delta); } Iterator operator-(int delta) const { return Iterator(pArray, CurIndex - delta); } SPInt operator-(const Iterator& right) const { OVR_ASSERT(pArray == right.pArray); return CurIndex - right.CurIndex; } ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; } ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); } void Remove() { if (!IsFinished()) pArray->RemoveAt(CurIndex); } SPInt GetIndex() const { return CurIndex; } }; Iterator Begin() { return Iterator(this); } Iterator End() { return Iterator(this, (SPInt)GetSize()); } Iterator Last() { return Iterator(this, (SPInt)GetSize() - 1); } class ConstIterator { const SelfType* pArray; SPInt CurIndex; public: ConstIterator() : pArray(0), CurIndex(-1) {} ConstIterator(const SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {} bool operator==(const ConstIterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; } bool operator!=(const ConstIterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; } ConstIterator& operator++() { if (pArray) { if (CurIndex < (int)pArray->GetSize()) ++CurIndex; } return *this; } ConstIterator operator++(int) { ConstIterator it(*this); operator++(); return it; } ConstIterator& operator--() { if (pArray) { if (CurIndex >= 0) --CurIndex; } return *this; } ConstIterator operator--(int) { ConstIterator it(*this); operator--(); return it; } ConstIterator operator+(int delta) const { return ConstIterator(pArray, CurIndex + delta); } ConstIterator operator-(int delta) const { return ConstIterator(pArray, CurIndex - delta); } SPInt operator-(const ConstIterator& right) const { OVR_ASSERT(pArray == right.pArray); return CurIndex - right.CurIndex; } const ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; } const ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } const ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); } SPInt GetIndex() const { return CurIndex; } }; ConstIterator Begin() const { return ConstIterator(this); } ConstIterator End() const { return ConstIterator(this, (SPInt)GetSize()); } ConstIterator Last() const { return ConstIterator(this, (SPInt)GetSize() - 1); } protected: ArrayData Data; }; //----------------------------------------------------------------------------------- // ***** Array // // General purpose array for movable objects that require explicit // construction/destruction. template class Array : public ArrayBase, SizePolicy> > { public: typedef T ValueType; typedef ContainerAllocator AllocatorType; typedef SizePolicy SizePolicyType; typedef Array SelfType; typedef ArrayBase, SizePolicy> > BaseType; Array() : BaseType() {} Array(int size) : BaseType(size) {} Array(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } Array(const SelfType& a) : BaseType(a) {} const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } }; // ***** ArrayPOD // // General purpose array for movable objects that DOES NOT require // construction/destruction. Constructors and destructors are not called! // Global heap is in use. template class ArrayPOD : public ArrayBase, SizePolicy> > { public: typedef T ValueType; typedef ContainerAllocator_POD AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayPOD SelfType; typedef ArrayBase, SizePolicy> > BaseType; ArrayPOD() : BaseType() {} ArrayPOD(int size) : BaseType(size) {} ArrayPOD(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } ArrayPOD(const SelfType& a) : BaseType(a) {} const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } }; // ***** ArrayCPP // // General purpose, fully C++ compliant array. Can be used with non-movable data. // Global heap is in use. template class ArrayCPP : public ArrayBase, SizePolicy> > { public: typedef T ValueType; typedef ContainerAllocator_CPP AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayCPP SelfType; typedef ArrayBase, SizePolicy> > BaseType; ArrayCPP() : BaseType() {} ArrayCPP(int size) : BaseType(size) {} ArrayCPP(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } ArrayCPP(const SelfType& a) : BaseType(a) {} const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } }; // ***** ArrayCC // // A modification of the array that uses the given default value to // construct the elements. The constructors and destructors are // properly called, the objects must be movable. template class ArrayCC : public ArrayBase, SizePolicy> > { public: typedef T ValueType; typedef ContainerAllocator AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayCC SelfType; typedef ArrayBase, SizePolicy> > BaseType; ArrayCC(const ValueType& defval) : BaseType(defval) {} ArrayCC(const ValueType& defval, int size) : BaseType(defval, size) {} ArrayCC(const ValueType& defval, const SizePolicyType& p) : BaseType(defval) { SetSizePolicy(p); } ArrayCC(const SelfType& a) : BaseType(a) {} const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } }; } // OVR #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Atomic.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Atomic.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : OVR_Atomic.cpp Content : Contains atomic operations and inline fastest locking functionality. Will contain #ifdefs for OS efficiency. Have non-thread-safe implementation if not available. Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_Atomic.h" #ifdef OVR_ENABLE_THREADS // Include Windows 8-Metro compatible Synchronization API #if defined(OVR_OS_WIN32) && defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8) #include #endif namespace OVR { // ***** Windows Lock implementation #if defined(OVR_OS_WIN32) // ***** Standard Win32 Lock implementation // Constructors Lock::Lock(unsigned spinCount) { #if defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8) // On Windows 8 we use InitializeCriticalSectionEx due to Metro-Compatibility InitializeCriticalSectionEx(&cs, spinCount, OVR_DEBUG_SELECT(NULL, CRITICAL_SECTION_NO_DEBUG_INFO)); #else // Spin count init critical section function prototype for Window NT typedef BOOL (WINAPI *Function_InitializeCriticalSectionAndSpinCount) (LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount); // Try to load function dynamically so that we don't require NT // On Windows NT we will use InitializeCriticalSectionAndSpinCount static bool initTried = 0; static Function_InitializeCriticalSectionAndSpinCount pInitFn = 0; if (!initTried) { HMODULE hmodule = ::LoadLibrary(OVR_STR("kernel32.dll")); pInitFn = (Function_InitializeCriticalSectionAndSpinCount) ::GetProcAddress(hmodule, "InitializeCriticalSectionAndSpinCount"); initTried = true; } // Initialize the critical section if (pInitFn) pInitFn(&cs, spinCount); else ::InitializeCriticalSection(&cs); #endif } Lock::~Lock() { DeleteCriticalSection(&cs); } #endif } // OVR #endif // OVR_ENABLE_THREADS \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Atomic.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Atomic.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Atomic.h Content : Contains atomic operations and inline fastest locking functionality. Will contain #ifdefs for OS efficiency. Have non-thread-safe implementaion if not available. Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Atomic_h #define OVR_Atomic_h #include "OVR_Types.h" // Include System thread functionality. #if defined(OVR_OS_WIN32) #include #else #include #endif namespace OVR { // ****** Declared classes // If there is NO thread support we implement AtomicOps and // Lock objects as no-ops. The other classes are not defined. template class AtomicOps; template class AtomicInt; template class AtomicPtr; class Lock; //----------------------------------------------------------------------------------- // ***** AtomicOps // Atomic operations are provided by the AtomicOps templates class, // implemented through system-specific AtomicOpsRaw specializations. // It provides several fundamental operations such as Exchange, ExchangeAdd // CompareAndSet, and Store_Release. Each function includes several memory // synchronization versions, important for multiprocessing CPUs with weak // memory consistency. The following memory fencing strategies are supported: // // - NoSync. No memory synchronization is done for atomic op. // - Release. All other memory writes are completed before atomic op // writes its results. // - Acquire. Further memory reads are forced to wait until atomic op // executes, guaranteeing that the right values will be seen. // - Sync. A combination of Release and Acquire. // *** AtomicOpsRaw // AtomicOpsRaw is a specialized template that provides atomic operations // used by AtomicOps. This class has two fundamental qualities: (1) it // defines a type T of correct size, and (2) provides operations that work // atomically, such as Exchange_Sync and CompareAndSet_Release. // AtomicOpsRawBase class contains shared constants/classes for AtomicOpsRaw. // The primary thing is does is define sync class objects, whose destructor and // constructor provide places to insert appropriate synchronization calls, on // systems where such calls are necessary. So far, the breakdown is as follows: // // - X86 systems don't need custom syncs, since their exchange/atomic // instructions are implicitly synchronized. // - PowerPC requires lwsync/isync instructions that can use this mechanism. // - If some other systems require a mechanism where syncing type is associated // with a particular instruction, the default implementation (which implements // all Sync, Acquire, and Release modes in terms of NoSync and fence) may not // work. Ii that case it will need to be #ifdef-ed conditionally. struct AtomicOpsRawBase { #if !defined(OVR_ENABLE_THREADS) || defined(OVR_CPU_X86) || defined(OVR_OS_WIN32) || defined(OVR_OS_IPHONE) // Need to have empty constructor to avoid class 'unused' variable warning. struct FullSync { inline FullSync() { } }; struct AcquireSync { inline AcquireSync() { } }; struct ReleaseSync { inline ReleaseSync() { } }; #elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC) struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("isync\n"); } }; struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("isync\n"); } }; struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } }; #elif defined(OVR_CPU_MIPS) struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("sync\n"); } }; struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("sync\n"); } }; struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } }; #elif defined(OVR_CPU_ARM) struct FullSync { inline FullSync() { asm volatile("dmb\n"); } ~FullSync() { asm volatile("dmb\n"); } }; struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("dmb\n"); } }; struct ReleaseSync { inline ReleaseSync() { asm volatile("dmb\n"); } }; #elif defined(OVR_CC_GNU) && (__GNUC__ >= 4) // __sync functions are already full sync struct FullSync { inline FullSync() { } }; struct AcquireSync { inline AcquireSync() { } }; struct ReleaseSync { inline ReleaseSync() { } }; #endif }; // 4-Byte raw data atomic op implementation class. struct AtomicOpsRaw_4ByteImpl : public AtomicOpsRawBase { #if !defined(OVR_ENABLE_THREADS) // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl. typedef UInt32 T; // *** Thread - Safe Atomic Versions. #elif defined(OVR_OS_WIN32) // Use special defined for VC6, where volatile is not used and // InterlockedCompareExchange is declared incorrectly. typedef LONG T; #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC < 1300) typedef T* InterlockTPtr; typedef LPVOID ET; typedef ET* InterlockETPtr; #else typedef volatile T* InterlockTPtr; typedef T ET; typedef InterlockTPtr InterlockETPtr; #endif inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange((InterlockTPtr)p, val); } inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd((InterlockTPtr)p, val); } inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange((InterlockETPtr)p, (ET)val, (ET)c) == (ET)c; } #elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret; asm volatile("1:\n\t" "lwarx %[r],0,%[i]\n\t" "stwcx. %[j],0,%[i]\n\t" "bne- 1b\n" : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [j] "b" (j) : "cc", "memory"); return ret; } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 dummy, ret; asm volatile("1:\n\t" "lwarx %[r],0,%[i]\n\t" "add %[o],%[r],%[j]\n\t" "stwcx. %[o],0,%[i]\n\t" "bne- 1b\n" : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc", "memory"); return ret; } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { UInt32 ret; asm volatile("1:\n\t" "lwarx %[r],0,%[i]\n\t" "cmpw 0,%[r],%[cmp]\n\t" "mfcr %[r]\n\t" "bne- 2f\n\t" "stwcx. %[val],0,%[i]\n\t" "bne- 1b\n\t" "2:\n" : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc", "memory"); return (ret & 0x20000000) ? 1 : 0; } #elif defined(OVR_CPU_MIPS) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret; asm volatile("1:\n\t" "ll %[r],0(%[i])\n\t" "sc %[j],0(%[i])\n\t" "beq %[j],$0,1b\n\t" "nop \n" : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory"); return ret; } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret; asm volatile("1:\n\t" "ll %[r],0(%[i])\n\t" "addu %[j],%[r],%[j]\n\t" "sc %[j],0(%[i])\n\t" "beq %[j],$0,1b\n\t" "nop \n" : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory"); return ret; } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { UInt32 ret, dummy; asm volatile("1:\n\t" "move %[r],$0\n\t" "ll %[o],0(%[i])\n\t" "bne %[o],%[c],2f\n\t" "move %[r],%[v]\n\t" "sc %[r],0(%[i])\n\t" "beq %[r],$0,1b\n\t" "nop \n\t" "2:\n" : "+m" (*i),[r] "=&d" (ret), [o] "=&d" (dummy) : [i] "d" (i), [c] "d" (c), [v] "d" (value) : "cc", "memory"); return ret; } #elif defined(OVR_CPU_ARM) && defined(OVR_CC_ARM) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { for(;;) { T r = __ldrex(i); if (__strex(j, i) == 0) return r; } } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { for(;;) { T r = __ldrex(i); if (__strex(r + j, i) == 0) return r; } } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { for(;;) { T r = __ldrex(i); if (r != c) return 0; if (__strex(value, i) == 0) return 1; } } #elif defined(OVR_CPU_ARM) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret, dummy; asm volatile("1:\n\t" "ldrex %[r],[%[i]]\n\t" "strex %[t],%[j],[%[i]]\n\t" "cmp %[t],#0\n\t" "bne 1b\n\t" : "+m" (*i), [r] "=&r" (ret), [t] "=&r" (dummy) : [i] "r" (i), [j] "r" (j) : "cc", "memory"); return ret; } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret, dummy, test; asm volatile("1:\n\t" "ldrex %[r],[%[i]]\n\t" "add %[o],%[r],%[j]\n\t" "strex %[t],%[o],[%[i]]\n\t" "cmp %[t],#0\n\t" "bne 1b\n\t" : "+m" (*i), [r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [j] "r" (j) : "cc", "memory"); return ret; } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { UInt32 ret = 1, dummy, test; asm volatile("1:\n\t" "ldrex %[o],[%[i]]\n\t" "cmp %[o],%[c]\n\t" "bne 2f\n\t" "strex %[r],%[v],[%[i]]\n\t" "cmp %[r],#0\n\t" "bne 1b\n\t" "2:\n" : "+m" (*i),[r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [c] "r" (c), [v] "r" (value) : "cc", "memory"); return !ret; } #elif defined(OVR_CPU_X86) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { asm volatile("xchgl %1,%[i]\n" : "+m" (*i), "=q" (j) : [i] "m" (*i), "1" (j) : "cc", "memory"); return j; } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { asm volatile("lock; xaddl %1,%[i]\n" : "+m" (*i), "+q" (j) : [i] "m" (*i) : "cc", "memory"); return j; } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { UInt32 ret; asm volatile("lock; cmpxchgl %[v],%[i]\n" : "+m" (*i), "=a" (ret) : [i] "m" (*i), "1" (c), [v] "q" (value) : "cc", "memory"); return (ret == c); } #elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1) typedef UInt32 T; static inline T Exchange_NoSync(volatile T *i, T j) { T v; do { v = *i; } while (!__sync_bool_compare_and_swap(i, v, j)); return v; } static inline T ExchangeAdd_NoSync(volatile T *i, T j) { return __sync_fetch_and_add(i, j); } static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value) { return __sync_bool_compare_and_swap(i, c, value); } #endif // OS }; // 8-Byte raw data data atomic op implementation class. // Currently implementation is provided only on systems with 64-bit pointers. struct AtomicOpsRaw_8ByteImpl : public AtomicOpsRawBase { #if !defined(OVR_64BIT_POINTERS) || !defined(OVR_ENABLE_THREADS) // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl. typedef UInt64 T; // *** Thread - Safe OS specific versions. #elif defined(OVR_OS_WIN32) // This is only for 64-bit systems. typedef LONG64 T; typedef volatile T* InterlockTPtr; inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange64((InterlockTPtr)p, val); } inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd64((InterlockTPtr)p, val); } inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange64((InterlockTPtr)p, val, c) == c; } #elif defined(OVR_CPU_PPC64) typedef UInt64 T; static inline UInt64 Exchange_NoSync(volatile UInt64 *i, UInt64 j) { UInt64 dummy, ret; asm volatile("1:\n\t" "ldarx %[r],0,%[i]\n\t" "mr %[o],%[j]\n\t" "stdcx. %[o],0,%[i]\n\t" "bne- 1b\n" : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc"); return ret; } static inline UInt64 ExchangeAdd_NoSync(volatile UInt64 *i, UInt64 j) { UInt64 dummy, ret; asm volatile("1:\n\t" "ldarx %[r],0,%[i]\n\t" "add %[o],%[r],%[j]\n\t" "stdcx. %[o],0,%[i]\n\t" "bne- 1b\n" : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc"); return ret; } static inline bool CompareAndSet_NoSync(volatile UInt64 *i, UInt64 c, UInt64 value) { UInt64 ret, dummy; asm volatile("1:\n\t" "ldarx %[r],0,%[i]\n\t" "cmpw 0,%[r],%[cmp]\n\t" "mfcr %[r]\n\t" "bne- 2f\n\t" "stdcx. %[val],0,%[i]\n\t" "bne- 1b\n\t" "2:\n" : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc"); return (ret & 0x20000000) ? 1 : 0; } #elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1) typedef UInt64 T; static inline T Exchange_NoSync(volatile T *i, T j) { T v; do { v = *i; } while (!__sync_bool_compare_and_swap(i, v, j)); return v; } static inline T ExchangeAdd_NoSync(volatile T *i, T j) { return __sync_fetch_and_add(i, j); } static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value) { return __sync_bool_compare_and_swap(i, c, value); } #endif // OS }; // Default implementation for AtomicOpsRaw; provides implementation of mem-fenced // atomic operations where fencing is done with a sync object wrapped around a NoSync // operation implemented in the base class. If such implementation is not possible // on a given platform, #ifdefs can be used to disable it and then op functions can be // implemented individually in the appropriate AtomicOpsRaw class. template struct AtomicOpsRaw_DefImpl : public O { typedef typename O::T O_T; typedef typename O::FullSync O_FullSync; typedef typename O::AcquireSync O_AcquireSync; typedef typename O::ReleaseSync O_ReleaseSync; // If there is no thread support, provide the default implementation. In this case, // the base class (0) must still provide the T declaration. #ifndef OVR_ENABLE_THREADS // Atomic exchange of val with argument. Returns old val. inline static O_T Exchange_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p = val; return old; } // Adds a new val to argument; returns its old val. inline static O_T ExchangeAdd_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p += val; return old; } // Compares the argument data with 'c' val. // If succeeded, stores val int '*p' and returns true; otherwise returns false. inline static bool CompareAndSet_NoSync(volatile O_T* p, O_T c, O_T val) { if (*p==c) { *p = val; return 1; } return 0; } #endif // If NoSync wrapped implementation may not be possible, it this block should be // replaced with per-function implementation in O. // "AtomicOpsRaw_DefImpl::" prefix in calls below. inline static O_T Exchange_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } inline static O_T Exchange_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } inline static O_T Exchange_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } inline static O_T ExchangeAdd_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } inline static O_T ExchangeAdd_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } inline static O_T ExchangeAdd_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } inline static bool CompareAndSet_Sync(volatile O_T* p, O_T c, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } inline static bool CompareAndSet_Release(volatile O_T* p, O_T c, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } inline static bool CompareAndSet_Acquire(volatile O_T* p, O_T c, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } // Loads and stores with memory fence. These have only the relevant versions. #ifdef OVR_CPU_X86 // On X86, Store_Release is implemented as exchange. Note that we can also // consider 'sfence' in the future, although it is not as compatible with older CPUs. inline static void Store_Release(volatile O_T* p, O_T val) { Exchange_Release(p, val); } #else inline static void Store_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); *p = val; } #endif inline static O_T Load_Acquire(const volatile O_T* p) { O_AcquireSync sync; OVR_UNUSED(sync); return *p; } }; template struct AtomicOpsRaw : public AtomicOpsRawBase { }; template<> struct AtomicOpsRaw<4> : public AtomicOpsRaw_DefImpl { // Ensure that assigned type size is correct. AtomicOpsRaw() { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl::T) == 4); } }; template<> struct AtomicOpsRaw<8> : public AtomicOpsRaw_DefImpl { AtomicOpsRaw() { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl::T) == 8); } }; // *** AtomicOps - implementation of atomic Ops for specified class // Implements atomic ops on a class, provided that the object is either // 4 or 8 bytes in size (depending on the AtomicOpsRaw specializations // available). Relies on AtomicOpsRaw for much of implementation. template class AtomicOps { typedef AtomicOpsRaw Ops; typedef typename Ops::T T; typedef volatile typename Ops::T* PT; // We cast through unions to (1) avoid pointer size compiler warnings // and (2) ensure that there are no problems with strict pointer aliasing. union C2T_union { C c; T t; }; public: // General purpose implementation for standard syncs. inline static C Exchange_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Sync((PT)p, u.t); return u.c; } inline static C Exchange_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Release((PT)p, u.t); return u.c; } inline static C Exchange_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Acquire((PT)p, u.t); return u.c; } inline static C Exchange_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_NoSync((PT)p, u.t); return u.c; } inline static C ExchangeAdd_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Sync((PT)p, u.t); return u.c; } inline static C ExchangeAdd_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Release((PT)p, u.t); return u.c; } inline static C ExchangeAdd_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Acquire((PT)p, u.t); return u.c; } inline static C ExchangeAdd_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_NoSync((PT)p, u.t); return u.c; } inline static bool CompareAndSet_Sync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Sync((PT)p, cu.t, u.t); } inline static bool CompareAndSet_Release(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Release((PT)p, cu.t, u.t); } inline static bool CompareAndSet_Relse(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Acquire((PT)p, cu.t, u.t); } inline static bool CompareAndSet_NoSync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_NoSync((PT)p, cu.t, u.t); } // Loads and stores with memory fence. These have only the relevant versions. inline static void Store_Release(volatile C* p, C val) { C2T_union u; u.c = val; Ops::Store_Release((PT)p, u.t); } inline static C Load_Acquire(const volatile C* p) { C2T_union u; u.t = Ops::Load_Acquire((PT)p); return u.c; } }; // Atomic value base class - implements operations shared for integers and pointers. template class AtomicValueBase { protected: typedef AtomicOps Ops; public: volatile T Value; inline AtomicValueBase() { } explicit inline AtomicValueBase(T val) { Ops::Store_Release(&Value, val); } // Most libraries (TBB and Joshua Scholar's) library do not do Load_Acquire // here, since most algorithms do not require atomic loads. Needs some research. inline operator T() const { return Value; } // *** Standard Atomic inlines inline T Exchange_Sync(T val) { return Ops::Exchange_Sync(&Value, val); } inline T Exchange_Release(T val) { return Ops::Exchange_Release(&Value, val); } inline T Exchange_Acquire(T val) { return Ops::Exchange_Acquire(&Value, val); } inline T Exchange_NoSync(T val) { return Ops::Exchange_NoSync(&Value, val); } inline bool CompareAndSet_Sync(T c, T val) { return Ops::CompareAndSet_Sync(&Value, c, val); } inline bool CompareAndSet_Release(T c, T val) { return Ops::CompareAndSet_Release(&Value, c, val); } inline bool CompareAndSet_Acquire(T c, T val) { return Ops::CompareAndSet_Relse(&Value, c, val); } inline bool CompareAndSet_NoSync(T c, T val) { return Ops::CompareAndSet_NoSync(&Value, c, val); } // Load & Store. inline void Store_Release(T val) { Ops::Store_Release(&Value, val); } inline T Load_Acquire() const { return Ops::Load_Acquire(&Value); } }; // ***** AtomicPtr - Atomic pointer template // This pointer class supports atomic assignments with release, // increment / decrement operations, and conditional compare + set. template class AtomicPtr : public AtomicValueBase { typedef typename AtomicValueBase::Ops Ops; public: // Initialize pointer value to 0 by default; use Store_Release only with explicit constructor. inline AtomicPtr() : AtomicValueBase() { this->Value = 0; } explicit inline AtomicPtr(T* val) : AtomicValueBase(val) { } // Pointer access. inline T* operator -> () const { return this->Load_Acquire(); } // It looks like it is convenient to have Load_Acquire characteristics // for this, since that is convenient for algorithms such as linked // list traversals that can be added to bu another thread. inline operator T* () const { return this->Load_Acquire(); } // *** Standard Atomic inlines (applicable to pointers) // ExhangeAdd considers pointer size for pointers. template inline T* ExchangeAdd_Sync(I incr) { return Ops::ExchangeAdd_Sync(&this->Value, ((T*)0) + incr); } template inline T* ExchangeAdd_Release(I incr) { return Ops::ExchangeAdd_Release(&this->Value, ((T*)0) + incr); } template inline T* ExchangeAdd_Acquire(I incr) { return Ops::ExchangeAdd_Acquire(&this->Value, ((T*)0) + incr); } template inline T* ExchangeAdd_NoSync(I incr) { return Ops::ExchangeAdd_NoSync(&this->Value, ((T*)0) + incr); } // *** Atomic Operators inline T* operator = (T* val) { this->Store_Release(val); return val; } template inline T* operator += (I val) { return ExchangeAdd_Sync(val) + val; } template inline T* operator -= (I val) { return operator += (-val); } inline T* operator ++ () { return ExchangeAdd_Sync(1) + 1; } inline T* operator -- () { return ExchangeAdd_Sync(-1) - 1; } inline T* operator ++ (int) { return ExchangeAdd_Sync(1); } inline T* operator -- (int) { return ExchangeAdd_Sync(-1); } }; // ***** AtomicInt - Atomic integer template // Implements an atomic integer type; the exact type to use is provided // as an argument. Supports atomic Acquire / Release semantics, atomic // arithmetic operations, and atomic conditional compare + set. template class AtomicInt : public AtomicValueBase { typedef typename AtomicValueBase::Ops Ops; public: inline AtomicInt() : AtomicValueBase() { } explicit inline AtomicInt(T val) : AtomicValueBase(val) { } // *** Standard Atomic inlines (applicable to int) inline T ExchangeAdd_Sync(T val) { return Ops::ExchangeAdd_Sync(&this->Value, val); } inline T ExchangeAdd_Release(T val) { return Ops::ExchangeAdd_Release(&this->Value, val); } inline T ExchangeAdd_Acquire(T val) { return Ops::ExchangeAdd_Acquire(&this->Value, val); } inline T ExchangeAdd_NoSync(T val) { return Ops::ExchangeAdd_NoSync(&this->Value, val); } // These increments could be more efficient because they don't return a value. inline void Increment_Sync() { ExchangeAdd_Sync((T)1); } inline void Increment_Release() { ExchangeAdd_Release((T)1); } inline void Increment_Acquire() { ExchangeAdd_Acquire((T)1); } inline void Increment_NoSync() { ExchangeAdd_NoSync((T)1); } // *** Atomic Operators inline T operator = (T val) { this->Store_Release(val); return val; } inline T operator += (T val) { return ExchangeAdd_Sync(val) + val; } inline T operator -= (T val) { return ExchangeAdd_Sync(0 - val) - val; } inline T operator ++ () { return ExchangeAdd_Sync((T)1) + 1; } inline T operator -- () { return ExchangeAdd_Sync(((T)0)-1) - 1; } inline T operator ++ (int) { return ExchangeAdd_Sync((T)1); } inline T operator -- (int) { return ExchangeAdd_Sync(((T)0)-1); } // More complex atomic operations. Leave it to compiler whether to optimize them or not. T operator &= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp & arg; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } T operator |= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp | arg; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } T operator ^= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp ^ arg; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } T operator *= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp * arg; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } T operator /= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp / arg; } while(!CompareAndSet_Sync(comp, newVal)); return newVal; } T operator >>= (unsigned bits) { T comp, newVal; do { comp = this->Value; newVal = comp >> bits; } while(!CompareAndSet_Sync(comp, newVal)); return newVal; } T operator <<= (unsigned bits) { T comp, newVal; do { comp = this->Value; newVal = comp << bits; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } }; //----------------------------------------------------------------------------------- // ***** Lock // Lock is a simplest and most efficient mutual-exclusion lock class. // Unlike Mutex, it cannot be waited on. class Lock { // NOTE: Locks are not allocatable and they themselves should not allocate // memory by standard means. This is the case because StandardAllocator // relies on this class. // Make 'delete' private. Don't do this for 'new' since it can be redefined. void operator delete(void*) {} // *** Lock implementation for various platforms. #if !defined(OVR_ENABLE_THREADS) public: // With no thread support, lock does nothing. inline Lock() { } inline Lock(unsigned) { } inline ~Lock() { } inline void DoLock() { } inline void Unlock() { } // Windows. #elif defined(OVR_OS_WIN32) CRITICAL_SECTION cs; public: Lock(unsigned spinCount = 0); ~Lock(); // Locking functions. inline void DoLock() { ::EnterCriticalSection(&cs); } inline void Unlock() { ::LeaveCriticalSection(&cs); } #else pthread_mutex_t mutex; public: static pthread_mutexattr_t RecursiveAttr; static bool RecursiveAttrInit; Lock (unsigned dummy = 0) { if (!RecursiveAttrInit) { pthread_mutexattr_init(&RecursiveAttr); pthread_mutexattr_settype(&RecursiveAttr, PTHREAD_MUTEX_RECURSIVE); RecursiveAttrInit = 1; } pthread_mutex_init(&mutex,&RecursiveAttr); } ~Lock () { pthread_mutex_destroy(&mutex); } inline void DoLock() { pthread_mutex_lock(&mutex); } inline void Unlock() { pthread_mutex_unlock(&mutex); } #endif // OVR_ENABLE_THREDS public: // Locker class, used for automatic locking class Locker { public: Lock *pLock; inline Locker(Lock *plock) { pLock = plock; pLock->DoLock(); } inline ~Locker() { pLock->Unlock(); } }; }; } // OVR #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Color.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Color.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Color.h Content : Contains color struct. Created : February 7, 2013 Notes : Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Color_h #define OVR_Color_h #include "OVR_Types.h" namespace OVR { struct Color { UByte R,G,B,A; Color() {} // Constructs color by channel. Alpha is set to 0xFF (fully visible) // if not specified. Color(unsigned char r,unsigned char g,unsigned char b, unsigned char a = 0xFF) : R(r), G(g), B(b), A(a) { } // 0xAARRGGBB - Common HTML color Hex layout Color(unsigned c) : R((unsigned char)(c>>16)), G((unsigned char)(c>>8)), B((unsigned char)c), A((unsigned char)(c>>24)) { } bool operator==(const Color& b) const { return R == b.R && G == b.G && B == b.B && A == b.A; } void GetRGBA(float *r, float *g, float *b, float* a) const { *r = R / 255.0f; *g = G / 255.0f; *b = B / 255.0f; *a = A / 255.0f; } }; } #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_ContainerAllocator.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_ContainerAllocator.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_ContainerAllocator.h Content : Template allocators and constructors for containers. Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_ContainerAllocator_h #define OVR_ContainerAllocator_h #include "OVR_Allocator.h" #include namespace OVR { //----------------------------------------------------------------------------------- // ***** Container Allocator // ContainerAllocator serves as a template argument for allocations done by // containers, such as Array and Hash; replacing it could allow allocator // substitution in containers. class ContainerAllocatorBase { public: static void* Alloc(UPInt size) { return OVR_ALLOC(size); } static void* Realloc(void* p, UPInt newSize) { return OVR_REALLOC(p, newSize); } static void Free(void *p) { OVR_FREE(p); } }; //----------------------------------------------------------------------------------- // ***** Constructors, Destructors, Copiers // Plain Old Data - movable, no special constructors/destructor. template class ConstructorPOD { public: static void Construct(void *) {} static void Construct(void *p, const T& source) { *(T*)p = source; } // Same as above, but allows for a different type of constructor. template static void ConstructAlt(void *p, const S& source) { *(T*)p = source; } static void ConstructArray(void*, UPInt) {} static void ConstructArray(void* p, UPInt count, const T& source) { UByte *pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) *(T*)pdata = source; } static void ConstructArray(void* p, UPInt count, const T* psource) { memcpy(p, psource, sizeof(T) * count); } static void Destruct(T*) {} static void DestructArray(T*, UPInt) {} static void CopyArrayForward(T* dst, const T* src, UPInt count) { memmove(dst, src, count * sizeof(T)); } static void CopyArrayBackward(T* dst, const T* src, UPInt count) { memmove(dst, src, count * sizeof(T)); } static bool IsMovable() { return true; } }; //----------------------------------------------------------------------------------- // ***** ConstructorMov // // Correct C++ construction and destruction for movable objects template class ConstructorMov { public: static void Construct(void* p) { OVR::Construct(p); } static void Construct(void* p, const T& source) { OVR::Construct(p, source); } // Same as above, but allows for a different type of constructor. template static void ConstructAlt(void* p, const S& source) { OVR::ConstructAlt(p, source); } static void ConstructArray(void* p, UPInt count) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata); } static void ConstructArray(void* p, UPInt count, const T& source) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata, source); } static void ConstructArray(void* p, UPInt count, const T* psource) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata, *psource++); } static void Destruct(T* p) { p->~T(); OVR_UNUSED(p); // Suppress silly MSVC warning } static void DestructArray(T* p, UPInt count) { p += count - 1; for (UPInt i=0; i~T(); } static void CopyArrayForward(T* dst, const T* src, UPInt count) { memmove(dst, src, count * sizeof(T)); } static void CopyArrayBackward(T* dst, const T* src, UPInt count) { memmove(dst, src, count * sizeof(T)); } static bool IsMovable() { return true; } }; //----------------------------------------------------------------------------------- // ***** ConstructorCPP // // Correct C++ construction and destruction for movable objects template class ConstructorCPP { public: static void Construct(void* p) { OVR::Construct(p); } static void Construct(void* p, const T& source) { OVR::Construct(p, source); } // Same as above, but allows for a different type of constructor. template static void ConstructAlt(void* p, const S& source) { OVR::ConstructAlt(p, source); } static void ConstructArray(void* p, UPInt count) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata); } static void ConstructArray(void* p, UPInt count, const T& source) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata, source); } static void ConstructArray(void* p, UPInt count, const T* psource) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata, *psource++); } static void Destruct(T* p) { p->~T(); OVR_UNUSED(p); // Suppress silly MSVC warning } static void DestructArray(T* p, UPInt count) { p += count - 1; for (UPInt i=0; i~T(); } static void CopyArrayForward(T* dst, const T* src, UPInt count) { for(UPInt i = 0; i < count; ++i) dst[i] = src[i]; } static void CopyArrayBackward(T* dst, const T* src, UPInt count) { for(UPInt i = count; i; --i) dst[i-1] = src[i-1]; } static bool IsMovable() { return false; } }; //----------------------------------------------------------------------------------- // ***** Container Allocator with movement policy // // Simple wraps as specialized allocators template struct ContainerAllocator_POD : ContainerAllocatorBase, ConstructorPOD {}; template struct ContainerAllocator : ContainerAllocatorBase, ConstructorMov {}; template struct ContainerAllocator_CPP : ContainerAllocatorBase, ConstructorCPP {}; } // OVR #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_File.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_File.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************** Filename : OVR_File.cpp Content : File wrapper class implementation (Win32) Created : April 5, 1999 Authors : Michael Antonov Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. **************************************************************************/ #define GFILE_CXX // Standard C library (Captain Obvious guarantees!) #include #include "OVR_File.h" namespace OVR { // Buffered file adds buffering to an existing file // FILEBUFFER_SIZE defines the size of internal buffer, while // FILEBUFFER_TOLERANCE controls the amount of data we'll effectively try to buffer #define FILEBUFFER_SIZE (8192-8) #define FILEBUFFER_TOLERANCE 4096 // ** Constructor/Destructor // Hidden constructor // Not supposed to be used BufferedFile::BufferedFile() : DelegatedFile(0) { pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE); BufferMode = NoBuffer; FilePos = 0; Pos = 0; DataSize = 0; } // Takes another file as source BufferedFile::BufferedFile(File *pfile) : DelegatedFile(pfile) { pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE); BufferMode = NoBuffer; FilePos = pfile->LTell(); Pos = 0; DataSize = 0; } // Destructor BufferedFile::~BufferedFile() { // Flush in case there's data if (pFile) FlushBuffer(); // Get rid of buffer if (pBuffer) OVR_FREE(pBuffer); } /* bool BufferedFile::VCopy(const Object &source) { if (!DelegatedFile::VCopy(source)) return 0; // Data members BufferedFile *psource = (BufferedFile*)&source; // Buffer & the mode it's in pBuffer = psource->pBuffer; BufferMode = psource->BufferMode; Pos = psource->Pos; DataSize = psource->DataSize; return 1; } */ // Initializes buffering to a certain mode bool BufferedFile::SetBufferMode(BufferModeType mode) { if (!pBuffer) return false; if (mode == BufferMode) return true; FlushBuffer(); // Can't set write mode if we can't write if ((mode==WriteBuffer) && (!pFile || !pFile->IsWritable()) ) return 0; // And SetMode BufferMode = mode; Pos = 0; DataSize = 0; return 1; } // Flushes buffer void BufferedFile::FlushBuffer() { switch(BufferMode) { case WriteBuffer: // Write data in buffer FilePos += pFile->Write(pBuffer,Pos); Pos = 0; break; case ReadBuffer: // Seek back & reset buffer data if ((DataSize-Pos)>0) FilePos = pFile->LSeek(-(int)(DataSize-Pos), Seek_Cur); DataSize = 0; Pos = 0; break; default: // not handled! break; } } // Reloads data for ReadBuffer void BufferedFile::LoadBuffer() { if (BufferMode == ReadBuffer) { // We should only reload once all of pre-loaded buffer is consumed. OVR_ASSERT(Pos == DataSize); // WARNING: Right now LoadBuffer() assumes the buffer's empty int sz = pFile->Read(pBuffer,FILEBUFFER_SIZE); DataSize = sz<0 ? 0 : (unsigned)sz; Pos = 0; FilePos += DataSize; } } // ** Overridden functions // We override all the functions that can possibly // require buffer mode switch, flush, or extra calculations // Tell() requires buffer adjustment int BufferedFile::Tell() { if (BufferMode == ReadBuffer) return int (FilePos - DataSize + Pos); int pos = pFile->Tell(); // Adjust position based on buffer mode & data if (pos!=-1) { OVR_ASSERT(BufferMode != ReadBuffer); if (BufferMode == WriteBuffer) pos += Pos; } return pos; } SInt64 BufferedFile::LTell() { if (BufferMode == ReadBuffer) return FilePos - DataSize + Pos; SInt64 pos = pFile->LTell(); if (pos!=-1) { OVR_ASSERT(BufferMode != ReadBuffer); if (BufferMode == WriteBuffer) pos += Pos; } return pos; } int BufferedFile::GetLength() { int len = pFile->GetLength(); // If writing through buffer, file length may actually be bigger if ((len!=-1) && (BufferMode==WriteBuffer)) { int currPos = pFile->Tell() + Pos; if (currPos>len) len = currPos; } return len; } SInt64 BufferedFile::LGetLength() { SInt64 len = pFile->LGetLength(); // If writing through buffer, file length may actually be bigger if ((len!=-1) && (BufferMode==WriteBuffer)) { SInt64 currPos = pFile->LTell() + Pos; if (currPos>len) len = currPos; } return len; } /* bool BufferedFile::Stat(FileStats *pfs) { // Have to fix up length is stat if (pFile->Stat(pfs)) { if (BufferMode==WriteBuffer) { SInt64 currPos = pFile->LTell() + Pos; if (currPos > pfs->Size) { pfs->Size = currPos; // ?? pfs->Blocks = (pfs->Size+511) >> 9; } } return 1; } return 0; } */ int BufferedFile::Write(const UByte *psourceBuffer, int numBytes) { if ( (BufferMode==WriteBuffer) || SetBufferMode(WriteBuffer)) { // If not data space in buffer, flush if ((FILEBUFFER_SIZE-(int)Pos)FILEBUFFER_TOLERANCE) { int sz = pFile->Write(psourceBuffer,numBytes); if (sz > 0) FilePos += sz; return sz; } } // Enough space in buffer.. so copy to it memcpy(pBuffer+Pos, psourceBuffer, numBytes); Pos += numBytes; return numBytes; } int sz = pFile->Write(psourceBuffer,numBytes); if (sz > 0) FilePos += sz; return sz; } int BufferedFile::Read(UByte *pdestBuffer, int numBytes) { if ( (BufferMode==ReadBuffer) || SetBufferMode(ReadBuffer)) { // Data in buffer... copy it if ((int)(DataSize-Pos) >= numBytes) { memcpy(pdestBuffer, pBuffer+Pos, numBytes); Pos += numBytes; return numBytes; } // Not enough data in buffer, copy buffer int readBytes = DataSize-Pos; memcpy(pdestBuffer, pBuffer+Pos, readBytes); numBytes -= readBytes; pdestBuffer += readBytes; Pos = DataSize; // Don't reload buffer if more then tolerance // (No major advantage, and we don't want to write a loop) if (numBytes>FILEBUFFER_TOLERANCE) { numBytes = pFile->Read(pdestBuffer,numBytes); if (numBytes > 0) { FilePos += numBytes; Pos = DataSize = 0; } return readBytes + ((numBytes==-1) ? 0 : numBytes); } // Reload the buffer // WARNING: Right now LoadBuffer() assumes the buffer's empty LoadBuffer(); if ((int)(DataSize-Pos) < numBytes) numBytes = (int)DataSize-Pos; memcpy(pdestBuffer, pBuffer+Pos, numBytes); Pos += numBytes; return numBytes + readBytes; /* // Alternative Read implementation. The one above is probably better // due to FILEBUFFER_TOLERANCE. int total = 0; do { int bufferBytes = (int)(DataSize-Pos); int copyBytes = (bufferBytes > numBytes) ? numBytes : bufferBytes; memcpy(pdestBuffer, pBuffer+Pos, copyBytes); numBytes -= copyBytes; pdestBuffer += copyBytes; Pos += copyBytes; total += copyBytes; if (numBytes == 0) break; LoadBuffer(); } while (DataSize > 0); return total; */ } int sz = pFile->Read(pdestBuffer,numBytes); if (sz > 0) FilePos += sz; return sz; } int BufferedFile::SkipBytes(int numBytes) { int skippedBytes = 0; // Special case for skipping a little data in read buffer if (BufferMode==ReadBuffer) { skippedBytes = (((int)DataSize-(int)Pos) >= numBytes) ? numBytes : (DataSize-Pos); Pos += skippedBytes; numBytes -= skippedBytes; } if (numBytes) { numBytes = pFile->SkipBytes(numBytes); // Make sure we return the actual number skipped, or error if (numBytes!=-1) { skippedBytes += numBytes; FilePos += numBytes; Pos = DataSize = 0; } else if (skippedBytes <= 0) skippedBytes = -1; } return skippedBytes; } int BufferedFile::BytesAvailable() { int available = pFile->BytesAvailable(); // Adjust available size based on buffers switch(BufferMode) { case ReadBuffer: available += DataSize-Pos; break; case WriteBuffer: available -= Pos; if (available<0) available= 0; break; default: break; } return available; } bool BufferedFile::Flush() { FlushBuffer(); return pFile->Flush(); } // Seeking could be optimized better.. int BufferedFile::Seek(int offset, int origin) { if (BufferMode == ReadBuffer) { if (origin == Seek_Cur) { // Seek can fall either before or after Pos in the buffer, // but it must be within bounds. if (((unsigned(offset) + Pos)) <= DataSize) { Pos += offset; return int (FilePos - DataSize + Pos); } // Lightweight buffer "Flush". We do this to avoid an extra seek // back operation which would take place if we called FlushBuffer directly. origin = Seek_Set; OVR_ASSERT(((FilePos - DataSize + Pos) + (UInt64)offset) < ~(UInt64)0); offset = (int)(FilePos - DataSize + Pos) + offset; Pos = DataSize = 0; } else if (origin == Seek_Set) { if (((unsigned)offset - (FilePos-DataSize)) <= DataSize) { OVR_ASSERT((FilePos-DataSize) < ~(UInt64)0); Pos = (unsigned)offset - (unsigned)(FilePos-DataSize); return offset; } Pos = DataSize = 0; } else { FlushBuffer(); } } else { FlushBuffer(); } /* // Old Seek Logic if (origin == Seek_Cur && offset + Pos < DataSize) { //OVR_ASSERT((FilePos - DataSize) >= (FilePos - DataSize + Pos + offset)); Pos += offset; OVR_ASSERT(int (Pos) >= 0); return int (FilePos - DataSize + Pos); } else if (origin == Seek_Set && unsigned(offset) >= FilePos - DataSize && unsigned(offset) < FilePos) { Pos = unsigned(offset - FilePos + DataSize); OVR_ASSERT(int (Pos) >= 0); return int (FilePos - DataSize + Pos); } FlushBuffer(); */ FilePos = pFile->Seek(offset,origin); return int (FilePos); } SInt64 BufferedFile::LSeek(SInt64 offset, int origin) { if (BufferMode == ReadBuffer) { if (origin == Seek_Cur) { // Seek can fall either before or after Pos in the buffer, // but it must be within bounds. if (((unsigned(offset) + Pos)) <= DataSize) { Pos += (unsigned)offset; return SInt64(FilePos - DataSize + Pos); } // Lightweight buffer "Flush". We do this to avoid an extra seek // back operation which would take place if we called FlushBuffer directly. origin = Seek_Set; offset = (SInt64)(FilePos - DataSize + Pos) + offset; Pos = DataSize = 0; } else if (origin == Seek_Set) { if (((UInt64)offset - (FilePos-DataSize)) <= DataSize) { Pos = (unsigned)((UInt64)offset - (FilePos-DataSize)); return offset; } Pos = DataSize = 0; } else { FlushBuffer(); } } else { FlushBuffer(); } /* OVR_ASSERT(BufferMode != NoBuffer); if (origin == Seek_Cur && offset + Pos < DataSize) { Pos += int (offset); return FilePos - DataSize + Pos; } else if (origin == Seek_Set && offset >= SInt64(FilePos - DataSize) && offset < SInt64(FilePos)) { Pos = unsigned(offset - FilePos + DataSize); return FilePos - DataSize + Pos; } FlushBuffer(); */ FilePos = pFile->LSeek(offset,origin); return FilePos; } int BufferedFile::CopyFromStream(File *pstream, int byteSize) { // We can't rely on overridden Write() // because delegation doesn't override virtual pointers // So, just re-implement UByte buff[0x4000]; int count = 0; int szRequest, szRead, szWritten; while(byteSize) { szRequest = (byteSize > int(sizeof(buff))) ? int(sizeof(buff)) : byteSize; szRead = pstream->Read(buff,szRequest); szWritten = 0; if (szRead > 0) szWritten = Write(buff,szRead); count +=szWritten; byteSize-=szWritten; if (szWritten < szRequest) break; } return count; } // Closing files bool BufferedFile::Close() { switch(BufferMode) { case WriteBuffer: FlushBuffer(); break; case ReadBuffer: // No need to seek back on close BufferMode = NoBuffer; break; default: break; } return pFile->Close(); } // ***** Global path helpers // Find trailing short filename in a path. const char* OVR_CDECL GetShortFilename(const char* purl) { UPInt len = OVR_strlen(purl); for (UPInt i=len; i>0; i--) if (purl[i]=='\\' || purl[i]=='/') return purl+i+1; return purl; } } // OVR \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_File.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_File.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: Kernel Filename : OVR_File.h Content : Header for all internal file management - functions and structures to be inherited by OS specific subclasses. Created : September 19, 2012 Notes : Notes : errno may not be preserved across use of BaseFile member functions : Directories cannot be deleted while files opened from them are in use (For the GetFullName function) Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_File_h #define OVR_File_h #include "OVR_RefCount.h" #include "OVR_Std.h" #include "OVR_Alg.h" #include #include "OVR_String.h" namespace OVR { // ***** Declared classes class FileConstants; class File; class DelegatedFile; class BufferedFile; // ***** Flags for File & Directory accesses class FileConstants { public: // *** File open flags enum OpenFlags { Open_Read = 1, Open_Write = 2, Open_ReadWrite = 3, // Opens file and truncates it to zero length // - file must have write permission // - when used with Create, it opens an existing // file and empties it or creates a new file Open_Truncate = 4, // Creates and opens new file // - does not erase contents if file already // exists unless combined with Truncate Open_Create = 8, // Returns an error value if the file already exists Open_CreateOnly = 24, // Open file with buffering Open_Buffered = 32 }; // *** File Mode flags enum Modes { Mode_Read = 0444, Mode_Write = 0222, Mode_Execute = 0111, Mode_ReadWrite = 0666 }; // *** Seek operations enum SeekOps { Seek_Set = 0, Seek_Cur = 1, Seek_End = 2 }; // *** Errors enum Errors { Error_FileNotFound = 0x1001, Error_Access = 0x1002, Error_IOError = 0x1003, Error_DiskFull = 0x1004 }; }; //----------------------------------------------------------------------------------- // ***** File Class // The pure virtual base random-access file // This is a base class to all files class File : public RefCountBase, public FileConstants { public: File() { } // ** Location Information // Returns a file name path relative to the 'reference' directory // This is often a path that was used to create a file // (this is not a global path, global path can be obtained with help of directory) virtual const char* GetFilePath() = 0; // ** File Information // Return 1 if file's usable (open) virtual bool IsValid() = 0; // Return 1 if file's writable, otherwise 0 virtual bool IsWritable() = 0; // Return position virtual int Tell() = 0; virtual SInt64 LTell() = 0; // File size virtual int GetLength() = 0; virtual SInt64 LGetLength() = 0; // Returns file stats // 0 for failure //virtual bool Stat(FileStats *pfs) = 0; // Return errno-based error code // Useful if any other function failed virtual int GetErrorCode() = 0; // ** Stream implementation & I/O // Blocking write, will write in the given number of bytes to the stream // Returns : -1 for error // Otherwise number of bytes read virtual int Write(const UByte *pbufer, int numBytes) = 0; // Blocking read, will read in the given number of bytes or less from the stream // Returns : -1 for error // Otherwise number of bytes read, // if 0 or < numBytes, no more bytes available; end of file or the other side of stream is closed virtual int Read(UByte *pbufer, int numBytes) = 0; // Skips (ignores) a given # of bytes // Same return values as Read virtual int SkipBytes(int numBytes) = 0; // Returns the number of bytes available to read from a stream without blocking // For a file, this should generally be number of bytes to the end virtual int BytesAvailable() = 0; // Causes any implementation's buffered data to be delivered to destination // Return 0 for error virtual bool Flush() = 0; // Need to provide a more optimized implementation that doe snot necessarily involve a lot of seeking inline bool IsEOF() { return !BytesAvailable(); } // Seeking // Returns new position, -1 for error virtual int Seek(int offset, int origin=Seek_Set) = 0; virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) = 0; // Seek simplification int SeekToBegin() {return Seek(0); } int SeekToEnd() {return Seek(0,Seek_End); } int Skip(int numBytes) {return Seek(numBytes,Seek_Cur); } // Appends other file data from a stream // Return -1 for error, else # of bytes written virtual int CopyFromStream(File *pstream, int byteSize) = 0; // Closes the file // After close, file cannot be accessed virtual bool Close() = 0; // ***** Inlines for convenient primitive type serialization // Read/Write helpers private: UInt64 PRead64() { UInt64 v = 0; Read((UByte*)&v, 8); return v; } UInt32 PRead32() { UInt32 v = 0; Read((UByte*)&v, 4); return v; } UInt16 PRead16() { UInt16 v = 0; Read((UByte*)&v, 2); return v; } UByte PRead8() { UByte v = 0; Read((UByte*)&v, 1); return v; } void PWrite64(UInt64 v) { Write((UByte*)&v, 8); } void PWrite32(UInt32 v) { Write((UByte*)&v, 4); } void PWrite16(UInt16 v) { Write((UByte*)&v, 2); } void PWrite8(UByte v) { Write((UByte*)&v, 1); } public: // Writing primitive types - Little Endian inline void WriteUByte(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSByte(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } inline void WriteUInt8(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSInt8(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } inline void WriteUInt16(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSInt16(SInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); } inline void WriteUInt32(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSInt32(SInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); } inline void WriteUInt64(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSInt64(SInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); } inline void WriteFloat(float v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 4); } inline void WriteDouble(double v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 8); } // Writing primitive types - Big Endian inline void WriteUByteBE(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSByteBE(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } inline void WriteUInt8BE(UInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSInt8BE(SInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } inline void WriteUInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); } inline void WriteUInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); } inline void WriteUInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); } inline void WriteFloatBE(float v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 4); } inline void WriteDoubleBE(double v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 8); } // Reading primitive types - Little Endian inline UByte ReadUByte() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); } inline SByte ReadSByte() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); } inline UByte ReadUInt8() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); } inline SByte ReadSInt8() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); } inline UInt16 ReadUInt16() { return (UInt16)Alg::ByteUtil::LEToSystem(PRead16()); } inline SInt16 ReadSInt16() { return (SInt16)Alg::ByteUtil::LEToSystem(PRead16()); } inline UInt32 ReadUInt32() { return (UInt32)Alg::ByteUtil::LEToSystem(PRead32()); } inline SInt32 ReadSInt32() { return (SInt32)Alg::ByteUtil::LEToSystem(PRead32()); } inline UInt64 ReadUInt64() { return (UInt64)Alg::ByteUtil::LEToSystem(PRead64()); } inline SInt64 ReadSInt64() { return (SInt64)Alg::ByteUtil::LEToSystem(PRead64()); } inline float ReadFloat() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::LEToSystem(v); } inline double ReadDouble() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::LEToSystem(v); } // Reading primitive types - Big Endian inline UByte ReadUByteBE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); } inline SByte ReadSByteBE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); } inline UByte ReadUInt8BE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); } inline SByte ReadSInt8BE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); } inline UInt16 ReadUInt16BE() { return (UInt16)Alg::ByteUtil::BEToSystem(PRead16()); } inline SInt16 ReadSInt16BE() { return (SInt16)Alg::ByteUtil::BEToSystem(PRead16()); } inline UInt32 ReadUInt32BE() { return (UInt32)Alg::ByteUtil::BEToSystem(PRead32()); } inline SInt32 ReadSInt32BE() { return (SInt32)Alg::ByteUtil::BEToSystem(PRead32()); } inline UInt64 ReadUInt64BE() { return (UInt64)Alg::ByteUtil::BEToSystem(PRead64()); } inline SInt64 ReadSInt64BE() { return (SInt64)Alg::ByteUtil::BEToSystem(PRead64()); } inline float ReadFloatBE() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::BEToSystem(v); } inline double ReadDoubleBE() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::BEToSystem(v); } }; // *** Delegated File class DelegatedFile : public File { protected: // Delegating file pointer Ptr pFile; // Hidden default constructor DelegatedFile() : pFile(0) { } DelegatedFile(const DelegatedFile &source) : File() { OVR_UNUSED(source); } public: // Constructors DelegatedFile(File *pfile) : pFile(pfile) { } // ** Location Information virtual const char* GetFilePath() { return pFile->GetFilePath(); } // ** File Information virtual bool IsValid() { return pFile && pFile->IsValid(); } virtual bool IsWritable() { return pFile->IsWritable(); } // virtual bool IsRecoverable() { return pFile->IsRecoverable(); } virtual int Tell() { return pFile->Tell(); } virtual SInt64 LTell() { return pFile->LTell(); } virtual int GetLength() { return pFile->GetLength(); } virtual SInt64 LGetLength() { return pFile->LGetLength(); } //virtual bool Stat(FileStats *pfs) { return pFile->Stat(pfs); } virtual int GetErrorCode() { return pFile->GetErrorCode(); } // ** Stream implementation & I/O virtual int Write(const UByte *pbuffer, int numBytes) { return pFile->Write(pbuffer,numBytes); } virtual int Read(UByte *pbuffer, int numBytes) { return pFile->Read(pbuffer,numBytes); } virtual int SkipBytes(int numBytes) { return pFile->SkipBytes(numBytes); } virtual int BytesAvailable() { return pFile->BytesAvailable(); } virtual bool Flush() { return pFile->Flush(); } // Seeking virtual int Seek(int offset, int origin=Seek_Set) { return pFile->Seek(offset,origin); } virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) { return pFile->LSeek(offset,origin); } virtual int CopyFromStream(File *pstream, int byteSize) { return pFile->CopyFromStream(pstream,byteSize); } // Closing the file virtual bool Close() { return pFile->Close(); } }; //----------------------------------------------------------------------------------- // ***** Buffered File // This file class adds buffering to an existing file // Buffered file never fails by itself; if there's not // enough memory for buffer, no buffer's used class BufferedFile : public DelegatedFile { protected: enum BufferModeType { NoBuffer, ReadBuffer, WriteBuffer }; // Buffer & the mode it's in UByte* pBuffer; BufferModeType BufferMode; // Position in buffer unsigned Pos; // Data in buffer if reading unsigned DataSize; // Underlying file position UInt64 FilePos; // Initializes buffering to a certain mode bool SetBufferMode(BufferModeType mode); // Flushes buffer // WriteBuffer - write data to disk, ReadBuffer - reset buffer & fix file position void FlushBuffer(); // Loads data into ReadBuffer // WARNING: Right now LoadBuffer() assumes the buffer's empty void LoadBuffer(); // Hidden constructor BufferedFile(); inline BufferedFile(const BufferedFile &source) : DelegatedFile() { OVR_UNUSED(source); } public: // Constructor // - takes another file as source BufferedFile(File *pfile); ~BufferedFile(); // ** Overridden functions // We override all the functions that can possibly // require buffer mode switch, flush, or extra calculations virtual int Tell(); virtual SInt64 LTell(); virtual int GetLength(); virtual SInt64 LGetLength(); // virtual bool Stat(GFileStats *pfs); virtual int Write(const UByte *pbufer, int numBytes); virtual int Read(UByte *pbufer, int numBytes); virtual int SkipBytes(int numBytes); virtual int BytesAvailable(); virtual bool Flush(); virtual int Seek(int offset, int origin=Seek_Set); virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set); virtual int CopyFromStream(File *pstream, int byteSize); virtual bool Close(); }; //----------------------------------------------------------------------------------- // ***** Memory File class MemoryFile : public File { public: const char* GetFilePath() { return FilePath.ToCStr(); } bool IsValid() { return Valid; } bool IsWritable() { return false; } bool Flush() { return true; } int GetErrorCode() { return 0; } int Tell() { return FileIndex; } SInt64 LTell() { return (SInt64) FileIndex; } int GetLength() { return FileSize; } SInt64 LGetLength() { return (SInt64) FileSize; } bool Close() { Valid = false; return false; } int CopyFromStream(File *pstream, int byteSize) { OVR_UNUSED2(pstream, byteSize); return 0; } int Write(const UByte *pbuffer, int numBytes) { OVR_UNUSED2(pbuffer, numBytes); return 0; } int Read(UByte *pbufer, int numBytes) { if (FileIndex + numBytes > FileSize) { numBytes = FileSize - FileIndex; } if (numBytes > 0) { ::memcpy (pbufer, &FileData [FileIndex], numBytes); FileIndex += numBytes; } return numBytes; } int SkipBytes(int numBytes) { if (FileIndex + numBytes > FileSize) { numBytes = FileSize - FileIndex; } FileIndex += numBytes; return numBytes; } int BytesAvailable() { return (FileSize - FileIndex); } int Seek(int offset, int origin = Seek_Set) { switch (origin) { case Seek_Set : FileIndex = offset; break; case Seek_Cur : FileIndex += offset; break; case Seek_End : FileIndex = FileSize - offset; break; } return FileIndex; } SInt64 LSeek(SInt64 offset, int origin = Seek_Set) { return (SInt64) Seek((int) offset, origin); } public: MemoryFile (const String& fileName, const UByte *pBuffer, int buffSize) : FilePath(fileName) { FileData = pBuffer; FileSize = buffSize; FileIndex = 0; Valid = (!fileName.IsEmpty() && pBuffer && buffSize > 0) ? true : false; } // pfileName should be encoded as UTF-8 to support international file names. MemoryFile (const char* pfileName, const UByte *pBuffer, int buffSize) : FilePath(pfileName) { FileData = pBuffer; FileSize = buffSize; FileIndex = 0; Valid = (pfileName && pBuffer && buffSize > 0) ? true : false; } private: String FilePath; const UByte *FileData; int FileSize; int FileIndex; bool Valid; }; // ***** Global path helpers // Find trailing short filename in a path. const char* OVR_CDECL GetShortFilename(const char* purl); } // OVR #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_FileFILE.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_FileFILE.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************** Filename : OVR_FileFILE.cpp Content : File wrapper class implementation (Win32) Created : April 5, 1999 Authors : Michael Antonov Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. **************************************************************************/ #define GFILE_CXX #include "OVR_Types.h" #include "OVR_Log.h" // Standard C library (Captain Obvious guarantees!) #include #ifndef OVR_OS_WINCE #include #endif #include "OVR_SysFile.h" #ifndef OVR_OS_WINCE #include #endif namespace OVR { // ***** File interface // ***** FILEFile - C streams file static int SFerror () { if (errno == ENOENT) return FileConstants::Error_FileNotFound; else if (errno == EACCES || errno == EPERM) return FileConstants::Error_Access; else if (errno == ENOSPC) return FileConstants::Error_DiskFull; else return FileConstants::Error_IOError; }; #ifdef OVR_OS_WIN32 #include "windows.h" // A simple helper class to disable/enable system error mode, if necessary // Disabling happens conditionally only if a drive name is involved class SysErrorModeDisabler { BOOL Disabled; UINT OldMode; public: SysErrorModeDisabler(const char* pfileName) { if (pfileName && (pfileName[0]!=0) && pfileName[1]==':') { Disabled = 1; OldMode = ::SetErrorMode(SEM_FAILCRITICALERRORS); } else Disabled = 0; } ~SysErrorModeDisabler() { if (Disabled) ::SetErrorMode(OldMode); } }; #else class SysErrorModeDisabler { public: SysErrorModeDisabler(const char* pfileName) { } }; #endif // OVR_OS_WIN32 // This macro enables verification of I/O results after seeks against a pre-loaded // full file buffer copy. This is generally not necessary, but can been used to debug // memory corruptions; we've seen this fail due to EAX2/DirectSound corrupting memory // under FMOD with XP64 (32-bit) and Realtek HA Audio driver. //#define GFILE_VERIFY_SEEK_ERRORS // This is the simplest possible file implementation, it wraps around the descriptor // This file is delegated to by SysFile. class FILEFile : public File { protected: // Allocated filename String FileName; // File handle & open mode bool Opened; FILE* fs; int OpenFlags; // Error code for last request int ErrorCode; int LastOp; #ifdef OVR_FILE_VERIFY_SEEK_ERRORS UByte* pFileTestBuffer; unsigned FileTestLength; unsigned TestPos; // File pointer position during tests. #endif public: FILEFile() { Opened = 0; FileName = ""; #ifdef OVR_FILE_VERIFY_SEEK_ERRORS pFileTestBuffer =0; FileTestLength =0; TestPos =0; #endif } // Initialize file by opening it FILEFile(const String& fileName, int flags, int Mode); // The 'pfileName' should be encoded as UTF-8 to support international file names. FILEFile(const char* pfileName, int flags, int Mode); ~FILEFile() { if (Opened) Close(); } virtual const char* GetFilePath(); // ** File Information virtual bool IsValid(); virtual bool IsWritable(); // Return position / file size virtual int Tell(); virtual SInt64 LTell(); virtual int GetLength(); virtual SInt64 LGetLength(); // virtual bool Stat(FileStats *pfs); virtual int GetErrorCode(); // ** Stream implementation & I/O virtual int Write(const UByte *pbuffer, int numBytes); virtual int Read(UByte *pbuffer, int numBytes); virtual int SkipBytes(int numBytes); virtual int BytesAvailable(); virtual bool Flush(); virtual int Seek(int offset, int origin); virtual SInt64 LSeek(SInt64 offset, int origin); virtual int CopyFromStream(File *pStream, int byteSize); virtual bool Close(); private: void init(); }; // Initialize file by opening it FILEFile::FILEFile(const String& fileName, int flags, int mode) : FileName(fileName), OpenFlags(flags) { OVR_UNUSED(mode); init(); } // The 'pfileName' should be encoded as UTF-8 to support international file names. FILEFile::FILEFile(const char* pfileName, int flags, int mode) : FileName(pfileName), OpenFlags(flags) { OVR_UNUSED(mode); init(); } void FILEFile::init() { // Open mode for file's open const char *omode = "rb"; if (OpenFlags & Open_Truncate) { if (OpenFlags & Open_Read) omode = "w+b"; else omode = "wb"; } else if (OpenFlags & Open_Create) { if (OpenFlags & Open_Read) omode = "a+b"; else omode = "ab"; } else if (OpenFlags & Open_Write) omode = "r+b"; #ifdef OVR_OS_WIN32 SysErrorModeDisabler disabler(FileName.ToCStr()); #endif #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) wchar_t womode[16]; wchar_t *pwFileName = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(FileName.ToCStr())+1) * sizeof(wchar_t)); UTF8Util::DecodeString(pwFileName, FileName.ToCStr()); OVR_ASSERT(strlen(omode) < sizeof(womode)/sizeof(womode[0])); UTF8Util::DecodeString(womode, omode); _wfopen_s(&fs, pwFileName, womode); OVR_FREE(pwFileName); #else fs = fopen(FileName.ToCStr(), omode); #endif if (fs) rewind (fs); Opened = (fs != NULL); // Set error code if (!Opened) ErrorCode = SFerror(); else { // If we are testing file seek correctness, pre-load the entire file so // that we can do comparison tests later. #ifdef OVR_FILE_VERIFY_SEEK_ERRORS TestPos = 0; fseek(fs, 0, SEEK_END); FileTestLength = ftell(fs); fseek(fs, 0, SEEK_SET); pFileTestBuffer = (UByte*)OVR_ALLOC(FileTestLength); if (pFileTestBuffer) { OVR_ASSERT(FileTestLength == (unsigned)Read(pFileTestBuffer, FileTestLength)); Seek(0, Seek_Set); } #endif ErrorCode = 0; } LastOp = 0; } const char* FILEFile::GetFilePath() { return FileName.ToCStr(); } // ** File Information bool FILEFile::IsValid() { return Opened; } bool FILEFile::IsWritable() { return IsValid() && (OpenFlags&Open_Write); } /* bool FILEFile::IsRecoverable() { return IsValid() && ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC); } */ // Return position / file size int FILEFile::Tell() { int pos = (int)ftell (fs); if (pos < 0) ErrorCode = SFerror(); return pos; } SInt64 FILEFile::LTell() { SInt64 pos = ftell(fs); if (pos < 0) ErrorCode = SFerror(); return pos; } int FILEFile::GetLength() { int pos = Tell(); if (pos >= 0) { Seek (0, Seek_End); int size = Tell(); Seek (pos, Seek_Set); return size; } return -1; } SInt64 FILEFile::LGetLength() { SInt64 pos = LTell(); if (pos >= 0) { LSeek (0, Seek_End); SInt64 size = LTell(); LSeek (pos, Seek_Set); return size; } return -1; } int FILEFile::GetErrorCode() { return ErrorCode; } // ** Stream implementation & I/O int FILEFile::Write(const UByte *pbuffer, int numBytes) { if (LastOp && LastOp != Open_Write) fflush(fs); LastOp = Open_Write; int written = (int) fwrite(pbuffer, 1, numBytes, fs); if (written < numBytes) ErrorCode = SFerror(); #ifdef OVR_FILE_VERIFY_SEEK_ERRORS if (written > 0) TestPos += written; #endif return written; } int FILEFile::Read(UByte *pbuffer, int numBytes) { if (LastOp && LastOp != Open_Read) fflush(fs); LastOp = Open_Read; int read = (int) fread(pbuffer, 1, numBytes, fs); if (read < numBytes) ErrorCode = SFerror(); #ifdef OVR_FILE_VERIFY_SEEK_ERRORS if (read > 0) { // Read-in data must match our pre-loaded buffer data! UByte* pcompareBuffer = pFileTestBuffer + TestPos; for (int i=0; i< read; i++) { OVR_ASSERT(pcompareBuffer[i] == pbuffer[i]); } //OVR_ASSERT(!memcmp(pFileTestBuffer + TestPos, pbuffer, read)); TestPos += read; OVR_ASSERT(ftell(fs) == (int)TestPos); } #endif return read; } // Seeks ahead to skip bytes int FILEFile::SkipBytes(int numBytes) { SInt64 pos = LTell(); SInt64 newPos = LSeek(numBytes, Seek_Cur); // Return -1 for major error if ((pos==-1) || (newPos==-1)) { return -1; } //ErrorCode = ((NewPos-Pos) int(sizeof(buff))) ? int(sizeof(buff)) : byteSize; szRead = pstream->Read(buff, szRequest); szWritten = 0; if (szRead > 0) szWritten = Write(buff, szRead); count += szWritten; byteSize -= szWritten; if (szWritten < szRequest) break; } return count; } bool FILEFile::Close() { #ifdef OVR_FILE_VERIFY_SEEK_ERRORS if (pFileTestBuffer) { OVR_FREE(pFileTestBuffer); pFileTestBuffer = 0; FileTestLength = 0; } #endif bool closeRet = !fclose(fs); if (!closeRet) { ErrorCode = SFerror(); return 0; } else { Opened = 0; fs = 0; ErrorCode = 0; } // Handle safe truncate /* if ((OpenFlags & OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC) { // Delete original file (if it existed) DWORD oldAttributes = FileUtilWin32::GetFileAttributes(FileName); if (oldAttributes!=0xFFFFFFFF) if (!FileUtilWin32::DeleteFile(FileName)) { // Try to remove the readonly attribute FileUtilWin32::SetFileAttributes(FileName, oldAttributes & (~FILE_ATTRIBUTE_READONLY) ); // And delete the file again if (!FileUtilWin32::DeleteFile(FileName)) return 0; } // Rename temp file to real filename if (!FileUtilWin32::MoveFile(TempName, FileName)) { //ErrorCode = errno; return 0; } } */ return 1; } /* bool FILEFile::CloseCancel() { bool closeRet = (bool)::CloseHandle(fd); if (!closeRet) { //ErrorCode = errno; return 0; } else { Opened = 0; fd = INVALID_HANDLE_VALUE; ErrorCode = 0; } // Handle safe truncate (delete tmp file, leave original unchanged) if ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC) if (!FileUtilWin32::DeleteFile(TempName)) { //ErrorCode = errno; return 0; } return 1; } */ File *FileFILEOpen(const String& path, int flags, int mode) { return new FILEFile(path, flags, mode); } // Helper function: obtain file information time. bool SysFile::GetFileStat(FileStat* pfileStat, const String& path) { #if defined(OVR_OS_WIN32) // 64-bit implementation on Windows. struct __stat64 fileStat; // Stat returns 0 for success. wchar_t *pwpath = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(path.ToCStr())+1)*sizeof(wchar_t)); UTF8Util::DecodeString(pwpath, path.ToCStr()); int ret = _wstat64(pwpath, &fileStat); OVR_FREE(pwpath); if (ret) return false; #else struct stat fileStat; // Stat returns 0 for success. if (stat(path, &fileStat) != 0) return false; #endif pfileStat->AccessTime = fileStat.st_atime; pfileStat->ModifyTime = fileStat.st_mtime; pfileStat->FileSize = fileStat.st_size; return true; } } // Scaleform \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Hash.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Hash.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: None Filename : OVR_Hash.h Content : Template hash-table/set implementation Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Hash_h #define OVR_Hash_h #include "OVR_ContainerAllocator.h" #include "OVR_Alg.h" // 'new' operator is redefined/used in this file. #undef new namespace OVR { //----------------------------------------------------------------------------------- // ***** Hash Table Implementation // HastSet and Hash. // // Hash table, linear probing, internal chaining. One interesting/nice thing // about this implementation is that the table itself is a flat chunk of memory // containing no pointers, only relative indices. If the key and value types // of the Hash contain no pointers, then the Hash can be serialized using raw IO. // // Never shrinks, unless you explicitly Clear() it. Expands on // demand, though. For best results, if you know roughly how big your // table will be, default it to that size when you create it. // // Key usability feature: // // 1. Allows node hash values to either be cached or not. // // 2. Allows for alternative keys with methods such as GetAlt(). Handy // if you need to search nodes by their components; no need to create // temporary nodes. // // *** Hash functors: // // IdentityHash - use when the key is already a good hash // HFixedSizeHash - general hash based on object's in-memory representation. // Hash is just the input value; can use this for integer-indexed hash tables. template class IdentityHash { public: UPInt operator()(const C& data) const { return (UPInt) data; } }; // Computes a hash of an object's representation. template class FixedSizeHash { public: // Alternative: "sdbm" hash function, suggested at same web page // above, http::/www.cs.yorku.ca/~oz/hash.html // This is somewhat slower then Bernstein, but it works way better than the above // hash function for hashing large numbers of 32-bit ints. static OVR_FORCE_INLINE UPInt SDBM_Hash(const void* data_in, UPInt size, UPInt seed = 5381) { const UByte* data = (const UByte*) data_in; UPInt h = seed; while (size > 0) { size--; h = (h << 16) + (h << 6) - h + (UPInt)data[size]; } return h; } UPInt operator()(const C& data) const { unsigned char* p = (unsigned char*) &data; int size = sizeof(C); return SDBM_Hash(p, size); } }; // *** HashsetEntry Entry types. // Compact hash table Entry type that re-computes hash keys during hash traversal. // Good to use if the hash function is cheap or the hash value is already cached in C. template class HashsetEntry { public: // Internal chaining for collisions. SPInt NextInChain; C Value; HashsetEntry() : NextInChain(-2) { } HashsetEntry(const HashsetEntry& e) : NextInChain(e.NextInChain), Value(e.Value) { } HashsetEntry(const C& key, SPInt next) : NextInChain(next), Value(key) { } bool IsEmpty() const { return NextInChain == -2; } bool IsEndOfChain() const { return NextInChain == -1; } // Cached hash value access - can be optimized bu storing hash locally. // Mask value only needs to be used if SetCachedHash is not implemented. UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; } void SetCachedHash(UPInt) {} void Clear() { Value.~C(); // placement delete NextInChain = -2; } // Free is only used from dtor of hash; Clear is used during regular operations: // assignment, hash reallocations, value reassignments, so on. void Free() { Clear(); } }; // Hash table Entry type that caches the Entry hash value for nodes, so that it // does not need to be re-computed during access. template class HashsetCachedEntry { public: // Internal chaining for collisions. SPInt NextInChain; UPInt HashValue; C Value; HashsetCachedEntry() : NextInChain(-2) { } HashsetCachedEntry(const HashsetCachedEntry& e) : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { } HashsetCachedEntry(const C& key, SPInt next) : NextInChain(next), Value(key) { } bool IsEmpty() const { return NextInChain == -2; } bool IsEndOfChain() const { return NextInChain == -1; } // Cached hash value access - can be optimized bu storing hash locally. // Mask value only needs to be used if SetCachedHash is not implemented. UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; } void SetCachedHash(UPInt hashValue) { HashValue = hashValue; } void Clear() { Value.~C(); NextInChain = -2; } // Free is only used from dtor of hash; Clear is used during regular operations: // assignment, hash reallocations, value reassignments, so on. void Free() { Clear(); } }; //----------------------------------------------------------------------------------- // *** HashSet implementation - relies on either cached or regular entries. // // Use: Entry = HashsetCachedEntry if hashes are expensive to // compute and thus need caching in entries. // Entry = HashsetEntry if hashes are already externally cached. // template, class AltHashF = HashF, class Allocator = ContainerAllocator, class Entry = HashsetCachedEntry > class HashSetBase { enum { HashMinSize = 8 }; public: OVR_MEMORY_REDEFINE_NEW(HashSetBase) typedef HashSetBase SelfType; HashSetBase() : pTable(NULL) { } HashSetBase(int sizeHint) : pTable(NULL) { SetCapacity(this, sizeHint); } HashSetBase(const SelfType& src) : pTable(NULL) { Assign(this, src); } ~HashSetBase() { if (pTable) { // Delete the entries. for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++) { Entry* e = &E(i); if (!e->IsEmpty()) e->Free(); } Allocator::Free(pTable); pTable = NULL; } } void Assign(const SelfType& src) { Clear(); if (src.IsEmpty() == false) { SetCapacity(src.GetSize()); for (ConstIterator it = src.Begin(); it != src.End(); ++it) { Add(*it); } } } // Remove all entries from the HashSet table. void Clear() { if (pTable) { // Delete the entries. for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++) { Entry* e = &E(i); if (!e->IsEmpty()) e->Clear(); } Allocator::Free(pTable); pTable = NULL; } } // Returns true if the HashSet is empty. bool IsEmpty() const { return pTable == NULL || pTable->EntryCount == 0; } // Set a new or existing value under the key, to the value. // Pass a different class of 'key' so that assignment reference object // can be passed instead of the actual object. template void Set(const CRef& key) { UPInt hashValue = HashF()(key); SPInt index = (SPInt)-1; if (pTable != NULL) index = findIndexCore(key, hashValue & pTable->SizeMask); if (index >= 0) { E(index).Value = key; } else { // Entry under key doesn't exist. add(key, hashValue); } } template inline void Add(const CRef& key) { UPInt hashValue = HashF()(key); add(key, hashValue); } // Remove by alternative key. template void RemoveAlt(const K& key) { if (pTable == NULL) return; UPInt hashValue = AltHashF()(key); SPInt index = hashValue & pTable->SizeMask; Entry* e = &E(index); // If empty node or occupied by collider, we have nothing to remove. if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != (UPInt)index)) return; // Save index SPInt naturalIndex = index; SPInt prevIndex = -1; while ((e->GetCachedHash(pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key)) { // Keep looking through the chain. prevIndex = index; index = e->NextInChain; if (index == -1) return; // End of chain, item not found e = &E(index); } // Found it - our item is at index if (naturalIndex == index) { // If we have a follower, move it to us if (!e->IsEndOfChain()) { Entry* enext = &E(e->NextInChain); e->Clear(); new (e) Entry(*enext); // Point us to the follower's cell that will be cleared e = enext; } } else { // We are not at natural index, so deal with the prev items next index E(prevIndex).NextInChain = e->NextInChain; } // Clear us, of the follower cell that was moved. e->Clear(); pTable->EntryCount --; // Should we check the size to condense hash? ... } // Remove by main key. template void Remove(const CRef& key) { RemoveAlt(key); } // Retrieve the pointer to a value under the given key. // - If there's no value under the key, then return NULL. // - If there is a value, return the pointer. template C* Get(const K& key) { SPInt index = findIndex(key); if (index >= 0) return &E(index).Value; return 0; } template const C* Get(const K& key) const { SPInt index = findIndex(key); if (index >= 0) return &E(index).Value; return 0; } // Alternative key versions of Get. Used by Hash. template const C* GetAlt(const K& key) const { SPInt index = findIndexAlt(key); if (index >= 0) return &E(index).Value; return 0; } template C* GetAlt(const K& key) { SPInt index = findIndexAlt(key); if (index >= 0) return &E(index).Value; return 0; } template bool GetAlt(const K& key, C* pval) const { SPInt index = findIndexAlt(key); if (index >= 0) { if (pval) *pval = E(index).Value; return true; } return false; } UPInt GetSize() const { return pTable == NULL ? 0 : (UPInt)pTable->EntryCount; } // Resize the HashSet table to fit one more Entry. Often this // doesn't involve any action. void CheckExpand() { if (pTable == NULL) { // Initial creation of table. Make a minimum-sized table. setRawCapacity(HashMinSize); } else if (pTable->EntryCount * 5 > (pTable->SizeMask + 1) * 4) { // pTable is more than 5/4 ths full. Expand. setRawCapacity((pTable->SizeMask + 1) * 2); } } // Hint the bucket count to >= n. void Resize(UPInt n) { // Not really sure what this means in relation to // STLport's hash_map... they say they "increase the // bucket count to at least n" -- but does that mean // their real capacity after Resize(n) is more like // n*2 (since they do linked-list chaining within // buckets?). SetCapacity(n); } // Size the HashSet so that it can comfortably contain the given // number of elements. If the HashSet already contains more // elements than newSize, then this may be a no-op. void SetCapacity(UPInt newSize) { UPInt newRawSize = (newSize * 5) / 4; if (newRawSize <= GetSize()) return; setRawCapacity(newRawSize); } // Disable inappropriate 'operator ->' warning on MSVC6. #ifdef OVR_CC_MSVC #if (OVR_CC_MSVC < 1300) # pragma warning(disable : 4284) #endif #endif // Iterator API, like STL. struct ConstIterator { const C& operator * () const { OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask); return pHash->E(Index).Value; } const C* operator -> () const { OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask); return &pHash->E(Index).Value; } void operator ++ () { // Find next non-empty Entry. if (Index <= (SPInt)pHash->pTable->SizeMask) { Index++; while ((UPInt)Index <= pHash->pTable->SizeMask && pHash->E(Index).IsEmpty()) { Index++; } } } bool operator == (const ConstIterator& it) const { if (IsEnd() && it.IsEnd()) { return true; } else { return (pHash == it.pHash) && (Index == it.Index); } } bool operator != (const ConstIterator& it) const { return ! (*this == it); } bool IsEnd() const { return (pHash == NULL) || (pHash->pTable == NULL) || (Index > (SPInt)pHash->pTable->SizeMask); } ConstIterator() : pHash(NULL), Index(0) { } public: // Constructor was intentionally made public to allow create // iterator with arbitrary index. ConstIterator(const SelfType* h, SPInt index) : pHash(h), Index(index) { } const SelfType* GetContainer() const { return pHash; } SPInt GetIndex() const { return Index; } protected: friend class HashSetBase; const SelfType* pHash; SPInt Index; }; friend struct ConstIterator; // Non-const Iterator; Get most of it from ConstIterator. struct Iterator : public ConstIterator { // Allow non-const access to entries. C& operator*() const { OVR_ASSERT(ConstIterator::Index >= 0 && ConstIterator::Index <= (SPInt)ConstIterator::pHash->pTable->SizeMask); return const_cast(ConstIterator::pHash)->E(ConstIterator::Index).Value; } C* operator->() const { return &(operator*()); } Iterator() : ConstIterator(NULL, 0) { } // Removes current element from Hash void Remove() { RemoveAlt(operator*()); } template void RemoveAlt(const K& key) { SelfType* phash = const_cast(ConstIterator::pHash); //Entry* ee = &phash->E(ConstIterator::Index); //const C& key = ee->Value; UPInt hashValue = AltHashF()(key); SPInt index = hashValue & phash->pTable->SizeMask; Entry* e = &phash->E(index); // If empty node or occupied by collider, we have nothing to remove. if (e->IsEmpty() || (e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)index)) return; // Save index SPInt naturalIndex = index; SPInt prevIndex = -1; while ((e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key)) { // Keep looking through the chain. prevIndex = index; index = e->NextInChain; if (index == -1) return; // End of chain, item not found e = &phash->E(index); } if (index == (SPInt)ConstIterator::Index) { // Found it - our item is at index if (naturalIndex == index) { // If we have a follower, move it to us if (!e->IsEndOfChain()) { Entry* enext = &phash->E(e->NextInChain); e->Clear(); new (e) Entry(*enext); // Point us to the follower's cell that will be cleared e = enext; --ConstIterator::Index; } } else { // We are not at natural index, so deal with the prev items next index phash->E(prevIndex).NextInChain = e->NextInChain; } // Clear us, of the follower cell that was moved. e->Clear(); phash->pTable->EntryCount --; } else OVR_ASSERT(0); //? } private: friend class HashSetBase; Iterator(SelfType* h, SPInt i0) : ConstIterator(h, i0) { } }; friend struct Iterator; Iterator Begin() { if (pTable == 0) return Iterator(NULL, 0); // Scan till we hit the First valid Entry. UPInt i0 = 0; while (i0 <= pTable->SizeMask && E(i0).IsEmpty()) { i0++; } return Iterator(this, i0); } Iterator End() { return Iterator(NULL, 0); } ConstIterator Begin() const { return const_cast(this)->Begin(); } ConstIterator End() const { return const_cast(this)->End(); } template Iterator Find(const K& key) { SPInt index = findIndex(key); if (index >= 0) return Iterator(this, index); return Iterator(NULL, 0); } template Iterator FindAlt(const K& key) { SPInt index = findIndexAlt(key); if (index >= 0) return Iterator(this, index); return Iterator(NULL, 0); } template ConstIterator Find(const K& key) const { return const_cast(this)->Find(key); } template ConstIterator FindAlt(const K& key) const { return const_cast(this)->FindAlt(key); } private: // Find the index of the matching Entry. If no match, then return -1. template SPInt findIndex(const K& key) const { if (pTable == NULL) return -1; UPInt hashValue = HashF()(key) & pTable->SizeMask; return findIndexCore(key, hashValue); } template SPInt findIndexAlt(const K& key) const { if (pTable == NULL) return -1; UPInt hashValue = AltHashF()(key) & pTable->SizeMask; return findIndexCore(key, hashValue); } // Find the index of the matching Entry. If no match, then return -1. template SPInt findIndexCore(const K& key, UPInt hashValue) const { // Table must exist. OVR_ASSERT(pTable != 0); // Hash key must be 'and-ed' by the caller. OVR_ASSERT((hashValue & ~pTable->SizeMask) == 0); UPInt index = hashValue; const Entry* e = &E(index); // If empty or occupied by a collider, not found. if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != index)) return -1; while(1) { OVR_ASSERT(e->GetCachedHash(pTable->SizeMask) == hashValue); if (e->GetCachedHash(pTable->SizeMask) == hashValue && e->Value == key) { // Found it. return index; } // Values can not be equal at this point. // That would mean that the hash key for the same value differs. OVR_ASSERT(!(e->Value == key)); // Keep looking through the chain. index = e->NextInChain; if (index == (UPInt)-1) break; // end of chain e = &E(index); OVR_ASSERT(!e->IsEmpty()); } return -1; } // Add a new value to the HashSet table, under the specified key. template void add(const CRef& key, UPInt hashValue) { CheckExpand(); hashValue &= pTable->SizeMask; pTable->EntryCount++; SPInt index = hashValue; Entry* naturalEntry = &(E(index)); if (naturalEntry->IsEmpty()) { // Put the new Entry in. new (naturalEntry) Entry(key, -1); } else { // Find a blank spot. SPInt blankIndex = index; do { blankIndex = (blankIndex + 1) & pTable->SizeMask; } while(!E(blankIndex).IsEmpty()); Entry* blankEntry = &E(blankIndex); if (naturalEntry->GetCachedHash(pTable->SizeMask) == (UPInt)index) { // Collision. Link into this chain. // Move existing list head. new (blankEntry) Entry(*naturalEntry); // placement new, copy ctor // Put the new info in the natural Entry. naturalEntry->Value = key; naturalEntry->NextInChain = blankIndex; } else { // Existing Entry does not naturally // belong in this slot. Existing // Entry must be moved. // Find natural location of collided element (i.e. root of chain) SPInt collidedIndex = naturalEntry->GetCachedHash(pTable->SizeMask); OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask); for (;;) { Entry* e = &E(collidedIndex); if (e->NextInChain == index) { // Here's where we need to splice. new (blankEntry) Entry(*naturalEntry); e->NextInChain = blankIndex; break; } collidedIndex = e->NextInChain; OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask); } // Put the new data in the natural Entry. naturalEntry->Value = key; naturalEntry->NextInChain = -1; } } // Record hash value: has effect only if cached node is used. naturalEntry->SetCachedHash(hashValue); } // Index access helpers. Entry& E(UPInt index) { // Must have pTable and access needs to be within bounds. OVR_ASSERT(index <= pTable->SizeMask); return *(((Entry*) (pTable + 1)) + index); } const Entry& E(UPInt index) const { OVR_ASSERT(index <= pTable->SizeMask); return *(((Entry*) (pTable + 1)) + index); } // Resize the HashSet table to the given size (Rehash the // contents of the current table). The arg is the number of // HashSet table entries, not the number of elements we should // actually contain (which will be less than this). void setRawCapacity(UPInt newSize) { if (newSize == 0) { // Special case. Clear(); return; } // Minimum size; don't incur rehashing cost when expanding // very small tables. Not that we perform this check before // 'log2f' call to avoid fp exception with newSize == 1. if (newSize < HashMinSize) newSize = HashMinSize; else { // Force newSize to be a power of two. int bits = Alg::UpperBit(newSize-1) + 1; // Chop( Log2f((float)(newSize-1)) + 1); OVR_ASSERT((UPInt(1) << bits) >= newSize); newSize = UPInt(1) << bits; } SelfType newHash; newHash.pTable = (TableType*) Allocator::Alloc( sizeof(TableType) + sizeof(Entry) * newSize); // Need to do something on alloc failure! OVR_ASSERT(newHash.pTable); newHash.pTable->EntryCount = 0; newHash.pTable->SizeMask = newSize - 1; UPInt i, n; // Mark all entries as empty. for (i = 0; i < newSize; i++) newHash.E(i).NextInChain = -2; // Copy stuff to newHash if (pTable) { for (i = 0, n = pTable->SizeMask; i <= n; i++) { Entry* e = &E(i); if (e->IsEmpty() == false) { // Insert old Entry into new HashSet. newHash.Add(e->Value); // placement delete of old element e->Clear(); } } // Delete our old data buffer. Allocator::Free(pTable); } // Steal newHash's data. pTable = newHash.pTable; newHash.pTable = NULL; } struct TableType { UPInt EntryCount; UPInt SizeMask; // Entry array follows this structure // in memory. }; TableType* pTable; }; //----------------------------------------------------------------------------------- template, class AltHashF = HashF, class Allocator = ContainerAllocator, class Entry = HashsetCachedEntry > class HashSet : public HashSetBase { public: typedef HashSetBase BaseType; typedef HashSet SelfType; HashSet() { } HashSet(int sizeHint) : BaseType(sizeHint) { } HashSet(const SelfType& src) : BaseType(src) { } ~HashSet() { } void operator = (const SelfType& src) { BaseType::Assign(src); } // Set a new or existing value under the key, to the value. // Pass a different class of 'key' so that assignment reference object // can be passed instead of the actual object. template void Set(const CRef& key) { BaseType::Set(key); } template inline void Add(const CRef& key) { BaseType::Add(key); } // Hint the bucket count to >= n. void Resize(UPInt n) { BaseType::SetCapacity(n); } // Size the HashSet so that it can comfortably contain the given // number of elements. If the HashSet already contains more // elements than newSize, then this may be a no-op. void SetCapacity(UPInt newSize) { BaseType::SetCapacity(newSize); } }; // HashSet with uncached hash code; declared for convenience. template, class AltHashF = HashF, class Allocator = ContainerAllocator > class HashSetUncached : public HashSet > { public: typedef HashSetUncached SelfType; typedef HashSet > BaseType; // Delegated constructors. HashSetUncached() { } HashSetUncached(int sizeHint) : BaseType(sizeHint) { } HashSetUncached(const SelfType& src) : BaseType(src) { } ~HashSetUncached() { } void operator = (const SelfType& src) { BaseType::operator = (src); } }; //----------------------------------------------------------------------------------- // ***** Hash hash table implementation // Node for Hash - necessary so that Hash can delegate its implementation // to HashSet. template struct HashNode { typedef HashNode SelfType; typedef C FirstType; typedef U SecondType; C First; U Second; // NodeRef is used to allow passing of elements into HashSet // without using a temporary object. struct NodeRef { const C* pFirst; const U* pSecond; NodeRef(const C& f, const U& s) : pFirst(&f), pSecond(&s) { } NodeRef(const NodeRef& src) : pFirst(src.pFirst), pSecond(src.pSecond) { } // Enable computation of ghash_node_hashf. inline UPInt GetHash() const { return HashF()(*pFirst); } // Necessary conversion to allow HashNode::operator == to work. operator const C& () const { return *pFirst; } }; // Note: No default constructor is necessary. HashNode(const HashNode& src) : First(src.First), Second(src.Second) { } HashNode(const NodeRef& src) : First(*src.pFirst), Second(*src.pSecond) { } void operator = (const NodeRef& src) { First = *src.pFirst; Second = *src.pSecond; } template bool operator == (const K& src) const { return (First == src); } template static UPInt CalcHash(const K& data) { return HashF()(data); } inline UPInt GetHash() const { return HashF()(First); } // Hash functors used with this node. A separate functor is used for alternative // key lookup so that it does not need to access the '.First' element. struct NodeHashF { template UPInt operator()(const K& data) const { return data.GetHash(); } }; struct NodeAltHashF { template UPInt operator()(const K& data) const { return HashNode::CalcHash(data); } }; }; // **** Extra hashset_entry types to allow NodeRef construction. // The big difference between the below types and the ones used in hash_set is that // these allow initializing the node with 'typename C::NodeRef& keyRef', which // is critical to avoid temporary node allocation on stack when using placement new. // Compact hash table Entry type that re-computes hash keys during hash traversal. // Good to use if the hash function is cheap or the hash value is already cached in C. template class HashsetNodeEntry { public: // Internal chaining for collisions. SPInt NextInChain; C Value; HashsetNodeEntry() : NextInChain(-2) { } HashsetNodeEntry(const HashsetNodeEntry& e) : NextInChain(e.NextInChain), Value(e.Value) { } HashsetNodeEntry(const C& key, SPInt next) : NextInChain(next), Value(key) { } HashsetNodeEntry(const typename C::NodeRef& keyRef, SPInt next) : NextInChain(next), Value(keyRef) { } bool IsEmpty() const { return NextInChain == -2; } bool IsEndOfChain() const { return NextInChain == -1; } UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; } void SetCachedHash(UPInt hashValue) { OVR_UNUSED(hashValue); } void Clear() { Value.~C(); // placement delete NextInChain = -2; } // Free is only used from dtor of hash; Clear is used during regular operations: // assignment, hash reallocations, value reassignments, so on. void Free() { Clear(); } }; // Hash table Entry type that caches the Entry hash value for nodes, so that it // does not need to be re-computed during access. template class HashsetCachedNodeEntry { public: // Internal chaining for collisions. SPInt NextInChain; UPInt HashValue; C Value; HashsetCachedNodeEntry() : NextInChain(-2) { } HashsetCachedNodeEntry(const HashsetCachedNodeEntry& e) : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { } HashsetCachedNodeEntry(const C& key, SPInt next) : NextInChain(next), Value(key) { } HashsetCachedNodeEntry(const typename C::NodeRef& keyRef, SPInt next) : NextInChain(next), Value(keyRef) { } bool IsEmpty() const { return NextInChain == -2; } bool IsEndOfChain() const { return NextInChain == -1; } UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; } void SetCachedHash(UPInt hashValue) { HashValue = hashValue; } void Clear() { Value.~C(); NextInChain = -2; } // Free is only used from dtor of hash; Clear is used during regular operations: // assignment, hash reallocations, value reassignments, so on. void Free() { Clear(); } }; //----------------------------------------------------------------------------------- template, class Allocator = ContainerAllocator, class HashNode = OVR::HashNode, class Entry = HashsetCachedNodeEntry, class Container = HashSet > class Hash { public: OVR_MEMORY_REDEFINE_NEW(Hash) // Types used for hash_set. typedef U ValueType; typedef Hash SelfType; // Actual hash table itself, implemented as hash_set. Container mHash; public: Hash() { } Hash(int sizeHint) : mHash(sizeHint) { } Hash(const SelfType& src) : mHash(src.mHash) { } ~Hash() { } void operator = (const SelfType& src) { mHash = src.mHash; } // Remove all entries from the Hash table. inline void Clear() { mHash.Clear(); } // Returns true if the Hash is empty. inline bool IsEmpty() const { return mHash.IsEmpty(); } // Access (set). inline void Set(const C& key, const U& value) { typename HashNode::NodeRef e(key, value); mHash.Set(e); } inline void Add(const C& key, const U& value) { typename HashNode::NodeRef e(key, value); mHash.Add(e); } // Removes an element by clearing its Entry. inline void Remove(const C& key) { mHash.RemoveAlt(key); } template inline void RemoveAlt(const K& key) { mHash.RemoveAlt(key); } // Retrieve the value under the given key. // - If there's no value under the key, then return false and leave *pvalue alone. // - If there is a value, return true, and Set *Pvalue to the Entry's value. // - If value == NULL, return true or false according to the presence of the key. bool Get(const C& key, U* pvalue) const { const HashNode* p = mHash.GetAlt(key); if (p) { if (pvalue) *pvalue = p->Second; return true; } return false; } template bool GetAlt(const K& key, U* pvalue) const { const HashNode* p = mHash.GetAlt(key); if (p) { if (pvalue) *pvalue = p->Second; return true; } return false; } // Retrieve the pointer to a value under the given key. // - If there's no value under the key, then return NULL. // - If there is a value, return the pointer. inline U* Get(const C& key) { HashNode* p = mHash.GetAlt(key); return p ? &p->Second : 0; } inline const U* Get(const C& key) const { const HashNode* p = mHash.GetAlt(key); return p ? &p->Second : 0; } template inline U* GetAlt(const K& key) { HashNode* p = mHash.GetAlt(key); return p ? &p->Second : 0; } template inline const U* GetAlt(const K& key) const { const HashNode* p = mHash.GetAlt(key); return p ? &p->Second : 0; } // Sizing methods - delegate to Hash. inline UPInt GetSize() const { return mHash.GetSize(); } inline void Resize(UPInt n) { mHash.Resize(n); } inline void SetCapacity(UPInt newSize) { mHash.SetCapacity(newSize); } // Iterator API, like STL. typedef typename Container::ConstIterator ConstIterator; typedef typename Container::Iterator Iterator; inline Iterator Begin() { return mHash.Begin(); } inline Iterator End() { return mHash.End(); } inline ConstIterator Begin() const { return mHash.Begin(); } inline ConstIterator End() const { return mHash.End(); } Iterator Find(const C& key) { return mHash.FindAlt(key); } ConstIterator Find(const C& key) const { return mHash.FindAlt(key); } template Iterator FindAlt(const K& key) { return mHash.FindAlt(key); } template ConstIterator FindAlt(const K& key) const { return mHash.FindAlt(key); } }; // Hash with uncached hash code; declared for convenience. template, class Allocator = ContainerAllocator > class HashUncached : public Hash, HashsetNodeEntry, typename HashNode::NodeHashF> > { public: typedef HashUncached SelfType; typedef Hash, HashsetNodeEntry, typename HashNode::NodeHashF> > BaseType; // Delegated constructors. HashUncached() { } HashUncached(int sizeHint) : BaseType(sizeHint) { } HashUncached(const SelfType& src) : BaseType(src) { } ~HashUncached() { } void operator = (const SelfType& src) { BaseType::operator = (src); } }; // And identity hash in which keys serve as hash value. Can be uncached, // since hash computation is assumed cheap. template, class HashF = IdentityHash > class HashIdentity : public HashUncached { public: typedef HashIdentity SelfType; typedef HashUncached BaseType; // Delegated constructors. HashIdentity() { } HashIdentity(int sizeHint) : BaseType(sizeHint) { } HashIdentity(const SelfType& src) : BaseType(src) { } ~HashIdentity() { } void operator = (const SelfType& src) { BaseType::operator = (src); } }; } // OVR #ifdef OVR_DEFINE_NEW #define new OVR_DEFINE_NEW #endif #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_KeyCodes.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_KeyCodes.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_KeyCodes.h Content : Common keyboard constants Created : September 19, 2012 Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_KeyCodes_h #define OVR_KeyCodes_h namespace OVR { //----------------------------------------------------------------------------------- // ***** KeyCode // KeyCode enumeration defines platform-independent keyboard key constants. // Note that Key_A through Key_Z are mapped to capital ascii constants. enum KeyCode { // Key_None indicates that no key was specified. Key_None = 0, // A through Z and numbers 0 through 9. Key_A = 65, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G, Key_H, Key_I, Key_J, Key_K, Key_L, Key_M, Key_N, Key_O, Key_P, Key_Q, Key_R, Key_S, Key_T, Key_U, Key_V, Key_W, Key_X, Key_Y, Key_Z, Key_Num0 = 48, Key_Num1, Key_Num2, Key_Num3, Key_Num4, Key_Num5, Key_Num6, Key_Num7, Key_Num8, Key_Num9, // Numeric keypad. Key_KP_0 = 0xa0, Key_KP_1, Key_KP_2, Key_KP_3, Key_KP_4, Key_KP_5, Key_KP_6, Key_KP_7, Key_KP_8, Key_KP_9, Key_KP_Multiply, Key_KP_Add, Key_KP_Enter, Key_KP_Subtract, Key_KP_Decimal, Key_KP_Divide, // Function keys. Key_F1 = 0xb0, Key_F2, Key_F3, Key_F4, Key_F5, Key_F6, Key_F7, Key_F8, Key_F9, Key_F10, Key_F11, Key_F12, Key_F13, Key_F14, Key_F15, // Other keys. Key_Backspace = 8, Key_Tab, Key_Clear = 12, Key_Return, Key_Shift = 16, Key_Control, Key_Alt, Key_Pause, Key_CapsLock = 20, // Toggle Key_Escape = 27, Key_Space = 32, Key_Quote = 39, Key_PageUp = 0xc0, Key_PageDown, Key_End, Key_Home, Key_Left, Key_Up, Key_Right, Key_Down, Key_Insert, Key_Delete, Key_Help, Key_Comma = 44, Key_Minus, Key_Slash = 47, Key_Period, Key_NumLock = 144, // Toggle Key_ScrollLock = 145, // Toggle Key_Semicolon = 59, Key_Equal = 61, Key_Bar = 192, Key_BracketLeft = 91, Key_Backslash, Key_BracketRight, Key_OEM_AX = 0xE1, // 'AX' key on Japanese AX keyboard Key_OEM_102 = 0xE2, // "<>" or "\|" on RT 102-key keyboard. Key_ICO_HELP = 0xE3, // Help key on ICO Key_ICO_00 = 0xE4, // 00 key on ICO Key_Meta, // Total number of keys. Key_CodeCount }; //----------------------------------------------------------------------------------- class KeyModifiers { public: enum { Key_ShiftPressed = 0x01, Key_CtrlPressed = 0x02, Key_AltPressed = 0x04, Key_MetaPressed = 0x08, Key_CapsToggled = 0x10, Key_NumToggled = 0x20, Key_ScrollToggled = 0x40, Initialized_Bit = 0x80, Initialized_Mask = 0xFF }; unsigned char States; KeyModifiers() : States(0) { } KeyModifiers(unsigned char st) : States((unsigned char)(st | Initialized_Bit)) { } void Reset() { States = 0; } bool IsShiftPressed() const { return (States & Key_ShiftPressed) != 0; } bool IsCtrlPressed() const { return (States & Key_CtrlPressed) != 0; } bool IsAltPressed() const { return (States & Key_AltPressed) != 0; } bool IsMetaPressed() const { return (States & Key_MetaPressed) != 0; } bool IsCapsToggled() const { return (States & Key_CapsToggled) != 0; } bool IsNumToggled() const { return (States & Key_NumToggled) != 0; } bool IsScrollToggled() const{ return (States & Key_ScrollToggled) != 0; } void SetShiftPressed(bool v = true) { (v) ? States |= Key_ShiftPressed : States &= ~Key_ShiftPressed; } void SetCtrlPressed(bool v = true) { (v) ? States |= Key_CtrlPressed : States &= ~Key_CtrlPressed; } void SetAltPressed(bool v = true) { (v) ? States |= Key_AltPressed : States &= ~Key_AltPressed; } void SetMetaPressed(bool v = true) { (v) ? States |= Key_MetaPressed : States &= ~Key_MetaPressed; } void SetCapsToggled(bool v = true) { (v) ? States |= Key_CapsToggled : States &= ~Key_CapsToggled; } void SetNumToggled(bool v = true) { (v) ? States |= Key_NumToggled : States &= ~Key_NumToggled; } void SetScrollToggled(bool v = true) { (v) ? States |= Key_ScrollToggled: States &= ~Key_ScrollToggled; } bool IsInitialized() const { return (States & Initialized_Mask) != 0; } }; //----------------------------------------------------------------------------------- /* enum PadKeyCode { Pad_None, // Indicates absence of key code. Pad_Back, Pad_Start, Pad_A, Pad_B, Pad_X, Pad_Y, Pad_R1, // RightShoulder; Pad_L1, // LeftShoulder; Pad_R2, // RightTrigger; Pad_L2, // LeftTrigger; Pad_Up, Pad_Down, Pad_Right, Pad_Left, Pad_Plus, Pad_Minus, Pad_1, Pad_2, Pad_H, Pad_C, Pad_Z, Pad_O, Pad_T, Pad_S, Pad_Select, Pad_Home, Pad_RT, // RightThumb; Pad_LT // LeftThumb; }; */ } // OVR #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_List.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_List.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR Filename : OVR_List.h Content : Template implementation for doubly-connected linked List Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_List_h #define OVR_List_h #include "OVR_Types.h" namespace OVR { //----------------------------------------------------------------------------------- // ***** ListNode // // Base class for the elements of the intrusive linked list. // To store elements in the List do: // // struct MyData : ListNode // { // . . . // }; template struct ListNode { union { T* pPrev; void* pVoidPrev; }; union { T* pNext; void* pVoidNext; }; void RemoveNode() { pPrev->pNext = pNext; pNext->pPrev = pPrev; } // Removes us from the list and inserts pnew there instead. void ReplaceNodeWith(T* pnew) { pPrev->pNext = pnew; pNext->pPrev = pnew; pnew->pPrev = pPrev; pnew->pNext = pNext; } // Inserts the argument linked list node after us in the list. void InsertNodeAfter(T* p) { p->pPrev = pNext->pPrev; // this p->pNext = pNext; pNext->pPrev = p; pNext = p; } // Inserts the argument linked list node before us in the list. void InsertNodeBefore(T* p) { p->pNext = pNext->pPrev; // this p->pPrev = pPrev; pPrev->pNext = p; pPrev = p; } void Alloc_MoveTo(ListNode* pdest) { pdest->pNext = pNext; pdest->pPrev = pPrev; pPrev->pNext = (T*)pdest; pNext->pPrev = (T*)pdest; } }; //------------------------------------------------------------------------ // ***** List // // Doubly linked intrusive list. // The data type must be derived from ListNode. // // Adding: PushFront(), PushBack(). // Removing: Remove() - the element must be in the list! // Moving: BringToFront(), SendToBack() - the element must be in the list! // // Iterating: // MyData* data = MyList.GetFirst(); // while (!MyList.IsNull(data)) // { // . . . // data = MyList.GetNext(data); // } // // Removing: // MyData* data = MyList.GetFirst(); // while (!MyList.IsNull(data)) // { // MyData* next = MyList.GetNext(data); // if (ToBeRemoved(data)) // MyList.Remove(data); // data = next; // } // // List<> represents a doubly-linked list of T, where each T must derive // from ListNode. B specifies the base class that was directly // derived from ListNode, and is only necessary if there is an intermediate // inheritance chain. template class List { public: typedef T ValueType; List() { Root.pNext = Root.pPrev = (ValueType*)&Root; } void Clear() { Root.pNext = Root.pPrev = (ValueType*)&Root; } const ValueType* GetFirst() const { return (const ValueType*)Root.pNext; } const ValueType* GetLast () const { return (const ValueType*)Root.pPrev; } ValueType* GetFirst() { return (ValueType*)Root.pNext; } ValueType* GetLast () { return (ValueType*)Root.pPrev; } // Determine if list is empty (i.e.) points to itself. // Go through void* access to avoid issues with strict-aliasing optimizing out the // access after RemoveNode(), etc. bool IsEmpty() const { return Root.pVoidNext == (const T*)(const B*)&Root; } bool IsFirst(const ValueType* p) const { return p == Root.pNext; } bool IsLast (const ValueType* p) const { return p == Root.pPrev; } bool IsNull (const ValueType* p) const { return p == (const T*)(const B*)&Root; } inline static const ValueType* GetPrev(const ValueType* p) { return (const ValueType*)p->pPrev; } inline static const ValueType* GetNext(const ValueType* p) { return (const ValueType*)p->pNext; } inline static ValueType* GetPrev( ValueType* p) { return (ValueType*)p->pPrev; } inline static ValueType* GetNext( ValueType* p) { return (ValueType*)p->pNext; } void PushFront(ValueType* p) { p->pNext = Root.pNext; p->pPrev = (ValueType*)&Root; Root.pNext->pPrev = p; Root.pNext = p; } void PushBack(ValueType* p) { p->pPrev = Root.pPrev; p->pNext = (ValueType*)&Root; Root.pPrev->pNext = p; Root.pPrev = p; } static void Remove(ValueType* p) { p->pPrev->pNext = p->pNext; p->pNext->pPrev = p->pPrev; } void BringToFront(ValueType* p) { Remove(p); PushFront(p); } void SendToBack(ValueType* p) { Remove(p); PushBack(p); } // Appends the contents of the argument list to the front of this list; // items are removed from the argument list. void PushListToFront(List& src) { if (!src.IsEmpty()) { ValueType* pfirst = src.GetFirst(); ValueType* plast = src.GetLast(); src.Clear(); plast->pNext = Root.pNext; pfirst->pPrev = (ValueType*)&Root; Root.pNext->pPrev = plast; Root.pNext = pfirst; } } void PushListToBack(List& src) { if (!src.IsEmpty()) { ValueType* pfirst = src.GetFirst(); ValueType* plast = src.GetLast(); src.Clear(); plast->pNext = (ValueType*)&Root; pfirst->pPrev = Root.pPrev; Root.pPrev->pNext = pfirst; Root.pPrev = plast; } } // Removes all source list items after (and including) the 'pfirst' node from the // source list and adds them to out list. void PushFollowingListItemsToFront(List& src, ValueType *pfirst) { if (pfirst != &src.Root) { ValueType *plast = src.Root.pPrev; // Remove list remainder from source. pfirst->pPrev->pNext = (ValueType*)&src.Root; src.Root.pPrev = pfirst->pPrev; // Add the rest of the items to list. plast->pNext = Root.pNext; pfirst->pPrev = (ValueType*)&Root; Root.pNext->pPrev = plast; Root.pNext = pfirst; } } // Removes all source list items up to but NOT including the 'pend' node from the // source list and adds them to out list. void PushPrecedingListItemsToFront(List& src, ValueType *ptail) { if (src.GetFirst() != ptail) { ValueType *pfirst = src.Root.pNext; ValueType *plast = ptail->pPrev; // Remove list remainder from source. ptail->pPrev = (ValueType*)&src.Root; src.Root.pNext = ptail; // Add the rest of the items to list. plast->pNext = Root.pNext; pfirst->pPrev = (ValueType*)&Root; Root.pNext->pPrev = plast; Root.pNext = pfirst; } } // Removes a range of source list items starting at 'pfirst' and up to, but not including 'pend', // and adds them to out list. Note that source items MUST already be in the list. void PushListItemsToFront(ValueType *pfirst, ValueType *pend) { if (pfirst != pend) { ValueType *plast = pend->pPrev; // Remove list remainder from source. pfirst->pPrev->pNext = pend; pend->pPrev = pfirst->pPrev; // Add the rest of the items to list. plast->pNext = Root.pNext; pfirst->pPrev = (ValueType*)&Root; Root.pNext->pPrev = plast; Root.pNext = pfirst; } } void Alloc_MoveTo(List* pdest) { if (IsEmpty()) pdest->Clear(); else { pdest->Root.pNext = Root.pNext; pdest->Root.pPrev = Root.pPrev; Root.pNext->pPrev = (ValueType*)&pdest->Root; Root.pPrev->pNext = (ValueType*)&pdest->Root; } } private: // Copying is prohibited List(const List&); const List& operator = (const List&); ListNode Root; }; //------------------------------------------------------------------------ // ***** FreeListElements // // Remove all elements in the list and free them in the allocator template void FreeListElements(List& list, Allocator& allocator) { typename List::ValueType* self = list.GetFirst(); while(!list.IsNull(self)) { typename List::ValueType* next = list.GetNext(self); allocator.Free(self); self = next; } list.Clear(); } } // OVR #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Log.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Log.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : OVR_Log.cpp Content : Logging support Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_Log.h" #include "OVR_Std.h" #include #include #if defined(OVR_OS_WIN32) #include #elif defined(OVR_OS_ANDROID) #include #endif namespace OVR { // Global Log pointer. Log* volatile OVR_GlobalLog = 0; //----------------------------------------------------------------------------------- // ***** Log Implementation Log::~Log() { // Clear out global log if (this == OVR_GlobalLog) { // TBD: perhaps we should ASSERT if this happens before system shutdown? OVR_GlobalLog = 0; } } void Log::LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList) { if ((messageType & LoggingMask) == 0) return; #ifndef OVR_BUILD_DEBUG if (IsDebugMessage(messageType)) return; #endif char buffer[MaxLogBufferMessageSize]; FormatLog(buffer, MaxLogBufferMessageSize, messageType, fmt, argList); DefaultLogOutput(buffer, IsDebugMessage(messageType)); } void OVR::Log::LogMessage(LogMessageType messageType, const char* pfmt, ...) { va_list argList; va_start(argList, pfmt); LogMessageVarg(messageType, pfmt, argList); va_end(argList); } void Log::FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType, const char* fmt, va_list argList) { bool addNewline = true; switch(messageType) { case Log_Error: OVR_strcpy(buffer, bufferSize, "Error: "); break; case Log_Debug: OVR_strcpy(buffer, bufferSize, "Debug: "); break; case Log_Assert: OVR_strcpy(buffer, bufferSize, "Assert: "); break; case Log_Text: buffer[0] = 0; addNewline = false; break; case Log_DebugText: buffer[0] = 0; addNewline = false; break; default: buffer[0] = 0; addNewline = false; break; } UPInt prefixLength = OVR_strlen(buffer); char *buffer2 = buffer + prefixLength; OVR_vsprintf(buffer2, bufferSize - prefixLength, fmt, argList); if (addNewline) OVR_strcat(buffer, bufferSize, "\n"); } void Log::DefaultLogOutput(const char* formattedText, bool debug) { #if defined(OVR_OS_WIN32) // Under Win32, output regular messages to console if it exists; debug window otherwise. static DWORD dummyMode; static bool hasConsole = (GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE) && (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dummyMode)); if (!hasConsole || debug) { ::OutputDebugStringA(formattedText); } else { fputs(formattedText, stdout); } #elif defined(OVR_OS_ANDROID) __android_log_write(ANDROID_LOG_INFO, "OVR", formattedText); #else fputs(formattedText, stdout); #endif // Just in case. OVR_UNUSED2(formattedText, debug); } //static void Log::SetGlobalLog(Log *log) { OVR_GlobalLog = log; } //static Log* Log::GetGlobalLog() { // No global log by default? // if (!OVR_GlobalLog) // OVR_GlobalLog = GetDefaultLog(); return OVR_GlobalLog; } //static Log* Log::GetDefaultLog() { // Create default log pointer statically so that it can be used // even during startup. static Log defaultLog; return &defaultLog; } //----------------------------------------------------------------------------------- // ***** Global Logging functions #define OVR_LOG_FUNCTION_IMPL(Name) \ void Log##Name(const char* fmt, ...) \ { \ if (OVR_GlobalLog) \ { \ va_list argList; va_start(argList, fmt); \ OVR_GlobalLog->LogMessageVarg(Log_##Name, fmt, argList); \ va_end(argList); \ } \ } OVR_LOG_FUNCTION_IMPL(Text) OVR_LOG_FUNCTION_IMPL(Error) #ifdef OVR_BUILD_DEBUG OVR_LOG_FUNCTION_IMPL(DebugText) OVR_LOG_FUNCTION_IMPL(Debug) OVR_LOG_FUNCTION_IMPL(Assert) #endif } // OVR \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Log.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Log.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR Filename : OVR_Log.h Content : Logging support Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Log_h #define OVR_Log_h #include "OVR_Types.h" #include namespace OVR { //----------------------------------------------------------------------------------- // ***** Logging Constants // LogMaskConstants defined bit mask constants that describe what log messages // should be displayed. enum LogMaskConstants { LogMask_Regular = 0x100, LogMask_Debug = 0x200, LogMask_None = 0, LogMask_All = LogMask_Regular|LogMask_Debug }; // LogMessageType describes the type of the log message, controls when it is // displayed and what prefix/suffix is given to it. Messages are subdivided into // regular and debug logging types. Debug logging is only generated in debug builds. // // Log_Text - General output text displayed without prefix or new-line. // Used in OVR libraries for general log flow messages // such as "Device Initialized". // // Log_Error - Error message output with "Error: %s\n", intended for // application/sample-level use only, in cases where an expected // operation failed. OVR libraries should not use this internally, // reporting status codes instead. // // Log_DebugText - Message without prefix or new lines; output in Debug build only. // // Log_Debug - Debug-build only message, formatted with "Debug: %s\n". // Intended to comment on incorrect API usage that doesn't lead // to crashes but can be avoided with proper use. // There is no Debug Error on purpose, since real errors should // be handled by API user. // // Log_Assert - Debug-build only message, formatted with "Assert: %s\n". // Intended for severe unrecoverable conditions in library // source code. Generated though OVR_ASSERT_MSG(c, "Text"). enum LogMessageType { // General Logging Log_Text = LogMask_Regular | 0, Log_Error = LogMask_Regular | 1, // "Error: %s\n". // Debug-only messages (not generated in release build) Log_DebugText = LogMask_Debug | 0, Log_Debug = LogMask_Debug | 1, // "Debug: %s\n". Log_Assert = LogMask_Debug | 2, // "Assert: %s\n". }; // LOG_VAARG_ATTRIBUTE macro, enforces printf-style fromatting for message types #ifdef __GNUC__ # define OVR_LOG_VAARG_ATTRIBUTE(a,b) __attribute__((format (printf, a, b))) #else # define OVR_LOG_VAARG_ATTRIBUTE(a,b) #endif //----------------------------------------------------------------------------------- // ***** Log // Log defines a base class interface that can be implemented to catch both // debug and runtime messages. // Debug logging can be overridden by calling Log::SetGlobalLog. class Log { friend class System; public: Log(unsigned logMask = LogMask_Debug) : LoggingMask(logMask) { } virtual ~Log(); // Log formating buffer size used by default LogMessageVarg. Longer strings are truncated. enum { MaxLogBufferMessageSize = 2048 }; unsigned GetLoggingMask() const { return LoggingMask; } void SetLoggingMask(unsigned logMask) { LoggingMask = logMask; } // This virtual function receives all the messages, // developers should override this function in order to do custom logging virtual void LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList); // Call the logging function with specific message type, with no type filtering. void LogMessage(LogMessageType messageType, const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(3,4); // Helper used by LogMessageVarg to format the log message, writing the resulting // string into buffer. It formats text based on fmt and appends prefix/new line // based on LogMessageType. static void FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType, const char* fmt, va_list argList); // Default log output implementation used by by LogMessageVarg. // Debug flag may be used to re-direct output on some platforms, but doesn't // necessarily disable it in release builds; that is the job of the called. static void DefaultLogOutput(const char* textBuffer, bool debug); // Determines if the specified message type is for debugging only. static bool IsDebugMessage(LogMessageType messageType) { return (messageType & LogMask_Debug) != 0; } // *** Global APIs // Global Log registration APIs. // - Global log is used for OVR_DEBUG messages. Set global log to null (0) // to disable all logging. static void SetGlobalLog(Log *log); static Log* GetGlobalLog(); // Returns default log singleton instance. static Log* GetDefaultLog(); // Applies logMask to the default log and returns a pointer to it. // By default, only Debug logging is enabled, so to avoid SDK generating console // messages in user app (those are always disabled in release build, // even if the flag is set). This function is useful in System constructor. static Log* ConfigureDefaultLog(unsigned logMask = LogMask_Debug) { Log* log = GetDefaultLog(); log->SetLoggingMask(logMask); return log; } private: // Logging mask described by LogMaskConstants. unsigned LoggingMask; }; //----------------------------------------------------------------------------------- // ***** Global Logging Functions and Debug Macros // These functions will output text to global log with semantics described by // their LogMessageType. void LogText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); void LogError(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); #ifdef OVR_BUILD_DEBUG // Debug build only logging. void LogDebugText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); void LogDebug(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); void LogAssert(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); // Macro to do debug logging, printf-style. // An extra set of set of parenthesis must be used around arguments, // as in: OVR_LOG_DEBUG(("Value %d", 2)). #define OVR_DEBUG_LOG(args) do { OVR::LogDebug args; } while(0) #define OVR_DEBUG_LOG_TEXT(args) do { OVR::LogDebugText args; } while(0) #define OVR_ASSERT_LOG(c, args) do { if (!(c)) { OVR::LogAssert args; OVR_DEBUG_BREAK; } } while(0) #else // If not in debug build, macros do nothing. #define OVR_DEBUG_LOG(args) ((void)0) #define OVR_DEBUG_LOG_TEXT(args) ((void)0) #define OVR_ASSERT_LOG(c, args) ((void)0) #endif } // OVR #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Math.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Math.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : OVR_Math.h Content : Implementation of 3D primitives such as vectors, matrices. Created : September 4, 2012 Authors : Andrew Reisse, Michael Antonov Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #include "OVR_Math.h" #include namespace OVR { //------------------------------------------------------------------------------------- // ***** Math // Single-precision Math constants class. const float Math::Pi = 3.1415926f; const float Math::TwoPi = 3.1415926f * 2; const float Math::PiOver2 = 3.1415926f / 2.0f; const float Math::PiOver4 = 3.1415926f / 4.0f; const float Math::E = 2.7182818f; const float Math::MaxValue = FLT_MAX; const float Math::MinPositiveValue = FLT_MIN; const float Math::RadToDegreeFactor = 360.0f / Math::TwoPi; const float Math::DegreeToRadFactor = Math::TwoPi / 360.0f; const float Math::Tolerance = 0.00001f; const float Math::SingularityRadius = 0.0000001f; // Use for Gimbal lock numerical problems // Double-precision Math constants class. const double Math::Pi = 3.14159265358979; const double Math::TwoPi = 3.14159265358979 * 2; const double Math::PiOver2 = 3.14159265358979 / 2.0; const double Math::PiOver4 = 3.14159265358979 / 4.0; const double Math::E = 2.71828182845905; const double Math::MaxValue = DBL_MAX; const double Math::MinPositiveValue = DBL_MIN; const double Math::RadToDegreeFactor = 360.0 / Math::TwoPi; const double Math::DegreeToRadFactor = Math::TwoPi / 360.0; const double Math::Tolerance = 0.00001; const double Math::SingularityRadius = 0.000000000001; // Use for Gimbal lock numerical problems //------------------------------------------------------------------------------------- // ***** Matrix4f Matrix4f Matrix4f::LookAtRH(const Vector3f& eye, const Vector3f& at, const Vector3f& up) { Vector3f z = (eye - at).Normalized(); // Forward Vector3f x = up.Cross(z).Normalized(); // Right Vector3f y = z.Cross(x); Matrix4f m(x.x, x.y, x.z, -(x * eye), y.x, y.y, y.z, -(y * eye), z.x, z.y, z.z, -(z * eye), 0, 0, 0, 1 ); return m; } Matrix4f Matrix4f::LookAtLH(const Vector3f& eye, const Vector3f& at, const Vector3f& up) { Vector3f z = (at - eye).Normalized(); // Forward Vector3f x = up.Cross(z).Normalized(); // Right Vector3f y = z.Cross(x); Matrix4f m(x.x, x.y, x.z, -(x * eye), y.x, y.y, y.z, -(y * eye), z.x, z.y, z.z, -(z * eye), 0, 0, 0, 1 ); return m; } Matrix4f Matrix4f::PerspectiveLH(float yfov, float aspect, float znear, float zfar) { Matrix4f m; float tanHalfFov = tan(yfov * 0.5f); m.M[0][0] = 1.0f / (aspect * tanHalfFov); m.M[1][1] = 1.0f / tanHalfFov; m.M[2][2] = zfar / (zfar - znear); m.M[3][2] = 1.0f; m.M[2][3] = (zfar * znear) / (znear - zfar); m.M[3][3] = 0.0f; // Note: Post-projection matrix result assumes Left-Handed coordinate system, // with Y up, X right and Z forward. This supports positive z-buffer values. return m; } Matrix4f Matrix4f::PerspectiveRH(float yfov, float aspect, float znear, float zfar) { Matrix4f m; float tanHalfFov = tan(yfov * 0.5f); m.M[0][0] = 1.0f / (aspect * tanHalfFov); m.M[1][1] = 1.0f / tanHalfFov; m.M[2][2] = zfar / (znear - zfar); // m.M[2][2] = zfar / (zfar - znear); m.M[3][2] = -1.0f; m.M[2][3] = (zfar * znear) / (znear - zfar); m.M[3][3] = 0.0f; // Note: Post-projection matrix result assumes Left-Handed coordinate system, // with Y up, X right and Z forward. This supports positive z-buffer values. // This is the case even for RHS cooridnate input. return m; } /* OffCenterLH 2*zn/(r-l) 0 0 0 0 2*zn/(t-b) 0 0 (l+r)/(l-r) (t+b)/(b-t) zf/(zf-zn) 1 0 0 zn*zf/(zn-zf) 0 */ Matrix4f Matrix4f::Ortho2D(float w, float h) { Matrix4f m; m.M[0][0] = 2.0f/w; m.M[1][1] = -2.0f/h; m.M[0][3] = -1.0; m.M[1][3] = 1.0; m.M[2][2] = 0; return m; } } \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Math.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Math.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Math.h Content : Implementation of 3D primitives such as vectors, matrices. Created : September 4, 2012 Authors : Andrew Reisse, Michael Antonov, Steve LaValle, Anna Yershova Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #ifndef OVR_Math_h #define OVR_Math_h #include #include #include #include "OVR_Types.h" #include "OVR_RefCount.h" namespace OVR { //------------------------------------------------------------------------------------- // Constants for 3D world/axis definitions. // Definitions of axes for coordinate and rotation conversions. enum Axis { Axis_X = 0, Axis_Y = 1, Axis_Z = 2 }; // RotateDirection describes the rotation direction around an axis, interpreted as follows: // CW - Clockwise while looking "down" from positive axis towards the origin. // CCW - Counter-clockwise while looking from the positive axis towards the origin, // which is in the negative axis direction. // CCW is the default for the RHS coordinate system. Oculus standard RHS coordinate // system defines Y up, X right, and Z back (pointing out from the screen). In this // system Rotate_CCW around Z will specifies counter-clockwise rotation in XY plane. enum RotateDirection { Rotate_CCW = 1, Rotate_CW = -1 }; enum HandedSystem { Handed_R = 1, Handed_L = -1 }; // AxisDirection describes which way the axis points. Used by WorldAxes. enum AxisDirection { Axis_Up = 2, Axis_Down = -2, Axis_Right = 1, Axis_Left = -1, Axis_In = 3, Axis_Out = -3 }; struct WorldAxes { AxisDirection XAxis, YAxis, ZAxis; WorldAxes(AxisDirection x, AxisDirection y, AxisDirection z) : XAxis(x), YAxis(y), ZAxis(z) { OVR_ASSERT(abs(x) != abs(y) && abs(y) != abs(z) && abs(z) != abs(x));} }; //------------------------------------------------------------------------------------- // ***** Math // Math class contains constants and functions. This class is a template specialized // per type, with Math and Math being distinct. template class Math { }; // Single-precision Math constants class. template<> class Math { public: static const float Pi; static const float TwoPi; static const float PiOver2; static const float PiOver4; static const float E; static const float MaxValue; // Largest positive float Value static const float MinPositiveValue; // Smallest possible positive value static const float RadToDegreeFactor; static const float DegreeToRadFactor; static const float Tolerance; // 0.00001f; static const float SingularityRadius; //0.00000000001f for Gimbal lock numerical problems }; // Double-precision Math constants class. template<> class Math { public: static const double Pi; static const double TwoPi; static const double PiOver2; static const double PiOver4; static const double E; static const double MaxValue; // Largest positive double Value static const double MinPositiveValue; // Smallest possible positive value static const double RadToDegreeFactor; static const double DegreeToRadFactor; static const double Tolerance; // 0.00001f; static const double SingularityRadius; //0.00000000001 for Gimbal lock numerical problems }; typedef Math Mathf; typedef Math Mathd; // Conversion functions between degrees and radians template FT RadToDegree(FT rads) { return rads * Math::RadToDegreeFactor; } template FT DegreeToRad(FT rads) { return rads * Math::DegreeToRadFactor; } template class Quat; //------------------------------------------------------------------------------------- // ***** Vector2f - 2D Vector2f // Vector2f represents a 2-dimensional vector or point in space, // consisting of coordinates x and y, template class Vector2 { public: T x, y; Vector2() : x(0), y(0) { } Vector2(T x_, T y_) : x(x_), y(y_) { } explicit Vector2(T s) : x(s), y(s) { } bool operator== (const Vector2& b) const { return x == b.x && y == b.y; } bool operator!= (const Vector2& b) const { return x != b.x || y != b.y; } Vector2 operator+ (const Vector2& b) const { return Vector2(x + b.x, y + b.y); } Vector2& operator+= (const Vector2& b) { x += b.x; y += b.y; return *this; } Vector2 operator- (const Vector2& b) const { return Vector2(x - b.x, y - b.y); } Vector2& operator-= (const Vector2& b) { x -= b.x; y -= b.y; return *this; } Vector2 operator- () const { return Vector2(-x, -y); } // Scalar multiplication/division scales vector. Vector2 operator* (T s) const { return Vector2(x*s, y*s); } Vector2& operator*= (T s) { x *= s; y *= s; return *this; } Vector2 operator/ (T s) const { T rcp = T(1)/s; return Vector2(x*rcp, y*rcp); } Vector2& operator/= (T s) { T rcp = T(1)/s; x *= rcp; y *= rcp; return *this; } // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance. bool Compare(const Vector2&b, T tolerance = Mathf::Tolerance) { return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance); } // Dot product overload. // Used to calculate angle q between two vectors among other things, // as (A dot B) = |a||b|cos(q). T operator* (const Vector2& b) const { return x*b.x + y*b.y; } // Returns the angle from this vector to b, in radians. T Angle(const Vector2& b) const { return acos((*this * b)/(Length()*b.Length())); } // Return Length of the vector squared. T LengthSq() const { return (x * x + y * y); } // Return vector length. T Length() const { return sqrt(LengthSq()); } // Returns distance between two points represented by vectors. T Distance(Vector2& b) const { return (*this - b).Length(); } // Determine if this a unit vector. bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance; } // Normalize, convention vector length to 1. void Normalize() { *this /= Length(); } // Returns normalized (unit) version of the vector without modifying itself. Vector2 Normalized() const { return *this / Length(); } // Linearly interpolates from this vector to another. // Factor should be between 0.0 and 1.0, with 0 giving full value to this. Vector2 Lerp(const Vector2& b, T f) const { return *this*(T(1) - f) + b*f; } // Projects this vector onto the argument; in other words, // A.Project(B) returns projection of vector A onto B. Vector2 ProjectTo(const Vector2& b) const { return b * ((*this * b) / b.LengthSq()); } }; typedef Vector2 Vector2f; typedef Vector2 Vector2d; //------------------------------------------------------------------------------------- // ***** Vector3f - 3D Vector3f // Vector3f represents a 3-dimensional vector or point in space, // consisting of coordinates x, y and z. template class Vector3 { public: T x, y, z; Vector3() : x(0), y(0), z(0) { } Vector3(T x_, T y_, T z_ = 0) : x(x_), y(y_), z(z_) { } explicit Vector3(T s) : x(s), y(s), z(s) { } bool operator== (const Vector3& b) const { return x == b.x && y == b.y && z == b.z; } bool operator!= (const Vector3& b) const { return x != b.x || y != b.y || z != b.z; } Vector3 operator+ (const Vector3& b) const { return Vector3(x + b.x, y + b.y, z + b.z); } Vector3& operator+= (const Vector3& b) { x += b.x; y += b.y; z += b.z; return *this; } Vector3 operator- (const Vector3& b) const { return Vector3(x - b.x, y - b.y, z - b.z); } Vector3& operator-= (const Vector3& b) { x -= b.x; y -= b.y; z -= b.z; return *this; } Vector3 operator- () const { return Vector3(-x, -y, -z); } // Scalar multiplication/division scales vector. Vector3 operator* (T s) const { return Vector3(x*s, y*s, z*s); } Vector3& operator*= (T s) { x *= s; y *= s; z *= s; return *this; } Vector3 operator/ (T s) const { T rcp = T(1)/s; return Vector3(x*rcp, y*rcp, z*rcp); } Vector3& operator/= (T s) { T rcp = T(1)/s; x *= rcp; y *= rcp; z *= rcp; return *this; } // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance. bool Compare(const Vector3&b, T tolerance = Mathf::Tolerance) { return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance) && (fabs(b.z-z) < tolerance); } // Dot product overload. // Used to calculate angle q between two vectors among other things, // as (A dot B) = |a||b|cos(q). T operator* (const Vector3& b) const { return x*b.x + y*b.y + z*b.z; } // Compute cross product, which generates a normal vector. // Direction vector can be determined by right-hand rule: Pointing index finder in // direction a and middle finger in direction b, thumb will point in a.Cross(b). Vector3 Cross(const Vector3& b) const { return Vector3(y*b.z - z*b.y, z*b.x - x*b.z, x*b.y - y*b.x); } // Returns the angle from this vector to b, in radians. T Angle(const Vector3& b) const { return acos((*this * b)/(Length()*b.Length())); } // Return Length of the vector squared. T LengthSq() const { return (x * x + y * y + z * z); } // Return vector length. T Length() const { return sqrt(LengthSq()); } // Returns distance between two points represented by vectors. T Distance(Vector3& b) const { return (*this - b).Length(); } // Determine if this a unit vector. bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance; } // Normalize, convention vector length to 1. void Normalize() { *this /= Length(); } // Returns normalized (unit) version of the vector without modifying itself. Vector3 Normalized() const { return *this / Length(); } // Linearly interpolates from this vector to another. // Factor should be between 0.0 and 1.0, with 0 giving full value to this. Vector3 Lerp(const Vector3& b, T f) const { return *this*(T(1) - f) + b*f; } // Projects this vector onto the argument; in other words, // A.Project(B) returns projection of vector A onto B. Vector3 ProjectTo(const Vector3& b) const { return b * ((*this * b) / b.LengthSq()); } }; typedef Vector3 Vector3f; typedef Vector3 Vector3d; //------------------------------------------------------------------------------------- // ***** Matrix4f // Matrix4f is a 4x4 matrix used for 3d transformations and projections. // Translation stored in the last column. // The matrix is stored in row-major order in memory, meaning that values // of the first row are stored before the next one. // // The arrangement of the matrix is chosen to be in Right-Handed // coordinate system and counterclockwise rotations when looking down // the axis // // Transformation Order: // - Transformations are applied from right to left, so the expression // M1 * M2 * M3 * V means that the vector V is transformed by M3 first, // followed by M2 and M1. // // Coordinate system: Right Handed // // Rotations: Counterclockwise when looking down the axis. All angles are in radians. // // | sx 01 02 tx | // First column (sx, 10, 20): Axis X basis vector. // | 10 sy 12 ty | // Second column (01, sy, 21): Axis Y basis vector. // | 20 21 sz tz | // Third columnt (02, 12, sz): Axis Z basis vector. // | 30 31 32 33 | // // The basis vectors are first three columns. class Matrix4f { static Matrix4f IdentityValue; public: float M[4][4]; enum NoInitType { NoInit }; // Construct with no memory initialization. Matrix4f(NoInitType) { } // By default, we construct identity matrix. Matrix4f() { SetIdentity(); } Matrix4f(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44) { M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = m14; M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = m24; M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = m34; M[3][0] = m41; M[3][1] = m42; M[3][2] = m43; M[3][3] = m44; } Matrix4f(float m11, float m12, float m13, float m21, float m22, float m23, float m31, float m32, float m33) { M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = 0; M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = 0; M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = 0; M[3][0] = 0; M[3][1] = 0; M[3][2] = 0; M[3][3] = 1; } static const Matrix4f& Identity() { return IdentityValue; } void SetIdentity() { M[0][0] = M[1][1] = M[2][2] = M[3][3] = 1; M[0][1] = M[1][0] = M[2][3] = M[3][1] = 0; M[0][2] = M[1][2] = M[2][0] = M[3][2] = 0; M[0][3] = M[1][3] = M[2][1] = M[3][0] = 0; } // Multiplies two matrices into destination with minimum copying. static Matrix4f& Multiply(Matrix4f* d, const Matrix4f& a, const Matrix4f& b) { OVR_ASSERT((d != &a) && (d != &b)); int i = 0; do { d->M[i][0] = a.M[i][0] * b.M[0][0] + a.M[i][1] * b.M[1][0] + a.M[i][2] * b.M[2][0] + a.M[i][3] * b.M[3][0]; d->M[i][1] = a.M[i][0] * b.M[0][1] + a.M[i][1] * b.M[1][1] + a.M[i][2] * b.M[2][1] + a.M[i][3] * b.M[3][1]; d->M[i][2] = a.M[i][0] * b.M[0][2] + a.M[i][1] * b.M[1][2] + a.M[i][2] * b.M[2][2] + a.M[i][3] * b.M[3][2]; d->M[i][3] = a.M[i][0] * b.M[0][3] + a.M[i][1] * b.M[1][3] + a.M[i][2] * b.M[2][3] + a.M[i][3] * b.M[3][3]; } while((++i) < 4); return *d; } Matrix4f operator* (const Matrix4f& b) const { Matrix4f result(Matrix4f::NoInit); Multiply(&result, *this, b); return result; } Matrix4f& operator*= (const Matrix4f& b) { return Multiply(this, Matrix4f(*this), b); } Matrix4f operator* (float s) const { return Matrix4f(M[0][0] * s, M[0][1] * s, M[0][2] * s, M[0][3] * s, M[1][0] * s, M[1][1] * s, M[1][2] * s, M[1][3] * s, M[2][0] * s, M[2][1] * s, M[2][2] * s, M[2][3] * s, M[3][0] * s, M[3][1] * s, M[3][2] * s, M[3][3] * s); } Matrix4f& operator*= (float s) { M[0][0] *= s; M[0][1] *= s; M[0][2] *= s; M[0][3] *= s; M[1][0] *= s; M[1][1] *= s; M[1][2] *= s; M[1][3] *= s; M[2][0] *= s; M[2][1] *= s; M[2][2] *= s; M[2][3] *= s; M[3][0] *= s; M[3][1] *= s; M[3][2] *= s; M[3][3] *= s; return *this; } Vector3f Transform(const Vector3f& v) const { return Vector3f(M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z + M[0][3], M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z + M[1][3], M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z + M[2][3]); } Matrix4f Transposed() const { return Matrix4f(M[0][0], M[1][0], M[2][0], M[3][0], M[0][1], M[1][1], M[2][1], M[3][1], M[0][2], M[1][2], M[2][2], M[3][2], M[0][3], M[1][3], M[2][3], M[3][3]); } void Transpose() { *this = Transposed(); } float SubDet (const int* rows, const int* cols) const { return M[rows[0]][cols[0]] * (M[rows[1]][cols[1]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[1]]) - M[rows[0]][cols[1]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[0]]) + M[rows[0]][cols[2]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[1]] - M[rows[1]][cols[1]] * M[rows[2]][cols[0]]); } float Cofactor(int I, int J) const { const int indices[4][3] = {{1,2,3},{0,2,3},{0,1,3},{0,1,2}}; return ((I+J)&1) ? -SubDet(indices[I],indices[J]) : SubDet(indices[I],indices[J]); } float Determinant() const { return M[0][0] * Cofactor(0,0) + M[0][1] * Cofactor(0,1) + M[0][2] * Cofactor(0,2) + M[0][3] * Cofactor(0,3); } Matrix4f Adjugated() const { return Matrix4f(Cofactor(0,0), Cofactor(1,0), Cofactor(2,0), Cofactor(3,0), Cofactor(0,1), Cofactor(1,1), Cofactor(2,1), Cofactor(3,1), Cofactor(0,2), Cofactor(1,2), Cofactor(2,2), Cofactor(3,2), Cofactor(0,3), Cofactor(1,3), Cofactor(2,3), Cofactor(3,3)); } Matrix4f Inverted() const { float det = Determinant(); assert(det != 0); return Adjugated() * (1.0f/det); } void Invert() { *this = Inverted(); } //AnnaSteve: // a,b,c, are the YawPitchRoll angles to be returned // rotation a around axis A1 // is followed by rotation b around axis A2 // is followed by rotation c around axis A3 // rotations are CCW or CW (D) in LH or RH coordinate system (S) template void ToEulerAngles(float *a, float *b, float *c) { OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3)); float psign = -1.0f; if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) // Determine whether even permutation psign = 1.0f; float pm = psign*M[A1][A3]; if (pm < -1.0f + Math::SingularityRadius) { // South pole singularity *a = 0.0f; *b = -S*D*Math::PiOver2; *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] ); } else if (pm > 1.0 - Math::SingularityRadius) { // North pole singularity *a = 0.0f; *b = S*D*Math::PiOver2; *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] ); } else { // Normal case (nonsingular) *a = S*D*atan2( -psign*M[A2][A3], M[A3][A3] ); *b = S*D*asin(pm); *c = S*D*atan2( -psign*M[A1][A2], M[A1][A1] ); } return; } //AnnaSteve: // a,b,c, are the YawPitchRoll angles to be returned // rotation a around axis A1 // is followed by rotation b around axis A2 // is followed by rotation c around axis A1 // rotations are CCW or CW (D) in LH or RH coordinate system (S) template void ToEulerAnglesABA(float *a, float *b, float *c) { OVR_COMPILER_ASSERT(A1 != A2); // Determine the axis that was not supplied int m = 3 - A1 - A2; float psign = -1.0f; if ((A1 + 1) % 3 == A2) // Determine whether even permutation psign = 1.0f; float c2 = M[A1][A1]; if (c2 < -1.0 + Math::SingularityRadius) { // South pole singularity *a = 0.0f; *b = S*D*Math::Pi; *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]); } else if (c2 > 1.0 - Math::SingularityRadius) { // North pole singularity *a = 0.0f; *b = 0.0f; *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]); } else { // Normal case (nonsingular) *a = S*D*atan2( M[A2][A1],-psign*M[m][A1]); *b = S*D*acos(c2); *c = S*D*atan2( M[A1][A2],psign*M[A1][m]); } return; } // Creates a matrix that converts the vertices from one coordinate system // to another. // static Matrix4f AxisConversion(const WorldAxes& to, const WorldAxes& from) { // Holds axis values from the 'to' structure int toArray[3] = { to.XAxis, to.YAxis, to.ZAxis }; // The inverse of the toArray int inv[4]; inv[0] = inv[abs(to.XAxis)] = 0; inv[abs(to.YAxis)] = 1; inv[abs(to.ZAxis)] = 2; Matrix4f m(0, 0, 0, 0, 0, 0, 0, 0, 0); // Only three values in the matrix need to be changed to 1 or -1. m.M[inv[abs(from.XAxis)]][0] = float(from.XAxis/toArray[inv[abs(from.XAxis)]]); m.M[inv[abs(from.YAxis)]][1] = float(from.YAxis/toArray[inv[abs(from.YAxis)]]); m.M[inv[abs(from.ZAxis)]][2] = float(from.ZAxis/toArray[inv[abs(from.ZAxis)]]); return m; } static Matrix4f Translation(const Vector3f& v) { Matrix4f t; t.M[0][3] = v.x; t.M[1][3] = v.y; t.M[2][3] = v.z; return t; } static Matrix4f Translation(float x, float y, float z = 0.0f) { Matrix4f t; t.M[0][3] = x; t.M[1][3] = y; t.M[2][3] = z; return t; } static Matrix4f Scaling(const Vector3f& v) { Matrix4f t; t.M[0][0] = v.x; t.M[1][1] = v.y; t.M[2][2] = v.z; return t; } static Matrix4f Scaling(float x, float y, float z) { Matrix4f t; t.M[0][0] = x; t.M[1][1] = y; t.M[2][2] = z; return t; } static Matrix4f Scaling(float s) { Matrix4f t; t.M[0][0] = s; t.M[1][1] = s; t.M[2][2] = s; return t; } //AnnaSteve : Just for quick testing. Not for final API. Need to remove case. static Matrix4f RotationAxis(Axis A, float angle, RotateDirection d, HandedSystem s) { float sina = s * d *sin(angle); float cosa = cos(angle); switch(A) { case Axis_X: return Matrix4f(1, 0, 0, 0, cosa, -sina, 0, sina, cosa); case Axis_Y: return Matrix4f(cosa, 0, sina, 0, 1, 0, -sina, 0, cosa); case Axis_Z: return Matrix4f(cosa, -sina, 0, sina, cosa, 0, 0, 0, 1); } } // Creates a rotation matrix rotating around the X axis by 'angle' radians. // Rotation direction is depends on the coordinate system: // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), // while looking in the negative axis direction. This is the // same as looking down from positive axis values towards origin. // LHS: Positive angle values rotate clock-wise (CW), while looking in the // negative axis direction. static Matrix4f RotationX(float angle) { float sina = sin(angle); float cosa = cos(angle); return Matrix4f(1, 0, 0, 0, cosa, -sina, 0, sina, cosa); } // Creates a rotation matrix rotating around the Y axis by 'angle' radians. // Rotation direction is depends on the coordinate system: // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), // while looking in the negative axis direction. This is the // same as looking down from positive axis values towards origin. // LHS: Positive angle values rotate clock-wise (CW), while looking in the // negative axis direction. static Matrix4f RotationY(float angle) { float sina = sin(angle); float cosa = cos(angle); return Matrix4f(cosa, 0, sina, 0, 1, 0, -sina, 0, cosa); } // Creates a rotation matrix rotating around the Z axis by 'angle' radians. // Rotation direction is depends on the coordinate system: // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), // while looking in the negative axis direction. This is the // same as looking down from positive axis values towards origin. // LHS: Positive angle values rotate clock-wise (CW), while looking in the // negative axis direction. static Matrix4f RotationZ(float angle) { float sina = sin(angle); float cosa = cos(angle); return Matrix4f(cosa, -sina, 0, sina, cosa, 0, 0, 0, 1); } // LookAtRH creates a View transformation matrix for right-handed coordinate system. // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up' // specifying the up vector. The resulting matrix should be used with PerspectiveRH // projection. static Matrix4f LookAtRH(const Vector3f& eye, const Vector3f& at, const Vector3f& up); // LookAtLH creates a View transformation matrix for left-handed coordinate system. // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up' // specifying the up vector. static Matrix4f LookAtLH(const Vector3f& eye, const Vector3f& at, const Vector3f& up); // PerspectiveRH creates a right-handed perspective projection matrix that can be // used with the Oculus sample renderer. // yfov - Specifies vertical field of view in radians. // aspect - Screen aspect ration, which is usually width/height for square pixels. // Note that xfov = yfov * aspect. // znear - Absolute value of near Z clipping clipping range. // zfar - Absolute value of far Z clipping clipping range (larger then near). // Even though RHS usually looks in the direction of negative Z, positive values // are expected for znear and zfar. static Matrix4f PerspectiveRH(float yfov, float aspect, float znear, float zfar); // PerspectiveRH creates a left-handed perspective projection matrix that can be // used with the Oculus sample renderer. // yfov - Specifies vertical field of view in radians. // aspect - Screen aspect ration, which is usually width/height for square pixels. // Note that xfov = yfov * aspect. // znear - Absolute value of near Z clipping clipping range. // zfar - Absolute value of far Z clipping clipping range (larger then near). static Matrix4f PerspectiveLH(float yfov, float aspect, float znear, float zfar); static Matrix4f Ortho2D(float w, float h); }; //------------------------------------------------------------------------------------- // ***** Quat // Quatf represents a quaternion class used for rotations. // // Quaternion multiplications are done in right-to-left order, to match the // behavior of matrices. template class Quat { public: // w + Xi + Yj + Zk T x, y, z, w; Quat() : x(0), y(0), z(0), w(1) {} Quat(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) {} // Constructs rotation quaternion around the axis. Quat(const Vector3& axis, T angle) { Vector3 unitAxis = axis.Normalized(); T sinHalfAngle = sin(angle * T(0.5)); w = cos(angle * T(0.5)); x = unitAxis.x * sinHalfAngle; y = unitAxis.y * sinHalfAngle; z = unitAxis.z * sinHalfAngle; } //AnnaSteve: void AxisAngle(Axis A, T angle, RotateDirection d, HandedSystem s) { T sinHalfAngle = s * d *sin(angle * (T)0.5); T v[3]; v[0] = v[1] = v[2] = (T)0; v[A] = sinHalfAngle; //return Quat(v[0], v[1], v[2], cos(angle * (T)0.5)); w = cos(angle * (T)0.5); x = v[0]; y = v[1]; z = v[2]; } void GetAxisAngle(Vector3* axis, T* angle) const { if (LengthSq() > Math::Tolerance * Math::Tolerance) { *axis = Vector3(x, y, z).Normalized(); *angle = 2 * acos(w); } else { *axis = Vector3(1, 0, 0); *angle= 0; } } bool operator== (const Quat& b) const { return x == b.x && y == b.y && z == b.z && w == b.w; } bool operator!= (const Quat& b) const { return x != b.x || y != b.y || z != b.z || w != b.w; } Quat operator+ (const Quat& b) const { return Quat(x + b.x, y + b.y, z + b.z, w + b.w); } Quat& operator+= (const Quat& b) { w += b.w; x += b.x; y += b.y; z += b.z; return *this; } Quat operator- (const Quat& b) const { return Quat(x - b.x, y - b.y, z - b.z, w - b.w); } Quat& operator-= (const Quat& b) { w -= b.w; x -= b.x; y -= b.y; z -= b.z; return *this; } Quat operator* (T s) const { return Quat(x * s, y * s, z * s, w * s); } Quat& operator*= (T s) { w *= s; x *= s; y *= s; z *= s; return *this; } Quat operator/ (T s) const { T rcp = T(1)/s; return Quat(x * rcp, y * rcp, z * rcp, w *rcp); } Quat& operator/= (T s) { T rcp = T(1)/s; w *= rcp; x *= rcp; y *= rcp; z *= rcp; return *this; } // Get Imaginary part vector Vector3 Imag() const { return Vector3(x,y,z); } // Get quaternion length. T Length() const { return sqrt(x * x + y * y + z * z + w * w); } // Get quaternion length squared. T LengthSq() const { return (x * x + y * y + z * z + w * w); } // Simple Eulidean distance in R^4 (not SLERP distance, but at least respects Haar measure) T Distance(const Quat& q) const { T d1 = (*this - q).Length(); T d2 = (*this + q).Length(); // Antipoldal point check return (d1 < d2) ? d1 : d2; } T DistanceSq(const Quat& q) const { T d1 = (*this - q).LengthSq(); T d2 = (*this + q).LengthSq(); // Antipoldal point check return (d1 < d2) ? d1 : d2; } // Normalize bool IsNormalized() const { return fabs(LengthSq() - 1) < Math::Tolerance; } void Normalize() { *this /= Length(); } Quat Normalized() const { return *this / Length(); } // Returns conjugate of the quaternion. Produces inverse rotation if quaternion is normalized. Quat Conj() const { return Quat(-x, -y, -z, w); } // AnnaSteve fixed: order of quaternion multiplication // Quaternion multiplication. Combines quaternion rotations, performing the one on the // right hand side first. Quat operator* (const Quat& b) const { return Quat(w * b.x + x * b.w + y * b.z - z * b.y, w * b.y - x * b.z + y * b.w + z * b.x, w * b.z + x * b.y - y * b.x + z * b.w, w * b.w - x * b.x - y * b.y - z * b.z); } // // this^p normalized; same as rotating by this p times. Quat PowNormalized(T p) const { Vector3 v; T a; GetAxisAngle(&v, &a); return Quat(v, a * p); } // Rotate transforms vector in a manner that matches Matrix rotations (counter-clockwise, // assuming negative direction of the axis). Standard formula: q(t) * V * q(t)^-1. Vector3 Rotate(const Vector3& v) const { return ((*this * Quat(v.x, v.y, v.z, 0)) * Inverted()).Imag(); } // Inversed quaternion rotates in the opposite direction. Quat Inverted() const { return Quat(-x, -y, -z, w); } // Sets this quaternion to the one rotates in the opposite direction. void Invert() { *this = Quat(-x, -y, -z, w); } // Converting quaternion to matrix. operator Matrix4f() const { T ww = w*w; T xx = x*x; T yy = y*y; T zz = z*z; return Matrix4f(float(ww + xx - yy - zz), float(T(2) * (x*y - w*z)), float(T(2) * (x*z + w*y)), float(T(2) * (x*y + w*z)), float(ww - xx + yy - zz), float(T(2) * (y*z - w*x)), float(T(2) * (x*z - w*y)), float(T(2) * (y*z + w*x)), float(ww - xx - yy + zz) ); } // GetEulerAngles extracts Euler angles from the quaternion, in the specified order of // axis rotations and the specified coordinate system. Right-handed coordinate system // is the default, with CCW rotations while looking in the negative axis direction. // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned. // rotation a around axis A1 // is followed by rotation b around axis A2 // is followed by rotation c around axis A3 // rotations are CCW or CW (D) in LH or RH coordinate system (S) template void GetEulerAngles(T *a, T *b, T *c) { OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3)); T Q[3] = { x, y, z }; //Quaternion components x,y,z T ww = w*w; T Q11 = Q[A1]*Q[A1]; T Q22 = Q[A2]*Q[A2]; T Q33 = Q[A3]*Q[A3]; T psign = T(-1.0); // Determine whether even permutation if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) psign = T(1.0); T s2 = psign * T(2.0) * (psign*w*Q[A2] + Q[A1]*Q[A3]); if (s2 < (T)-1.0 + Math::SingularityRadius) { // South pole singularity *a = T(0.0); *b = -S*D*Math::PiOver2; *c = S*D*atan2((T)2.0*(psign*Q[A1]*Q[A2] + w*Q[A3]), ww + Q22 - Q11 - Q33 ); } else if (s2 > (T)1.0 - Math::SingularityRadius) { // North pole singularity *a = (T)0.0; *b = S*D*Math::PiOver2; *c = S*D*atan2((T)2.0*(psign*Q[A1]*Q[A2] + w*Q[A3]), ww + Q22 - Q11 - Q33); } else { *a = -S*D*atan2((T)-2.0*(w*Q[A1] - psign*Q[A2]*Q[A3]), ww + Q33 - Q11 - Q22); *b = S*D*asin(s2); *c = S*D*atan2((T)2.0*(w*Q[A3] - psign*Q[A1]*Q[A2]), ww + Q11 - Q22 - Q33); } return; } template void GetEulerAngles(T *a, T *b, T *c) { GetEulerAngles(a, b, c); } template void GetEulerAngles(T *a, T *b, T *c) { GetEulerAngles(a, b, c); } // GetEulerAnglesABA extracts Euler angles from the quaternion, in the specified order of // axis rotations and the specified coordinate system. Right-handed coordinate system // is the default, with CCW rotations while looking in the negative axis direction. // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned. // rotation a around axis A1 // is followed by rotation b around axis A2 // is followed by rotation c around axis A1 // Rotations are CCW or CW (D) in LH or RH coordinate system (S) template void GetEulerAnglesABA(T *a, T *b, T *c) { OVR_COMPILER_ASSERT(A1 != A2); T Q[3] = {x, y, z}; // Quaternion components // Determine the missing axis that was not supplied int m = 3 - A1 - A2; T ww = w*w; T Q11 = Q[A1]*Q[A1]; T Q22 = Q[A2]*Q[A2]; T Qmm = Q[m]*Q[m]; T psign = T(-1.0); if ((A1 + 1) % 3 == A2) // Determine whether even permutation { psign = (T)1.0; } T c2 = ww + Q11 - Q22 - Qmm; if (c2 < (T)-1.0 + Math::SingularityRadius) { // South pole singularity *a = (T)0.0; *b = S*D*Math::Pi; *c = S*D*atan2( (T)2.0*(w*Q[A1] - psign*Q[A2]*Q[m]), ww + Q22 - Q11 - Qmm); } else if (c2 > (T)1.0 - Math::SingularityRadius) { // North pole singularity *a = (T)0.0; *b = (T)0.0; *c = S*D*atan2( (T)2.0*(w*Q[A1] - psign*Q[A2]*Q[m]), ww + Q22 - Q11 - Qmm); } else { *a = S*D*atan2( psign*w*Q[m] + Q[A1]*Q[A2], w*Q[A2] -psign*Q[A1]*Q[m]); *b = S*D*acos(c2); *c = S*D*atan2( -psign*w*Q[m] + Q[A1]*Q[A2], w*Q[A2] + psign*Q[A1]*Q[m]); } return; } }; typedef Quat Quatf; typedef Quat Quatd; //------------------------------------------------------------------------------------- // ***** Angle // Cleanly representing the algebra of 2D rotations. // The operations maintain the angle between -Pi and Pi, the same range as atan2. // template class Angle { public: enum AngularUnits { Radians = 0, Degrees = 1 }; Angle() : a(0) {} // Fix the range to be between -Pi and Pi Angle(T a_, AngularUnits u = Radians) : a((u == Radians) ? a_ : a_*Math::DegreeToRadFactor) { FixRange(); } T Get(AngularUnits u = Radians) const { return (u == Radians) ? a : a*Math::RadToDegreeFactor; } void Set(const T& x, AngularUnits u = Radians) { a = (u == Radians) ? x : x*Math::DegreeToRadFactor; FixRange(); } int Sign() const { if (a == 0) return 0; else return (a > 0) ? 1 : -1; } T Abs() const { return (a > 0) ? a : -a; } bool operator== (const Angle& b) const { return a == b.a; } bool operator!= (const Angle& b) const { return a != b.a; } // bool operator< (const Angle& b) const { return a < a.b; } // bool operator> (const Angle& b) const { return a > a.b; } // bool operator<= (const Angle& b) const { return a <= a.b; } // bool operator>= (const Angle& b) const { return a >= a.b; } // bool operator= (const T& x) { a = x; FixRange(); } // These operations assume a is already between -Pi and Pi. Angle operator+ (const Angle& b) const { return Angle(a + b.a); } Angle operator+ (const T& x) const { return Angle(a + x); } Angle& operator+= (const Angle& b) { a = a + b.a; FastFixRange(); return *this; } Angle& operator+= (const T& x) { a = a + x; FixRange(); return *this; } Angle operator- (const Angle& b) const { return Angle(a - b.a); } Angle operator- (const T& x) const { return Angle(a - x); } Angle& operator-= (const Angle& b) { a = a - b.a; FastFixRange(); return *this; } Angle& operator-= (const T& x) { a = a - x; FixRange(); return *this; } T Distance(const Angle& b) { T c = fabs(a - b.a); return (c <= Math::Pi) ? c : Math::TwoPi - c; } private: // The stored angle, which should be maintained between -Pi and Pi T a; // Fixes the angle range to [-Pi,Pi], but assumes no more than 2Pi away on either side inline void FastFixRange() { if (a < -Math::Pi) a += Math::TwoPi; else if (a > Math::Pi) a -= Math::TwoPi; } // Fixes the angle range to [-Pi,Pi] for any given range, but slower then the fast method inline void FixRange() { a = fmod(a,Math::TwoPi); if (a < -Math::Pi) a += Math::TwoPi; else if (a > Math::Pi) a -= Math::TwoPi; } }; typedef Angle Anglef; typedef Angle Angled; //------------------------------------------------------------------------------------- // ***** Plane // Consists of a normal vector and distance from the origin where the plane is located. template class Plane : public RefCountBase > { public: Vector3 N; T D; Plane() : D(0) {} // Normals must already be normalized Plane(const Vector3& n, T d) : N(n), D(d) {} Plane(T x, T y, T z, T d) : N(x,y,z), D(d) {} // construct from a point on the plane and the normal Plane(const Vector3& p, const Vector3& n) : N(n), D(-(p * n)) {} // Find the point to plane distance. The sign indicates what side of the plane the point is on (0 = point on plane). T TestSide(const Vector3& p) const { return (N * p) + D; } Plane Flipped() const { return Plane(-N, -D); } void Flip() { N = -N; D = -D; } bool operator==(const Plane& rhs) const { return (this->D == rhs.D && this->N == rhs.N); } }; typedef Plane Planef; } #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_RefCount.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_RefCount.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : OVR_RefCount.cpp Content : Reference counting implementation Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_RefCount.h" #include "OVR_Atomic.h" #include "OVR_Log.h" namespace OVR { #ifdef OVR_CC_ARM void* ReturnArg0(void* p) { return p; } #endif // ***** Reference Count Base implementation RefCountImplCore::~RefCountImplCore() { // RefCount can be either 1 or 0 here. // 0 if Release() was properly called. // 1 if the object was declared on stack or as an aggregate. OVR_ASSERT(RefCount <= 1); } #ifdef OVR_BUILD_DEBUG void RefCountImplCore::reportInvalidDelete(void *pmem) { OVR_DEBUG_LOG( ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem)); OVR_ASSERT(0); } #endif RefCountNTSImplCore::~RefCountNTSImplCore() { // RefCount can be either 1 or 0 here. // 0 if Release() was properly called. // 1 if the object was declared on stack or as an aggregate. OVR_ASSERT(RefCount <= 1); } #ifdef OVR_BUILD_DEBUG void RefCountNTSImplCore::reportInvalidDelete(void *pmem) { OVR_DEBUG_LOG( ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem)); OVR_ASSERT(0); } #endif // *** Thread-Safe RefCountImpl void RefCountImpl::AddRef() { AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); } void RefCountImpl::Release() { if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) delete this; } // *** Thread-Safe RefCountVImpl w/virtual AddRef/Release void RefCountVImpl::AddRef() { AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); } void RefCountVImpl::Release() { if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) delete this; } // *** NON-Thread-Safe RefCountImpl void RefCountNTSImpl::Release() const { RefCount--; if (RefCount == 0) delete this; } } // OVR \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_RefCount.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_RefCount.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: Kernel Filename : OVR_RefCount.h Content : Reference counting implementation headers Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_RefCount_h #define OVR_RefCount_h #include "OVR_Types.h" #include "OVR_Allocator.h" namespace OVR { //----------------------------------------------------------------------------------- // ***** Reference Counting // There are three types of reference counting base classes: // // RefCountBase - Provides thread-safe reference counting (Default). // RefCountBaseNTS - Non Thread Safe version of reference counting. // ***** Declared classes template class RefCountBase; template class RefCountBaseNTS; class RefCountImpl; class RefCountNTSImpl; //----------------------------------------------------------------------------------- // ***** Implementation For Reference Counting // RefCountImplCore holds RefCount value and defines a few utility // functions shared by all implementations. class RefCountImplCore { protected: volatile int RefCount; public: // RefCountImpl constructor always initializes RefCount to 1 by default. OVR_FORCE_INLINE RefCountImplCore() : RefCount(1) { } // Need virtual destructor // This: 1. Makes sure the right destructor's called. // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem() virtual ~RefCountImplCore(); // Debug method only. int GetRefCount() const { return RefCount; } // This logic is used to detect invalid 'delete' calls of reference counted // objects. Direct delete calls are not allowed on them unless they come in // internally from Release. #ifdef OVR_BUILD_DEBUG static void OVR_CDECL reportInvalidDelete(void *pmem); inline static void checkInvalidDelete(RefCountImplCore *pmem) { if (pmem->RefCount != 0) reportInvalidDelete(pmem); } #else inline static void checkInvalidDelete(RefCountImplCore *) { } #endif // Base class ref-count content should not be copied. void operator = (const RefCountImplCore &) { } }; class RefCountNTSImplCore { protected: mutable int RefCount; public: // RefCountImpl constructor always initializes RefCount to 1 by default. OVR_FORCE_INLINE RefCountNTSImplCore() : RefCount(1) { } // Need virtual destructor // This: 1. Makes sure the right destructor's called. // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem() virtual ~RefCountNTSImplCore(); // Debug method only. int GetRefCount() const { return RefCount; } // This logic is used to detect invalid 'delete' calls of reference counted // objects. Direct delete calls are not allowed on them unless they come in // internally from Release. #ifdef OVR_BUILD_DEBUG static void OVR_CDECL reportInvalidDelete(void *pmem); OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *pmem) { if (pmem->RefCount != 0) reportInvalidDelete(pmem); } #else OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *) { } #endif // Base class ref-count content should not be copied. void operator = (const RefCountNTSImplCore &) { } }; // RefCountImpl provides Thread-Safe implementation of reference counting, so // it should be used by default in most places. class RefCountImpl : public RefCountImplCore { public: // Thread-Safe Ref-Count Implementation. void AddRef(); void Release(); }; // RefCountVImpl provides Thread-Safe implementation of reference counting, plus, // virtual AddRef and Release. class RefCountVImpl : public RefCountImplCore { public: // Thread-Safe Ref-Count Implementation. virtual void AddRef(); virtual void Release(); }; // RefCountImplNTS provides Non-Thread-Safe implementation of reference counting, // which is slightly more efficient since it doesn't use atomics. class RefCountNTSImpl : public RefCountNTSImplCore { public: OVR_FORCE_INLINE void AddRef() const { RefCount++; } void Release() const; }; // RefCountBaseStatImpl<> is a common class that adds new/delete override with Stat tracking // to the reference counting implementation. Base must be one of the RefCountImpl classes. template class RefCountBaseStatImpl : public Base { public: RefCountBaseStatImpl() { } // *** Override New and Delete // DOM-IGNORE-BEGIN // Undef new temporarily if it is being redefined #ifdef OVR_DEFINE_NEW #undef new #endif #ifdef OVR_BUILD_DEBUG // Custom check used to detect incorrect calls of 'delete' on ref-counted objects. #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) \ do {if (p) Base::checkInvalidDelete((class_name*)p); } while(0) #else #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) #endif // Redefine all new & delete operators. OVR_MEMORY_REDEFINE_NEW_IMPL(Base, OVR_REFCOUNTALLOC_CHECK_DELETE) #ifdef OVR_DEFINE_NEW #define new OVR_DEFINE_NEW #endif // OVR_BUILD_DEFINE_NEW // DOM-IGNORE-END }; //----------------------------------------------------------------------------------- // *** End user RefCountBase<> classes // RefCountBase is a base class for classes that require thread-safe reference // counting; it also overrides the new and delete operators to use MemoryHeap. // // Reference counted objects start out with RefCount value of 1. Further lifetime // management is done through the AddRef() and Release() methods, typically // hidden by Ptr<>. template class RefCountBase : public RefCountBaseStatImpl { public: // Constructor. OVR_FORCE_INLINE RefCountBase() : RefCountBaseStatImpl() { } }; // RefCountBaseV is the same as RefCountBase but with virtual AddRef/Release template class RefCountBaseV : public RefCountBaseStatImpl { public: // Constructor. OVR_FORCE_INLINE RefCountBaseV() : RefCountBaseStatImpl() { } }; // RefCountBaseNTS is a base class for classes that require Non-Thread-Safe reference // counting; it also overrides the new and delete operators to use MemoryHeap. // This class should only be used if all pointers to it are known to be assigned, // destroyed and manipulated within one thread. // // Reference counted objects start out with RefCount value of 1. Further lifetime // management is done through the AddRef() and Release() methods, typically // hidden by Ptr<>. template class RefCountBaseNTS : public RefCountBaseStatImpl { public: // Constructor. OVR_FORCE_INLINE RefCountBaseNTS() : RefCountBaseStatImpl() { } }; //----------------------------------------------------------------------------------- // ***** Pickable template pointer enum PickType { PickValue }; template class Pickable { public: Pickable() : pV(NULL) {} explicit Pickable(T* p) : pV(p) {} Pickable(T* p, PickType) : pV(p) { OVR_ASSERT(pV); if (pV) pV->AddRef(); } template Pickable(const Pickable& other) : pV(other.GetPtr()) {} public: Pickable& operator =(const Pickable& other) { OVR_ASSERT(pV == NULL); pV = other.pV; // Extra check. //other.pV = NULL; return *this; } public: T* GetPtr() const { return pV; } T* operator->() const { return pV; } T& operator*() const { OVR_ASSERT(pV); return *pV; } private: T* pV; }; template OVR_FORCE_INLINE Pickable MakePickable(T* p) { return Pickable(p); } //----------------------------------------------------------------------------------- // ***** Ref-Counted template pointer // Automatically AddRefs and Releases interfaces void* ReturnArg0(void* p); template class Ptr { #ifdef OVR_CC_ARM static C* ReturnArg(void* p) { return (C*)ReturnArg0(p); } #endif protected: C *pObject; public: // Constructors OVR_FORCE_INLINE Ptr() : pObject(0) { } #ifdef OVR_CC_ARM OVR_FORCE_INLINE Ptr(C &robj) : pObject(ReturnArg(&robj)) #else OVR_FORCE_INLINE Ptr(C &robj) : pObject(&robj) #endif { } OVR_FORCE_INLINE Ptr(Pickable v) : pObject(v.GetPtr()) { // No AddRef() on purpose. } OVR_FORCE_INLINE Ptr(Ptr& other, PickType) : pObject(other.pObject) { other.pObject = NULL; // No AddRef() on purpose. } OVR_FORCE_INLINE Ptr(C *pobj) { if (pobj) pobj->AddRef(); pObject = pobj; } OVR_FORCE_INLINE Ptr(const Ptr &src) { if (src.pObject) src.pObject->AddRef(); pObject = src.pObject; } template OVR_FORCE_INLINE Ptr(Ptr &src) { if (src) src->AddRef(); pObject = src; } template OVR_FORCE_INLINE Ptr(Pickable v) : pObject(v.GetPtr()) { // No AddRef() on purpose. } // Destructor OVR_FORCE_INLINE ~Ptr() { if (pObject) pObject->Release(); } // Compares OVR_FORCE_INLINE bool operator == (const Ptr &other) const { return pObject == other.pObject; } OVR_FORCE_INLINE bool operator != (const Ptr &other) const { return pObject != other.pObject; } OVR_FORCE_INLINE bool operator == (C *pother) const { return pObject == pother; } OVR_FORCE_INLINE bool operator != (C *pother) const { return pObject != pother; } OVR_FORCE_INLINE bool operator < (const Ptr &other) const { return pObject < other.pObject; } // Assignment template OVR_FORCE_INLINE const Ptr& operator = (const Ptr &src) { if (src) src->AddRef(); if (pObject) pObject->Release(); pObject = src; return *this; } // Specialization OVR_FORCE_INLINE const Ptr& operator = (const Ptr &src) { if (src) src->AddRef(); if (pObject) pObject->Release(); pObject = src; return *this; } OVR_FORCE_INLINE const Ptr& operator = (C *psrc) { if (psrc) psrc->AddRef(); if (pObject) pObject->Release(); pObject = psrc; return *this; } OVR_FORCE_INLINE const Ptr& operator = (C &src) { if (pObject) pObject->Release(); pObject = &src; return *this; } OVR_FORCE_INLINE Ptr& operator = (Pickable src) { return Pick(src); } template OVR_FORCE_INLINE Ptr& operator = (Pickable src) { return Pick(src); } // Set Assignment template OVR_FORCE_INLINE Ptr& SetPtr(const Ptr &src) { if (src) src->AddRef(); if (pObject) pObject->Release(); pObject = src; return *this; } // Specialization OVR_FORCE_INLINE Ptr& SetPtr(const Ptr &src) { if (src) src->AddRef(); if (pObject) pObject->Release(); pObject = src; return *this; } OVR_FORCE_INLINE Ptr& SetPtr(C *psrc) { if (psrc) psrc->AddRef(); if (pObject) pObject->Release(); pObject = psrc; return *this; } OVR_FORCE_INLINE Ptr& SetPtr(C &src) { if (pObject) pObject->Release(); pObject = &src; return *this; } OVR_FORCE_INLINE Ptr& SetPtr(Pickable src) { return Pick(src); } // Nulls ref-counted pointer without decrement OVR_FORCE_INLINE void NullWithoutRelease() { pObject = 0; } // Clears the pointer to the object OVR_FORCE_INLINE void Clear() { if (pObject) pObject->Release(); pObject = 0; } // Obtain pointer reference directly, for D3D interfaces OVR_FORCE_INLINE C*& GetRawRef() { return pObject; } // Access Operators OVR_FORCE_INLINE C* GetPtr() const { return pObject; } OVR_FORCE_INLINE C& operator * () const { return *pObject; } OVR_FORCE_INLINE C* operator -> () const { return pObject; } // Conversion OVR_FORCE_INLINE operator C* () const { return pObject; } // Pickers. // Pick a value. OVR_FORCE_INLINE Ptr& Pick(Ptr& other) { if (&other != this) { if (pObject) pObject->Release(); pObject = other.pObject; other.pObject = 0; } return *this; } OVR_FORCE_INLINE Ptr& Pick(Pickable v) { if (v.GetPtr() != pObject) { if (pObject) pObject->Release(); pObject = v.GetPtr(); } return *this; } template OVR_FORCE_INLINE Ptr& Pick(Pickable v) { if (v.GetPtr() != pObject) { if (pObject) pObject->Release(); pObject = v.GetPtr(); } return *this; } OVR_FORCE_INLINE Ptr& Pick(C* p) { if (p != pObject) { if (pObject) pObject->Release(); pObject = p; } return *this; } }; } // OVR #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Std.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Std.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : OVR_Std.cpp Content : Standard C function implementation Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_Std.h" #include "OVR_Alg.h" // localeconv() call in OVR_strtod() #include namespace OVR { // Source for functions not available on all platforms is included here. // Case insensitive compare implemented in platform-specific way. int OVR_CDECL OVR_stricmp(const char* a, const char* b) { #if defined(OVR_OS_WIN32) #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) return ::_stricmp(a, b); #else return ::stricmp(a, b); #endif #else return strcasecmp(a, b); #endif } int OVR_CDECL OVR_strnicmp(const char* a, const char* b, UPInt count) { #if defined(OVR_OS_WIN32) #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) return ::_strnicmp(a, b, count); #else return ::strnicmp(a, b, count); #endif #else return strncasecmp(a, b, count); #endif } wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src) { #if defined(OVR_MSVC_SAFESTRING) wcscpy_s(dest, destsize, src); return dest; #elif defined(OVR_OS_WIN32) OVR_UNUSED(destsize); wcscpy(dest, src); return dest; #else UPInt l = OVR_wcslen(src) + 1; // incl term null l = (l < destsize) ? l : destsize; memcpy(dest, src, l * sizeof(wchar_t)); return dest; #endif } wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count) { #if defined(OVR_MSVC_SAFESTRING) wcsncpy_s(dest, destsize, src, count); return dest; #else UPInt srclen = OVR_wcslen(src); UPInt l = Alg::Min(srclen, count); l = (l < destsize) ? l : destsize; memcpy(dest, src, l * sizeof(wchar_t)); if (count > srclen) { UPInt remLen = Alg::Min(destsize - l, (count - srclen)); memset(&dest[l], 0, sizeof(wchar_t)*remLen); } else if (l < destsize) dest[l] = 0; return dest; #endif } wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src) { #if defined(OVR_MSVC_SAFESTRING) wcscat_s(dest, destsize, src); return dest; #elif defined(OVR_OS_WIN32) OVR_UNUSED(destsize); wcscat(dest, src); return dest; #else UPInt dstlen = OVR_wcslen(dest); // do not incl term null UPInt srclen = OVR_wcslen(src) + 1; // incl term null UPInt copylen = (dstlen + srclen < destsize) ? srclen : destsize - dstlen; memcpy(dest + dstlen, src, copylen * sizeof(wchar_t)); return dest; #endif } UPInt OVR_CDECL OVR_wcslen(const wchar_t* str) { #if defined(OVR_OS_WIN32) return wcslen(str); #else UPInt i = 0; while(str[i] != '\0') ++i; return i; #endif } int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b) { #if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX) return wcscmp(a, b); #else // not supported, use custom implementation const wchar_t *pa = a, *pb = b; while (*pa && *pb) { wchar_t ca = *pa; wchar_t cb = *pb; if (ca < cb) return -1; else if (ca > cb) return 1; pa++; pb++; } if (*pa) return 1; else if (*pb) return -1; else return 0; #endif } int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b) { #if defined(OVR_OS_WIN32) #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) return ::_wcsicmp(a, b); #else return ::wcsicmp(a, b); #endif #elif defined(OVR_OS_MAC) || defined(__CYGWIN__) || defined(OVR_OS_ANDROID) || defined(OVR_OS_IPHONE) // not supported, use custom implementation const wchar_t *pa = a, *pb = b; while (*pa && *pb) { wchar_t ca = OVR_towlower(*pa); wchar_t cb = OVR_towlower(*pb); if (ca < cb) return -1; else if (ca > cb) return 1; pa++; pb++; } if (*pa) return 1; else if (*pb) return -1; else return 0; #else return wcscasecmp(a, b); #endif } // This function is not inline because of dependency on double OVR_CDECL OVR_strtod(const char* string, char** tailptr) { #if !defined(OVR_OS_ANDROID) const char s = *localeconv()->decimal_point; if (s != '.') { char buffer[347 + 1]; OVR_strcpy(buffer, sizeof(buffer), string); for (char* c = buffer; *c != '\0'; ++c) { if (*c == '.') { *c = s; break; } } return strtod(buffer, tailptr); } #endif return strtod(string, tailptr); } #ifndef OVR_NO_WCTYPE //// Use this class to generate Unicode bitsets. For example: //// //// UnicodeBitSet bitSet; //// for(unsigned i = 0; i < 65536; ++i) //// { //// if (iswalpha(i)) //// bitSet.Set(i); //// } //// bitSet.Dump(); //// ////--------------------------------------------------------------- //class UnicodeBitSet //{ //public: // UnicodeBitSet() // { // memset(Offsets, 0, sizeof(Offsets)); // memset(Bits, 0, sizeof(Bits)); // } // // void Set(unsigned bit) { Bits[bit >> 8][(bit >> 4) & 15] |= 1 << (bit & 15); } // // void Dump() // { // unsigned i, j; // unsigned offsetCount = 0; // for(i = 0; i < 256; ++i) // { // if (isNull(i)) Offsets[i] = 0; // else // if (isFull(i)) Offsets[i] = 1; // else Offsets[i] = UInt16(offsetCount++ * 16 + 256); // } // for(i = 0; i < 16; ++i) // { // for(j = 0; j < 16; ++j) // { // printf("%5u,", Offsets[i*16+j]); // } // printf("\n"); // } // for(i = 0; i < 256; ++i) // { // if (Offsets[i] > 255) // { // for(j = 0; j < 16; j++) // { // printf("%5u,", Bits[i][j]); // } // printf("\n"); // } // } // } // //private: // bool isNull(unsigned n) const // { // const UInt16* p = Bits[n]; // for(unsigned i = 0; i < 16; ++i) // if (p[i] != 0) return false; // return true; // } // // bool isFull(unsigned n) const // { // const UInt16* p = Bits[n]; // for(unsigned i = 0; i < 16; ++i) // if (p[i] != 0xFFFF) return false; // return true; // } // // UInt16 Offsets[256]; // UInt16 Bits[256][16]; //}; const UInt16 UnicodeAlnumBits[] = { 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832, 0, 0, 0, 1023,65534, 2047,65534, 2047, 0, 0, 0, 524,65535,65407,65535,65407, 65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15, 65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7, 0, 0,65534, 2047,65534, 63, 1023,65535,65535,65535,65535,65535,65535, 8175, 8702, 8191, 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, 65518,65535,65535,58367, 8191,65281,65487, 0,40942,65529,65023,50117, 6559,45184,65487, 3, 34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, 40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, 57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, 57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 12, 65534,65535,65535, 2047,32767, 1023, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, 1, 0, 1023, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0, 65535,65535,63227, 327, 1023, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127, 65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, 65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, 32767,32573,65535,65535,65407, 2047,65024, 3, 0, 0,65535,65535,65535,65535,65535, 31, 65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 1023, 0, 0, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, 65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, 64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047, 65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, 65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, 65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 1023,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; const UInt16 UnicodeAlphaBits[] = { 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832, 0, 0, 0, 0,65534, 2047,65534, 2047, 0, 0, 0, 0,65535,65407,65535,65407, 65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15, 65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7, 0, 0,65534, 2047,65534, 63, 0,65535,65535,65535,65535,65535,65535, 8175, 8702, 7168, 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, 65518,65535,65535,58367, 8191,65281, 15, 0,40942,65529,65023,50117, 6559,45184, 15, 3, 34788,65529,65023,50029, 6535,24064, 0, 31,45038,65531,65023,58349, 7103, 1, 1, 0, 40942,65529,65023,58317, 6543,45248, 3, 0,51180,54845,50968,50111, 7623, 128, 0, 0, 57326,65533,65023,50159, 7647, 96, 3, 0,57324,65533,65023,50159, 7647,16480, 3, 0, 57324,65533,65023,50175, 7631, 128, 3, 0,65516,64639,65535,12283,32895,65375, 0, 12, 65534,65535,65535, 2047,32767, 0, 0, 0, 9622,65264,60590,15359, 8223,12288, 0, 0, 1, 0, 0, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0, 65535,65535,63227, 327, 0, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127, 65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, 65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, 32767,32573,65535,65535,65407, 2047, 0, 0, 0, 0,65535,65535,65535,65535,65535, 31, 65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 0, 0, 0, 0,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, 65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, 64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047, 65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, 65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, 65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; const UInt16 UnicodeDigitBits[] = { 256, 0, 0, 0, 0, 0, 272, 0, 0, 288, 304, 320, 336, 352, 368, 384, 400, 0, 0, 416, 0, 0, 0, 432, 448, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 464, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 524, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65408, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65024, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; const UInt16 UnicodeSpaceBits[] = { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0, 288, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15872, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4095, 0,33536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; const UInt16 UnicodeXDigitBits[] = { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Uncomment if necessary //const UInt16 UnicodeCntrlBits[] = { // 256, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 288, 0, 0, 0, 0, 0, 0, 0, // 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 320, 336, //65535,65535, 0, 0, 0, 0, 0,32768,65535,65535, 0, 0, 0, 0, 0, 0, //32768, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //30720, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //61440, 0,31744, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,32768, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3584}; // //const UInt16 UnicodeGraphBits[] = { // 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, // 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, // 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736, // 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848, // 0, 0,65534,65535,65535,65535,65535,32767, 0, 0,65534,65535,65535,65535,65535,65535, //65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, // 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15, //65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, // 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31, // 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191, //16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, //65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3, //34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, //40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, //57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, //57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28, //65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, //65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0, //65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175, //65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, //65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, //32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31, //65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, //65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0, // 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0, // 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, //65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, // 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0, //64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //65486,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095, //65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, //65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, //65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, // 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535, 8191, //63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; // //const UInt16 UnicodePrintBits[] = { // 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, // 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, // 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736, // 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848, // 512, 0,65535,65535,65535,65535,65535,32767, 0, 0,65535,65535,65535,65535,65535,65535, //65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, // 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15, //65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, // 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31, // 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191, //16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, //65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3, //34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, //40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, //57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, //57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28, //65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, //65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0, //65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175, //65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, //65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, //32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31, //65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, //65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0, // 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0, // 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, //65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, // 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0, //64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //65487,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095, //65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, //65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, //65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, // 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535,40959, //63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; // //const UInt16 UnicodePunctBits[] = { // 256, 0, 0, 272, 0, 288, 304, 320, 0, 336, 0, 0, 0, 352, 368, 384, // 400, 0, 0, 416, 0, 0, 432, 448, 464, 0, 0, 0, 0, 0, 0, 0, // 480, 0, 0, 496, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 528, 544, 560, // 0, 0,65534,64512, 1,63488, 1,30720, 0, 0,65534,65535, 0, 128, 0, 128, // 0, 0, 0, 0, 0, 0, 0,16384, 128, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0,64512, 0, 0, 1536, 0, 0,16384, 9, 0, 0, 24, // 4096,34816, 0, 0, 0, 0,15360, 0, 0, 0, 0, 0, 0, 16, 0, 0, //16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, // 0, 0, 0, 0,32768, 3072, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //65520, 7, 0,15360, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048, // 0, 0, 0, 0, 0, 0, 510, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0,24576, 0, 0, 6144, 0, 0, 0, 0,14336, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6128, 0, 0, // 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0,65535, 255,65535,16239, 0, 0,24576,24576, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //65294,65523, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048, // 0, 0, 0,49152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0,65535,65055,65527, 3339, 0, 0, 0, 0, 0, 0, 0, 0, 0, //63470,35840, 1,47104, 0,10240, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // //const UInt16 UnicodeLowerBits[] = { // 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368, // 384, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, 432, // 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0,32768,65535,65407, //43690,43690,43690,21930,43861,43690,43690,54442,12585,20004,11562,58961,23392,46421,43690,43565, //43690,43690,43688, 10, 0,65535,65535,65535,65535,65535,16383, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,61440,65535,32767,43235,43690, 15, // 0, 0, 0,65535,65535,65535,43690,43690,40962,43690,43690,43690, 4372,43690,43690, 554, // 0, 0, 0, 0, 0, 0,65534,65535, 255, 0, 0, 0, 0, 0, 0, 0, //43690,43690,43690,43690,43690,43690,43690,43690,43690, 4074,43690,43690,43690,43690,43690, 682, // 255, 63, 255, 255, 63, 255, 255,16383,65535,65535,65535,20703, 4316, 207, 255, 4316, // 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, //50176, 8,32768, 528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 127, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // //const UInt16 UnicodeUpperBits[] = { // 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384, // 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, // 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0, //21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53973, 4526,44464,19114,21845,21974, //21845,21845,21844, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0,21532,21845, 0, //65535,65535,65535, 0, 0, 0,21845,21845,20481,21845,21845,21845, 2187,21845,21845, 277, // 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0, //21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341, //65280,16128,65280,65280,16128,43520,65280, 0,65280,65280,65280, 7936, 7936, 3840, 7936, 7936, //14468,15911,15696, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // MA: March 19, 2010 // Modified ToUpper and ToLower tables to match values expected by AS3 tests. // ToLower modifications: // 304 -> 105 // 1024 -> 1104 * // 1037 -> 1117 * // UoUpper modifications: // 255 -> 376 // 305 -> 73 // 383 -> 83 // 1104 -> 1024 * // 1117 -> 1037 * // Entries marked with a '*' don't make complete sense based on Unicode manual, although // they match AS3. static const UInt16 UnicodeToUpperBits[] = { 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368, 0, 384, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,65407, 43690,43690,43690,21674,43349,43690,43690,54442, 4392, 516, 8490, 8785,21056,46421,43690,43048, // MA: Modified for AS3. 43690, 170, 0, 0, 0, 2776,33545, 36, 3336, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,61440,65534,32767, 0,43688, 0, 0, 0, 0,65535,65535,65535,43690,43690, 2,43690,43690,43690, 4372,43690,35498, 554, // MA: Modified for AS3. 0, 0, 0, 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 43690,43690,43690,43690,43690,43690,43690,43690,43690, 42,43690,43690,43690,43690,43690, 682, 255, 63, 255, 255, 63, 170, 255,16383, 0, 0, 0, 3, 0, 3, 35, 0, 0, 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535, 1023, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; static const UInt16 UnicodeToLowerBits[] = { 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384, 0, 400, 0, 0, 416, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 432, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0, 21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53909, 4526,42128,19114,21845,21522,// MA: Modidied for AS3. 21845, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0, 0,21844, 0, 65535,65535,65535, 0, 0, 0,21845,21845, 1,21845,21845,21845, 2186,21845,17749, 277, 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0, 21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341, 65280,16128,65280,65280,16128,43520,65280, 0, 0, 0, 0, 3840, 3840, 3840, 7936, 3840, 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65472,65535, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; struct GUnicodePairType { UInt16 Key, Value; }; static inline bool CmpUnicodeKey(const GUnicodePairType& a, UInt16 key) { return a.Key < key; } static const GUnicodePairType UnicodeToUpperTable[] = { { 97, 65}, { 98, 66}, { 99, 67}, { 100, 68}, { 101, 69}, { 102, 70}, { 103, 71}, { 104, 72}, { 105, 73}, { 106, 74}, { 107, 75}, { 108, 76}, { 109, 77}, { 110, 78}, { 111, 79}, { 112, 80}, { 113, 81}, { 114, 82}, { 115, 83}, { 116, 84}, { 117, 85}, { 118, 86}, { 119, 87}, { 120, 88}, { 121, 89}, { 122, 90}, { 224, 192}, { 225, 193}, { 226, 194}, { 227, 195}, { 228, 196}, { 229, 197}, { 230, 198}, { 231, 199}, { 232, 200}, { 233, 201}, { 234, 202}, { 235, 203}, { 236, 204}, { 237, 205}, { 238, 206}, { 239, 207}, { 240, 208}, { 241, 209}, { 242, 210}, { 243, 211}, { 244, 212}, { 245, 213}, { 246, 214}, { 248, 216}, { 249, 217}, { 250, 218}, { 251, 219}, { 252, 220}, { 253, 221}, { 254, 222}, { 255, 376}, { 257, 256}, { 259, 258}, { 261, 260}, { 263, 262}, { 265, 264}, { 267, 266}, { 269, 268}, { 271, 270}, { 273, 272}, { 275, 274}, { 277, 276}, { 279, 278}, { 281, 280}, { 283, 282}, { 285, 284}, { 287, 286}, { 289, 288}, { 291, 290}, { 293, 292}, { 295, 294}, { 297, 296}, { 299, 298}, { 301, 300}, { 303, 302}, { 305, 73}, { 307, 306}, { 309, 308}, { 311, 310}, { 314, 313}, { 316, 315}, { 318, 317}, { 320, 319}, { 322, 321}, { 324, 323}, { 326, 325}, { 328, 327}, { 331, 330}, { 333, 332}, { 335, 334}, { 337, 336}, { 339, 338}, { 341, 340}, { 343, 342}, { 345, 344}, { 347, 346}, { 349, 348}, { 351, 350}, { 353, 352}, { 355, 354}, { 357, 356}, { 359, 358}, { 361, 360}, { 363, 362}, { 365, 364}, { 367, 366}, { 369, 368}, { 371, 370}, { 373, 372}, { 375, 374}, { 378, 377}, { 380, 379}, { 382, 381}, { 383, 83}, { 387, 386}, { 389, 388}, { 392, 391}, { 396, 395}, { 402, 401}, { 409, 408}, { 417, 416}, { 419, 418}, { 421, 420}, { 424, 423}, { 429, 428}, { 432, 431}, { 436, 435}, { 438, 437}, { 441, 440}, { 445, 444}, { 454, 452}, { 457, 455}, { 460, 458}, { 462, 461}, { 464, 463}, { 466, 465}, { 468, 467}, { 470, 469}, { 472, 471}, { 474, 473}, { 476, 475}, { 477, 398}, { 479, 478}, { 481, 480}, { 483, 482}, { 485, 484}, { 487, 486}, { 489, 488}, { 491, 490}, { 493, 492}, { 495, 494}, { 499, 497}, { 501, 500}, { 507, 506}, { 509, 508}, { 511, 510}, { 513, 512}, { 515, 514}, { 517, 516}, { 519, 518}, { 521, 520}, { 523, 522}, { 525, 524}, { 527, 526}, { 529, 528}, { 531, 530}, { 533, 532}, { 535, 534}, { 595, 385}, { 596, 390}, { 598, 393}, { 599, 394}, { 601, 399}, { 603, 400}, { 608, 403}, { 611, 404}, { 616, 407}, { 617, 406}, { 623, 412}, { 626, 413}, { 629, 415}, { 643, 425}, { 648, 430}, { 650, 433}, { 651, 434}, { 658, 439}, { 940, 902}, { 941, 904}, { 942, 905}, { 943, 906}, { 945, 913}, { 946, 914}, { 947, 915}, { 948, 916}, { 949, 917}, { 950, 918}, { 951, 919}, { 952, 920}, { 953, 921}, { 954, 922}, { 955, 923}, { 956, 924}, { 957, 925}, { 958, 926}, { 959, 927}, { 960, 928}, { 961, 929}, { 962, 931}, { 963, 931}, { 964, 932}, { 965, 933}, { 966, 934}, { 967, 935}, { 968, 936}, { 969, 937}, { 970, 938}, { 971, 939}, { 972, 908}, { 973, 910}, { 974, 911}, { 995, 994}, { 997, 996}, { 999, 998}, { 1001, 1000}, { 1003, 1002}, { 1005, 1004}, { 1007, 1006}, { 1072, 1040}, { 1073, 1041}, { 1074, 1042}, { 1075, 1043}, { 1076, 1044}, { 1077, 1045}, { 1078, 1046}, { 1079, 1047}, { 1080, 1048}, { 1081, 1049}, { 1082, 1050}, { 1083, 1051}, { 1084, 1052}, { 1085, 1053}, { 1086, 1054}, { 1087, 1055}, { 1088, 1056}, { 1089, 1057}, { 1090, 1058}, { 1091, 1059}, { 1092, 1060}, { 1093, 1061}, { 1094, 1062}, { 1095, 1063}, { 1096, 1064}, { 1097, 1065}, { 1098, 1066}, { 1099, 1067}, { 1100, 1068}, { 1101, 1069}, { 1102, 1070}, { 1103, 1071}, { 1104, 1024}, { 1105, 1025}, { 1106, 1026}, { 1107, 1027}, { 1108, 1028}, { 1109, 1029}, { 1110, 1030}, { 1111, 1031}, { 1112, 1032}, { 1113, 1033}, { 1114, 1034}, { 1115, 1035}, { 1116, 1036}, { 1117, 1037}, { 1118, 1038}, { 1119, 1039}, { 1121, 1120}, { 1123, 1122}, { 1125, 1124}, { 1127, 1126}, { 1129, 1128}, { 1131, 1130}, { 1133, 1132}, { 1135, 1134}, { 1137, 1136}, { 1139, 1138}, { 1141, 1140}, { 1143, 1142}, { 1145, 1144}, { 1147, 1146}, { 1149, 1148}, { 1151, 1150}, { 1153, 1152}, { 1169, 1168}, { 1171, 1170}, { 1173, 1172}, { 1175, 1174}, { 1177, 1176}, { 1179, 1178}, { 1181, 1180}, { 1183, 1182}, { 1185, 1184}, { 1187, 1186}, { 1189, 1188}, { 1191, 1190}, { 1193, 1192}, { 1195, 1194}, { 1197, 1196}, { 1199, 1198}, { 1201, 1200}, { 1203, 1202}, { 1205, 1204}, { 1207, 1206}, { 1209, 1208}, { 1211, 1210}, { 1213, 1212}, { 1215, 1214}, { 1218, 1217}, { 1220, 1219}, { 1224, 1223}, { 1228, 1227}, { 1233, 1232}, { 1235, 1234}, { 1237, 1236}, { 1239, 1238}, { 1241, 1240}, { 1243, 1242}, { 1245, 1244}, { 1247, 1246}, { 1249, 1248}, { 1251, 1250}, { 1253, 1252}, { 1255, 1254}, { 1257, 1256}, { 1259, 1258}, { 1263, 1262}, { 1265, 1264}, { 1267, 1266}, { 1269, 1268}, { 1273, 1272}, { 1377, 1329}, { 1378, 1330}, { 1379, 1331}, { 1380, 1332}, { 1381, 1333}, { 1382, 1334}, { 1383, 1335}, { 1384, 1336}, { 1385, 1337}, { 1386, 1338}, { 1387, 1339}, { 1388, 1340}, { 1389, 1341}, { 1390, 1342}, { 1391, 1343}, { 1392, 1344}, { 1393, 1345}, { 1394, 1346}, { 1395, 1347}, { 1396, 1348}, { 1397, 1349}, { 1398, 1350}, { 1399, 1351}, { 1400, 1352}, { 1401, 1353}, { 1402, 1354}, { 1403, 1355}, { 1404, 1356}, { 1405, 1357}, { 1406, 1358}, { 1407, 1359}, { 1408, 1360}, { 1409, 1361}, { 1410, 1362}, { 1411, 1363}, { 1412, 1364}, { 1413, 1365}, { 1414, 1366}, { 7681, 7680}, { 7683, 7682}, { 7685, 7684}, { 7687, 7686}, { 7689, 7688}, { 7691, 7690}, { 7693, 7692}, { 7695, 7694}, { 7697, 7696}, { 7699, 7698}, { 7701, 7700}, { 7703, 7702}, { 7705, 7704}, { 7707, 7706}, { 7709, 7708}, { 7711, 7710}, { 7713, 7712}, { 7715, 7714}, { 7717, 7716}, { 7719, 7718}, { 7721, 7720}, { 7723, 7722}, { 7725, 7724}, { 7727, 7726}, { 7729, 7728}, { 7731, 7730}, { 7733, 7732}, { 7735, 7734}, { 7737, 7736}, { 7739, 7738}, { 7741, 7740}, { 7743, 7742}, { 7745, 7744}, { 7747, 7746}, { 7749, 7748}, { 7751, 7750}, { 7753, 7752}, { 7755, 7754}, { 7757, 7756}, { 7759, 7758}, { 7761, 7760}, { 7763, 7762}, { 7765, 7764}, { 7767, 7766}, { 7769, 7768}, { 7771, 7770}, { 7773, 7772}, { 7775, 7774}, { 7777, 7776}, { 7779, 7778}, { 7781, 7780}, { 7783, 7782}, { 7785, 7784}, { 7787, 7786}, { 7789, 7788}, { 7791, 7790}, { 7793, 7792}, { 7795, 7794}, { 7797, 7796}, { 7799, 7798}, { 7801, 7800}, { 7803, 7802}, { 7805, 7804}, { 7807, 7806}, { 7809, 7808}, { 7811, 7810}, { 7813, 7812}, { 7815, 7814}, { 7817, 7816}, { 7819, 7818}, { 7821, 7820}, { 7823, 7822}, { 7825, 7824}, { 7827, 7826}, { 7829, 7828}, { 7841, 7840}, { 7843, 7842}, { 7845, 7844}, { 7847, 7846}, { 7849, 7848}, { 7851, 7850}, { 7853, 7852}, { 7855, 7854}, { 7857, 7856}, { 7859, 7858}, { 7861, 7860}, { 7863, 7862}, { 7865, 7864}, { 7867, 7866}, { 7869, 7868}, { 7871, 7870}, { 7873, 7872}, { 7875, 7874}, { 7877, 7876}, { 7879, 7878}, { 7881, 7880}, { 7883, 7882}, { 7885, 7884}, { 7887, 7886}, { 7889, 7888}, { 7891, 7890}, { 7893, 7892}, { 7895, 7894}, { 7897, 7896}, { 7899, 7898}, { 7901, 7900}, { 7903, 7902}, { 7905, 7904}, { 7907, 7906}, { 7909, 7908}, { 7911, 7910}, { 7913, 7912}, { 7915, 7914}, { 7917, 7916}, { 7919, 7918}, { 7921, 7920}, { 7923, 7922}, { 7925, 7924}, { 7927, 7926}, { 7929, 7928}, { 7936, 7944}, { 7937, 7945}, { 7938, 7946}, { 7939, 7947}, { 7940, 7948}, { 7941, 7949}, { 7942, 7950}, { 7943, 7951}, { 7952, 7960}, { 7953, 7961}, { 7954, 7962}, { 7955, 7963}, { 7956, 7964}, { 7957, 7965}, { 7968, 7976}, { 7969, 7977}, { 7970, 7978}, { 7971, 7979}, { 7972, 7980}, { 7973, 7981}, { 7974, 7982}, { 7975, 7983}, { 7984, 7992}, { 7985, 7993}, { 7986, 7994}, { 7987, 7995}, { 7988, 7996}, { 7989, 7997}, { 7990, 7998}, { 7991, 7999}, { 8000, 8008}, { 8001, 8009}, { 8002, 8010}, { 8003, 8011}, { 8004, 8012}, { 8005, 8013}, { 8017, 8025}, { 8019, 8027}, { 8021, 8029}, { 8023, 8031}, { 8032, 8040}, { 8033, 8041}, { 8034, 8042}, { 8035, 8043}, { 8036, 8044}, { 8037, 8045}, { 8038, 8046}, { 8039, 8047}, { 8048, 8122}, { 8049, 8123}, { 8050, 8136}, { 8051, 8137}, { 8052, 8138}, { 8053, 8139}, { 8054, 8154}, { 8055, 8155}, { 8056, 8184}, { 8057, 8185}, { 8058, 8170}, { 8059, 8171}, { 8060, 8186}, { 8061, 8187}, { 8112, 8120}, { 8113, 8121}, { 8144, 8152}, { 8145, 8153}, { 8160, 8168}, { 8161, 8169}, { 8165, 8172}, { 8560, 8544}, { 8561, 8545}, { 8562, 8546}, { 8563, 8547}, { 8564, 8548}, { 8565, 8549}, { 8566, 8550}, { 8567, 8551}, { 8568, 8552}, { 8569, 8553}, { 8570, 8554}, { 8571, 8555}, { 8572, 8556}, { 8573, 8557}, { 8574, 8558}, { 8575, 8559}, { 9424, 9398}, { 9425, 9399}, { 9426, 9400}, { 9427, 9401}, { 9428, 9402}, { 9429, 9403}, { 9430, 9404}, { 9431, 9405}, { 9432, 9406}, { 9433, 9407}, { 9434, 9408}, { 9435, 9409}, { 9436, 9410}, { 9437, 9411}, { 9438, 9412}, { 9439, 9413}, { 9440, 9414}, { 9441, 9415}, { 9442, 9416}, { 9443, 9417}, { 9444, 9418}, { 9445, 9419}, { 9446, 9420}, { 9447, 9421}, { 9448, 9422}, { 9449, 9423}, {65345,65313}, {65346,65314}, {65347,65315}, {65348,65316}, {65349,65317}, {65350,65318}, {65351,65319}, {65352,65320}, {65353,65321}, {65354,65322}, {65355,65323}, {65356,65324}, {65357,65325}, {65358,65326}, {65359,65327}, {65360,65328}, {65361,65329}, {65362,65330}, {65363,65331}, {65364,65332}, {65365,65333}, {65366,65334}, {65367,65335}, {65368,65336}, {65369,65337}, {65370,65338}, {65535, 0}}; static const GUnicodePairType UnicodeToLowerTable[] = { { 65, 97}, { 66, 98}, { 67, 99}, { 68, 100}, { 69, 101}, { 70, 102}, { 71, 103}, { 72, 104}, { 73, 105}, { 74, 106}, { 75, 107}, { 76, 108}, { 77, 109}, { 78, 110}, { 79, 111}, { 80, 112}, { 81, 113}, { 82, 114}, { 83, 115}, { 84, 116}, { 85, 117}, { 86, 118}, { 87, 119}, { 88, 120}, { 89, 121}, { 90, 122}, { 192, 224}, { 193, 225}, { 194, 226}, { 195, 227}, { 196, 228}, { 197, 229}, { 198, 230}, { 199, 231}, { 200, 232}, { 201, 233}, { 202, 234}, { 203, 235}, { 204, 236}, { 205, 237}, { 206, 238}, { 207, 239}, { 208, 240}, { 209, 241}, { 210, 242}, { 211, 243}, { 212, 244}, { 213, 245}, { 214, 246}, { 216, 248}, { 217, 249}, { 218, 250}, { 219, 251}, { 220, 252}, { 221, 253}, { 222, 254}, { 256, 257}, { 258, 259}, { 260, 261}, { 262, 263}, { 264, 265}, { 266, 267}, { 268, 269}, { 270, 271}, { 272, 273}, { 274, 275}, { 276, 277}, { 278, 279}, { 280, 281}, { 282, 283}, { 284, 285}, { 286, 287}, { 288, 289}, { 290, 291}, { 292, 293}, { 294, 295}, { 296, 297}, { 298, 299}, { 300, 301}, { 302, 303}, { 304, 105}, { 306, 307}, { 308, 309}, { 310, 311}, { 313, 314}, { 315, 316}, { 317, 318}, { 319, 320}, { 321, 322}, { 323, 324}, { 325, 326}, { 327, 328}, { 330, 331}, { 332, 333}, { 334, 335}, { 336, 337}, { 338, 339}, { 340, 341}, { 342, 343}, { 344, 345}, { 346, 347}, { 348, 349}, { 350, 351}, { 352, 353}, { 354, 355}, { 356, 357}, { 358, 359}, { 360, 361}, { 362, 363}, { 364, 365}, { 366, 367}, { 368, 369}, { 370, 371}, { 372, 373}, { 374, 375}, { 376, 255}, { 377, 378}, { 379, 380}, { 381, 382}, { 385, 595}, { 386, 387}, { 388, 389}, { 390, 596}, { 391, 392}, { 393, 598}, { 394, 599}, { 395, 396}, { 398, 477}, { 399, 601}, { 400, 603}, { 401, 402}, { 403, 608}, { 404, 611}, { 406, 617}, { 407, 616}, { 408, 409}, { 412, 623}, { 413, 626}, { 415, 629}, { 416, 417}, { 418, 419}, { 420, 421}, { 423, 424}, { 425, 643}, { 428, 429}, { 430, 648}, { 431, 432}, { 433, 650}, { 434, 651}, { 435, 436}, { 437, 438}, { 439, 658}, { 440, 441}, { 444, 445}, { 452, 454}, { 455, 457}, { 458, 460}, { 461, 462}, { 463, 464}, { 465, 466}, { 467, 468}, { 469, 470}, { 471, 472}, { 473, 474}, { 475, 476}, { 478, 479}, { 480, 481}, { 482, 483}, { 484, 485}, { 486, 487}, { 488, 489}, { 490, 491}, { 492, 493}, { 494, 495}, { 497, 499}, { 500, 501}, { 506, 507}, { 508, 509}, { 510, 511}, { 512, 513}, { 514, 515}, { 516, 517}, { 518, 519}, { 520, 521}, { 522, 523}, { 524, 525}, { 526, 527}, { 528, 529}, { 530, 531}, { 532, 533}, { 534, 535}, { 902, 940}, { 904, 941}, { 905, 942}, { 906, 943}, { 908, 972}, { 910, 973}, { 911, 974}, { 913, 945}, { 914, 946}, { 915, 947}, { 916, 948}, { 917, 949}, { 918, 950}, { 919, 951}, { 920, 952}, { 921, 953}, { 922, 954}, { 923, 955}, { 924, 956}, { 925, 957}, { 926, 958}, { 927, 959}, { 928, 960}, { 929, 961}, { 931, 963}, { 932, 964}, { 933, 965}, { 934, 966}, { 935, 967}, { 936, 968}, { 937, 969}, { 938, 970}, { 939, 971}, { 994, 995}, { 996, 997}, { 998, 999}, { 1000, 1001}, { 1002, 1003}, { 1004, 1005}, { 1006, 1007}, { 1024, 1104}, { 1025, 1105}, { 1026, 1106}, { 1027, 1107}, { 1028, 1108}, { 1029, 1109}, { 1030, 1110}, { 1031, 1111}, { 1032, 1112}, { 1033, 1113}, { 1034, 1114}, { 1035, 1115}, { 1036, 1116}, { 1037, 1117}, { 1038, 1118}, { 1039, 1119}, { 1040, 1072}, { 1041, 1073}, { 1042, 1074}, { 1043, 1075}, { 1044, 1076}, { 1045, 1077}, { 1046, 1078}, { 1047, 1079}, { 1048, 1080}, { 1049, 1081}, { 1050, 1082}, { 1051, 1083}, { 1052, 1084}, { 1053, 1085}, { 1054, 1086}, { 1055, 1087}, { 1056, 1088}, { 1057, 1089}, { 1058, 1090}, { 1059, 1091}, { 1060, 1092}, { 1061, 1093}, { 1062, 1094}, { 1063, 1095}, { 1064, 1096}, { 1065, 1097}, { 1066, 1098}, { 1067, 1099}, { 1068, 1100}, { 1069, 1101}, { 1070, 1102}, { 1071, 1103}, { 1120, 1121}, { 1122, 1123}, { 1124, 1125}, { 1126, 1127}, { 1128, 1129}, { 1130, 1131}, { 1132, 1133}, { 1134, 1135}, { 1136, 1137}, { 1138, 1139}, { 1140, 1141}, { 1142, 1143}, { 1144, 1145}, { 1146, 1147}, { 1148, 1149}, { 1150, 1151}, { 1152, 1153}, { 1168, 1169}, { 1170, 1171}, { 1172, 1173}, { 1174, 1175}, { 1176, 1177}, { 1178, 1179}, { 1180, 1181}, { 1182, 1183}, { 1184, 1185}, { 1186, 1187}, { 1188, 1189}, { 1190, 1191}, { 1192, 1193}, { 1194, 1195}, { 1196, 1197}, { 1198, 1199}, { 1200, 1201}, { 1202, 1203}, { 1204, 1205}, { 1206, 1207}, { 1208, 1209}, { 1210, 1211}, { 1212, 1213}, { 1214, 1215}, { 1217, 1218}, { 1219, 1220}, { 1223, 1224}, { 1227, 1228}, { 1232, 1233}, { 1234, 1235}, { 1236, 1237}, { 1238, 1239}, { 1240, 1241}, { 1242, 1243}, { 1244, 1245}, { 1246, 1247}, { 1248, 1249}, { 1250, 1251}, { 1252, 1253}, { 1254, 1255}, { 1256, 1257}, { 1258, 1259}, { 1262, 1263}, { 1264, 1265}, { 1266, 1267}, { 1268, 1269}, { 1272, 1273}, { 1329, 1377}, { 1330, 1378}, { 1331, 1379}, { 1332, 1380}, { 1333, 1381}, { 1334, 1382}, { 1335, 1383}, { 1336, 1384}, { 1337, 1385}, { 1338, 1386}, { 1339, 1387}, { 1340, 1388}, { 1341, 1389}, { 1342, 1390}, { 1343, 1391}, { 1344, 1392}, { 1345, 1393}, { 1346, 1394}, { 1347, 1395}, { 1348, 1396}, { 1349, 1397}, { 1350, 1398}, { 1351, 1399}, { 1352, 1400}, { 1353, 1401}, { 1354, 1402}, { 1355, 1403}, { 1356, 1404}, { 1357, 1405}, { 1358, 1406}, { 1359, 1407}, { 1360, 1408}, { 1361, 1409}, { 1362, 1410}, { 1363, 1411}, { 1364, 1412}, { 1365, 1413}, { 1366, 1414}, { 4256, 4304}, { 4257, 4305}, { 4258, 4306}, { 4259, 4307}, { 4260, 4308}, { 4261, 4309}, { 4262, 4310}, { 4263, 4311}, { 4264, 4312}, { 4265, 4313}, { 4266, 4314}, { 4267, 4315}, { 4268, 4316}, { 4269, 4317}, { 4270, 4318}, { 4271, 4319}, { 4272, 4320}, { 4273, 4321}, { 4274, 4322}, { 4275, 4323}, { 4276, 4324}, { 4277, 4325}, { 4278, 4326}, { 4279, 4327}, { 4280, 4328}, { 4281, 4329}, { 4282, 4330}, { 4283, 4331}, { 4284, 4332}, { 4285, 4333}, { 4286, 4334}, { 4287, 4335}, { 4288, 4336}, { 4289, 4337}, { 4290, 4338}, { 4291, 4339}, { 4292, 4340}, { 4293, 4341}, { 7680, 7681}, { 7682, 7683}, { 7684, 7685}, { 7686, 7687}, { 7688, 7689}, { 7690, 7691}, { 7692, 7693}, { 7694, 7695}, { 7696, 7697}, { 7698, 7699}, { 7700, 7701}, { 7702, 7703}, { 7704, 7705}, { 7706, 7707}, { 7708, 7709}, { 7710, 7711}, { 7712, 7713}, { 7714, 7715}, { 7716, 7717}, { 7718, 7719}, { 7720, 7721}, { 7722, 7723}, { 7724, 7725}, { 7726, 7727}, { 7728, 7729}, { 7730, 7731}, { 7732, 7733}, { 7734, 7735}, { 7736, 7737}, { 7738, 7739}, { 7740, 7741}, { 7742, 7743}, { 7744, 7745}, { 7746, 7747}, { 7748, 7749}, { 7750, 7751}, { 7752, 7753}, { 7754, 7755}, { 7756, 7757}, { 7758, 7759}, { 7760, 7761}, { 7762, 7763}, { 7764, 7765}, { 7766, 7767}, { 7768, 7769}, { 7770, 7771}, { 7772, 7773}, { 7774, 7775}, { 7776, 7777}, { 7778, 7779}, { 7780, 7781}, { 7782, 7783}, { 7784, 7785}, { 7786, 7787}, { 7788, 7789}, { 7790, 7791}, { 7792, 7793}, { 7794, 7795}, { 7796, 7797}, { 7798, 7799}, { 7800, 7801}, { 7802, 7803}, { 7804, 7805}, { 7806, 7807}, { 7808, 7809}, { 7810, 7811}, { 7812, 7813}, { 7814, 7815}, { 7816, 7817}, { 7818, 7819}, { 7820, 7821}, { 7822, 7823}, { 7824, 7825}, { 7826, 7827}, { 7828, 7829}, { 7840, 7841}, { 7842, 7843}, { 7844, 7845}, { 7846, 7847}, { 7848, 7849}, { 7850, 7851}, { 7852, 7853}, { 7854, 7855}, { 7856, 7857}, { 7858, 7859}, { 7860, 7861}, { 7862, 7863}, { 7864, 7865}, { 7866, 7867}, { 7868, 7869}, { 7870, 7871}, { 7872, 7873}, { 7874, 7875}, { 7876, 7877}, { 7878, 7879}, { 7880, 7881}, { 7882, 7883}, { 7884, 7885}, { 7886, 7887}, { 7888, 7889}, { 7890, 7891}, { 7892, 7893}, { 7894, 7895}, { 7896, 7897}, { 7898, 7899}, { 7900, 7901}, { 7902, 7903}, { 7904, 7905}, { 7906, 7907}, { 7908, 7909}, { 7910, 7911}, { 7912, 7913}, { 7914, 7915}, { 7916, 7917}, { 7918, 7919}, { 7920, 7921}, { 7922, 7923}, { 7924, 7925}, { 7926, 7927}, { 7928, 7929}, { 7944, 7936}, { 7945, 7937}, { 7946, 7938}, { 7947, 7939}, { 7948, 7940}, { 7949, 7941}, { 7950, 7942}, { 7951, 7943}, { 7960, 7952}, { 7961, 7953}, { 7962, 7954}, { 7963, 7955}, { 7964, 7956}, { 7965, 7957}, { 7976, 7968}, { 7977, 7969}, { 7978, 7970}, { 7979, 7971}, { 7980, 7972}, { 7981, 7973}, { 7982, 7974}, { 7983, 7975}, { 7992, 7984}, { 7993, 7985}, { 7994, 7986}, { 7995, 7987}, { 7996, 7988}, { 7997, 7989}, { 7998, 7990}, { 7999, 7991}, { 8008, 8000}, { 8009, 8001}, { 8010, 8002}, { 8011, 8003}, { 8012, 8004}, { 8013, 8005}, { 8025, 8017}, { 8027, 8019}, { 8029, 8021}, { 8031, 8023}, { 8040, 8032}, { 8041, 8033}, { 8042, 8034}, { 8043, 8035}, { 8044, 8036}, { 8045, 8037}, { 8046, 8038}, { 8047, 8039}, { 8120, 8112}, { 8121, 8113}, { 8122, 8048}, { 8123, 8049}, { 8136, 8050}, { 8137, 8051}, { 8138, 8052}, { 8139, 8053}, { 8152, 8144}, { 8153, 8145}, { 8154, 8054}, { 8155, 8055}, { 8168, 8160}, { 8169, 8161}, { 8170, 8058}, { 8171, 8059}, { 8172, 8165}, { 8184, 8056}, { 8185, 8057}, { 8186, 8060}, { 8187, 8061}, { 8544, 8560}, { 8545, 8561}, { 8546, 8562}, { 8547, 8563}, { 8548, 8564}, { 8549, 8565}, { 8550, 8566}, { 8551, 8567}, { 8552, 8568}, { 8553, 8569}, { 8554, 8570}, { 8555, 8571}, { 8556, 8572}, { 8557, 8573}, { 8558, 8574}, { 8559, 8575}, { 9398, 9424}, { 9399, 9425}, { 9400, 9426}, { 9401, 9427}, { 9402, 9428}, { 9403, 9429}, { 9404, 9430}, { 9405, 9431}, { 9406, 9432}, { 9407, 9433}, { 9408, 9434}, { 9409, 9435}, { 9410, 9436}, { 9411, 9437}, { 9412, 9438}, { 9413, 9439}, { 9414, 9440}, { 9415, 9441}, { 9416, 9442}, { 9417, 9443}, { 9418, 9444}, { 9419, 9445}, { 9420, 9446}, { 9421, 9447}, { 9422, 9448}, { 9423, 9449}, {65313,65345}, {65314,65346}, {65315,65347}, {65316,65348}, {65317,65349}, {65318,65350}, {65319,65351}, {65320,65352}, {65321,65353}, {65322,65354}, {65323,65355}, {65324,65356}, {65325,65357}, {65326,65358}, {65327,65359}, {65328,65360}, {65329,65361}, {65330,65362}, {65331,65363}, {65332,65364}, {65333,65365}, {65334,65366}, {65335,65367}, {65336,65368}, {65337,65369}, {65338,65370}, {65535, 0}}; int OVR_CDECL OVR_towupper(wchar_t charCode) { // Don't use UnicodeUpperBits! It differs from UnicodeToUpperBits. if (UnicodeCharIs(UnicodeToUpperBits, charCode)) { // To protect from memory overrun in case the character is not found // we use one extra fake element in the table {65536, 0}. UPInt idx = Alg::LowerBoundSliced( UnicodeToUpperTable, 0, sizeof(UnicodeToUpperTable) / sizeof(UnicodeToUpperTable[0]) - 1, (UInt16)charCode, CmpUnicodeKey); return UnicodeToUpperTable[idx].Value; } return charCode; } int OVR_CDECL OVR_towlower(wchar_t charCode) { // Don't use UnicodeLowerBits! It differs from UnicodeToLowerBits. if (UnicodeCharIs(UnicodeToLowerBits, charCode)) { // To protect from memory overrun in case the character is not found // we use one extra fake element in the table {65536, 0}. UPInt idx = Alg::LowerBoundSliced( UnicodeToLowerTable, 0, sizeof(UnicodeToLowerTable) / sizeof(UnicodeToLowerTable[0]) - 1, (UInt16)charCode, CmpUnicodeKey); return UnicodeToLowerTable[idx].Value; } return charCode; } #endif //OVR_NO_WCTYPE } // OVR \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Std.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Std.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Std.h Content : Standard C function interface Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Std_h #define OVR_Std_h #include "OVR_Types.h" #include // for va_list args #include #include #include #include #if !defined(OVR_OS_WINCE) && defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) #define OVR_MSVC_SAFESTRING #include #endif // Wide-char funcs #include #include namespace OVR { #if defined(OVR_OS_WIN32) inline char* OVR_CDECL OVR_itoa(int val, char *dest, UPInt destsize, int radix) { #if defined(OVR_MSVC_SAFESTRING) _itoa_s(val, dest, destsize, radix); return dest; #else OVR_UNUSED(destsize); return itoa(val, dest, radix); #endif } #else // OVR_OS_WIN32 inline char* OVR_itoa(int val, char* dest, unsigned int len, int radix) { if (val == 0) { if (len > 1) { dest[0] = '0'; dest[1] = '\0'; } return dest; } int cur = val; unsigned int i = 0; unsigned int sign = 0; if (val < 0) { val = -val; sign = 1; } while ((val != 0) && (i < (len - 1 - sign))) { cur = val % radix; val /= radix; if (radix == 16) { switch(cur) { case 10: dest[i] = 'a'; break; case 11: dest[i] = 'b'; break; case 12: dest[i] = 'c'; break; case 13: dest[i] = 'd'; break; case 14: dest[i] = 'e'; break; case 15: dest[i] = 'f'; break; default: dest[i] = (char)('0' + cur); break; } } else { dest[i] = (char)('0' + cur); } ++i; } if (sign) { dest[i++] = '-'; } for (unsigned int j = 0; j < i / 2; ++j) { char tmp = dest[j]; dest[j] = dest[i - 1 - j]; dest[i - 1 - j] = tmp; } dest[i] = '\0'; return dest; } #endif // String functions inline UPInt OVR_CDECL OVR_strlen(const char* str) { return strlen(str); } inline char* OVR_CDECL OVR_strcpy(char* dest, UPInt destsize, const char* src) { #if defined(OVR_MSVC_SAFESTRING) strcpy_s(dest, destsize, src); return dest; #else OVR_UNUSED(destsize); return strcpy(dest, src); #endif } inline char* OVR_CDECL OVR_strncpy(char* dest, UPInt destsize, const char* src, UPInt count) { #if defined(OVR_MSVC_SAFESTRING) strncpy_s(dest, destsize, src, count); return dest; #else OVR_UNUSED(destsize); return strncpy(dest, src, count); #endif } inline char * OVR_CDECL OVR_strcat(char* dest, UPInt destsize, const char* src) { #if defined(OVR_MSVC_SAFESTRING) strcat_s(dest, destsize, src); return dest; #else OVR_UNUSED(destsize); return strcat(dest, src); #endif } inline int OVR_CDECL OVR_strcmp(const char* dest, const char* src) { return strcmp(dest, src); } inline const char* OVR_CDECL OVR_strchr(const char* str, char c) { return strchr(str, c); } inline char* OVR_CDECL OVR_strchr(char* str, char c) { return strchr(str, c); } inline const char* OVR_strrchr(const char* str, char c) { UPInt len = OVR_strlen(str); for (UPInt i=len; i>0; i--) if (str[i]==c) return str+i; return 0; } inline const UByte* OVR_CDECL OVR_memrchr(const UByte* str, UPInt size, UByte c) { for (SPInt i = (SPInt)size - 1; i >= 0; i--) { if (str[i] == c) return str + i; } return 0; } inline char* OVR_CDECL OVR_strrchr(char* str, char c) { UPInt len = OVR_strlen(str); for (UPInt i=len; i>0; i--) if (str[i]==c) return str+i; return 0; } double OVR_CDECL OVR_strtod(const char* string, char** tailptr); inline long OVR_CDECL OVR_strtol(const char* string, char** tailptr, int radix) { return strtol(string, tailptr, radix); } inline long OVR_CDECL OVR_strtoul(const char* string, char** tailptr, int radix) { return strtoul(string, tailptr, radix); } inline int OVR_CDECL OVR_strncmp(const char* ws1, const char* ws2, UPInt size) { return strncmp(ws1, ws2, size); } inline UInt64 OVR_CDECL OVR_strtouq(const char *nptr, char **endptr, int base) { #if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) return _strtoui64(nptr, endptr, base); #else return strtoull(nptr, endptr, base); #endif } inline SInt64 OVR_CDECL OVR_strtoq(const char *nptr, char **endptr, int base) { #if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) return _strtoi64(nptr, endptr, base); #else return strtoll(nptr, endptr, base); #endif } inline SInt64 OVR_CDECL OVR_atoq(const char* string) { #if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) return _atoi64(string); #else return atoll(string); #endif } inline UInt64 OVR_CDECL OVR_atouq(const char* string) { return OVR_strtouq(string, NULL, 10); } // Implemented in GStd.cpp in platform-specific manner. int OVR_CDECL OVR_stricmp(const char* dest, const char* src); int OVR_CDECL OVR_strnicmp(const char* dest, const char* src, UPInt count); inline UPInt OVR_CDECL OVR_sprintf(char *dest, UPInt destsize, const char* format, ...) { va_list argList; va_start(argList,format); UPInt ret; #if defined(OVR_CC_MSVC) #if defined(OVR_MSVC_SAFESTRING) ret = _vsnprintf_s(dest, destsize, _TRUNCATE, format, argList); OVR_ASSERT(ret != -1); #else OVR_UNUSED(destsize); ret = _vsnprintf(dest, destsize - 1, format, argList); // -1 for space for the null character OVR_ASSERT(ret != -1); dest[destsize-1] = 0; #endif #else OVR_UNUSED(destsize); ret = vsprintf(dest, format, argList); OVR_ASSERT(ret < destsize); #endif va_end(argList); return ret; } inline UPInt OVR_CDECL OVR_vsprintf(char *dest, UPInt destsize, const char * format, va_list argList) { UPInt ret; #if defined(OVR_CC_MSVC) #if defined(OVR_MSVC_SAFESTRING) dest[0] = '\0'; int rv = vsnprintf_s(dest, destsize, _TRUNCATE, format, argList); if (rv == -1) { dest[destsize - 1] = '\0'; ret = destsize - 1; } else ret = (UPInt)rv; #else OVR_UNUSED(destsize); int rv = _vsnprintf(dest, destsize - 1, format, argList); OVR_ASSERT(rv != -1); ret = (UPInt)rv; dest[destsize-1] = 0; #endif #else OVR_UNUSED(destsize); ret = (UPInt)vsprintf(dest, format, argList); OVR_ASSERT(ret < destsize); #endif return ret; } // Returns the number of characters in the formatted string. inline UPInt OVR_CDECL OVR_vscprintf(const char * format, va_list argList) { UPInt ret; #if defined(OVR_CC_MSVC) ret = (UPInt) _vscprintf(format, argList); #else ret = (UPInt) vsnprintf(NULL, 0, format, argList); #endif return ret; } wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src); wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count); wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src); UPInt OVR_CDECL OVR_wcslen(const wchar_t* str); int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b); int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b); inline int OVR_CDECL OVR_wcsicoll(const wchar_t* a, const wchar_t* b) { #if defined(OVR_OS_WIN32) #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) return ::_wcsicoll(a, b); #else return ::wcsicoll(a, b); #endif #else // not supported, use regular wcsicmp return OVR_wcsicmp(a, b); #endif } inline int OVR_CDECL OVR_wcscoll(const wchar_t* a, const wchar_t* b) { #if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX) return wcscoll(a, b); #else // not supported, use regular wcscmp return OVR_wcscmp(a, b); #endif } #ifndef OVR_NO_WCTYPE inline int OVR_CDECL UnicodeCharIs(const UInt16* table, wchar_t charCode) { unsigned offset = table[charCode >> 8]; if (offset == 0) return 0; if (offset == 1) return 1; return (table[offset + ((charCode >> 4) & 15)] & (1 << (charCode & 15))) != 0; } extern const UInt16 UnicodeAlnumBits[]; extern const UInt16 UnicodeAlphaBits[]; extern const UInt16 UnicodeDigitBits[]; extern const UInt16 UnicodeSpaceBits[]; extern const UInt16 UnicodeXDigitBits[]; // Uncomment if necessary //extern const UInt16 UnicodeCntrlBits[]; //extern const UInt16 UnicodeGraphBits[]; //extern const UInt16 UnicodeLowerBits[]; //extern const UInt16 UnicodePrintBits[]; //extern const UInt16 UnicodePunctBits[]; //extern const UInt16 UnicodeUpperBits[]; inline int OVR_CDECL OVR_iswalnum (wchar_t charCode) { return UnicodeCharIs(UnicodeAlnumBits, charCode); } inline int OVR_CDECL OVR_iswalpha (wchar_t charCode) { return UnicodeCharIs(UnicodeAlphaBits, charCode); } inline int OVR_CDECL OVR_iswdigit (wchar_t charCode) { return UnicodeCharIs(UnicodeDigitBits, charCode); } inline int OVR_CDECL OVR_iswspace (wchar_t charCode) { return UnicodeCharIs(UnicodeSpaceBits, charCode); } inline int OVR_CDECL OVR_iswxdigit(wchar_t charCode) { return UnicodeCharIs(UnicodeXDigitBits, charCode); } // Uncomment if necessary //inline int OVR_CDECL OVR_iswcntrl (wchar_t charCode) { return UnicodeCharIs(UnicodeCntrlBits, charCode); } //inline int OVR_CDECL OVR_iswgraph (wchar_t charCode) { return UnicodeCharIs(UnicodeGraphBits, charCode); } //inline int OVR_CDECL OVR_iswlower (wchar_t charCode) { return UnicodeCharIs(UnicodeLowerBits, charCode); } //inline int OVR_CDECL OVR_iswprint (wchar_t charCode) { return UnicodeCharIs(UnicodePrintBits, charCode); } //inline int OVR_CDECL OVR_iswpunct (wchar_t charCode) { return UnicodeCharIs(UnicodePunctBits, charCode); } //inline int OVR_CDECL OVR_iswupper (wchar_t charCode) { return UnicodeCharIs(UnicodeUpperBits, charCode); } int OVR_CDECL OVR_towupper(wchar_t charCode); int OVR_CDECL OVR_towlower(wchar_t charCode); #else // OVR_NO_WCTYPE inline int OVR_CDECL OVR_iswspace(wchar_t c) { return iswspace(c); } inline int OVR_CDECL OVR_iswdigit(wchar_t c) { return iswdigit(c); } inline int OVR_CDECL OVR_iswxdigit(wchar_t c) { return iswxdigit(c); } inline int OVR_CDECL OVR_iswalpha(wchar_t c) { return iswalpha(c); } inline int OVR_CDECL OVR_iswalnum(wchar_t c) { return iswalnum(c); } inline wchar_t OVR_CDECL OVR_towlower(wchar_t c) { return (wchar_t)towlower(c); } inline wchar_t OVR_towupper(wchar_t c) { return (wchar_t)towupper(c); } #endif // OVR_NO_WCTYPE // ASCII versions of tolower and toupper. Don't use "char" inline int OVR_CDECL OVR_tolower(int c) { return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; } inline int OVR_CDECL OVR_toupper(int c) { return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; } inline double OVR_CDECL OVR_wcstod(const wchar_t* string, wchar_t** tailptr) { #if defined(OVR_OS_OTHER) OVR_UNUSED(tailptr); char buffer[64]; char* tp = NULL; UPInt max = OVR_wcslen(string); if (max > 63) max = 63; unsigned char c = 0; for (UPInt i=0; i < max; i++) { c = (unsigned char)string[i]; buffer[i] = ((c) < 128 ? (char)c : '!'); } buffer[max] = 0; return OVR_strtod(buffer, &tp); #else return wcstod(string, tailptr); #endif } inline long OVR_CDECL OVR_wcstol(const wchar_t* string, wchar_t** tailptr, int radix) { #if defined(OVR_OS_OTHER) OVR_UNUSED(tailptr); char buffer[64]; char* tp = NULL; UPInt max = OVR_wcslen(string); if (max > 63) max = 63; unsigned char c = 0; for (UPInt i=0; i < max; i++) { c = (unsigned char)string[i]; buffer[i] = ((c) < 128 ? (char)c : '!'); } buffer[max] = 0; return strtol(buffer, &tp, radix); #else return wcstol(string, tailptr, radix); #endif } } // OVR #endif // OVR_Std_h \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_String.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_String.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : OVR_String.cpp Content : String UTF8 string implementation with copy-on-write semantics (thread-safe for assignment but not modification). Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_String.h" #include #include #ifdef OVR_OS_QNX # include #endif namespace OVR { #define String_LengthIsSize (UPInt(1) << String::Flag_LengthIsSizeShift) String::DataDesc String::NullData = {String_LengthIsSize, 1, {0} }; String::String() { pData = &NullData; pData->AddRef(); }; String::String(const char* pdata) { // Obtain length in bytes; it doesn't matter if _data is UTF8. UPInt size = pdata ? OVR_strlen(pdata) : 0; pData = AllocDataCopy1(size, 0, pdata, size); }; String::String(const char* pdata1, const char* pdata2, const char* pdata3) { // Obtain length in bytes; it doesn't matter if _data is UTF8. UPInt size1 = pdata1 ? OVR_strlen(pdata1) : 0; UPInt size2 = pdata2 ? OVR_strlen(pdata2) : 0; UPInt size3 = pdata3 ? OVR_strlen(pdata3) : 0; DataDesc *pdataDesc = AllocDataCopy2(size1 + size2 + size3, 0, pdata1, size1, pdata2, size2); memcpy(pdataDesc->Data + size1 + size2, pdata3, size3); pData = pdataDesc; } String::String(const char* pdata, UPInt size) { OVR_ASSERT((size == 0) || (pdata != 0)); pData = AllocDataCopy1(size, 0, pdata, size); }; String::String(const InitStruct& src, UPInt size) { pData = AllocData(size, 0); src.InitString(GetData()->Data, size); } String::String(const String& src) { pData = src.GetData(); pData->AddRef(); } String::String(const StringBuffer& src) { pData = AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize()); } String::String(const wchar_t* data) { pData = &NullData; pData->AddRef(); // Simplified logic for wchar_t constructor. if (data) *this = data; } String::DataDesc* String::AllocData(UPInt size, UPInt lengthIsSize) { String::DataDesc* pdesc; if (size == 0) { pdesc = &NullData; pdesc->AddRef(); return pdesc; } pdesc = (DataDesc*)OVR_ALLOC(sizeof(DataDesc)+ size); pdesc->Data[size] = 0; pdesc->RefCount = 1; pdesc->Size = size | lengthIsSize; return pdesc; } String::DataDesc* String::AllocDataCopy1(UPInt size, UPInt lengthIsSize, const char* pdata, UPInt copySize) { String::DataDesc* pdesc = AllocData(size, lengthIsSize); memcpy(pdesc->Data, pdata, copySize); return pdesc; } String::DataDesc* String::AllocDataCopy2(UPInt size, UPInt lengthIsSize, const char* pdata1, UPInt copySize1, const char* pdata2, UPInt copySize2) { String::DataDesc* pdesc = AllocData(size, lengthIsSize); memcpy(pdesc->Data, pdata1, copySize1); memcpy(pdesc->Data + copySize1, pdata2, copySize2); return pdesc; } UPInt String::GetLength() const { // Optimize length accesses for non-UTF8 character strings. DataDesc* pdata = GetData(); UPInt length, size = pdata->GetSize(); if (pdata->LengthIsSize()) return size; length = (UPInt)UTF8Util::GetLength(pdata->Data, (UPInt)size); if (length == size) pdata->Size |= String_LengthIsSize; return length; } //static UInt32 String_CharSearch(const char* buf, ) UInt32 String::GetCharAt(UPInt index) const { SPInt i = (SPInt) index; DataDesc* pdata = GetData(); const char* buf = pdata->Data; UInt32 c; if (pdata->LengthIsSize()) { OVR_ASSERT(index < pdata->GetSize()); buf += i; return UTF8Util::DecodeNextChar_Advance0(&buf); } c = UTF8Util::GetCharAt(index, buf, pdata->GetSize()); return c; } UInt32 String::GetFirstCharAt(UPInt index, const char** offset) const { DataDesc* pdata = GetData(); SPInt i = (SPInt) index; const char* buf = pdata->Data; const char* end = buf + pdata->GetSize(); UInt32 c; do { c = UTF8Util::DecodeNextChar_Advance0(&buf); i--; if (buf >= end) { // We've hit the end of the string; don't go further. OVR_ASSERT(i == 0); return c; } } while (i >= 0); *offset = buf; return c; } UInt32 String::GetNextChar(const char** offset) const { return UTF8Util::DecodeNextChar(offset); } void String::AppendChar(UInt32 ch) { DataDesc* pdata = GetData(); UPInt size = pdata->GetSize(); char buff[8]; SPInt encodeSize = 0; // Converts ch into UTF8 string and fills it into buff. UTF8Util::EncodeChar(buff, &encodeSize, ch); OVR_ASSERT(encodeSize >= 0); SetData(AllocDataCopy2(size + (UPInt)encodeSize, 0, pdata->Data, size, buff, (UPInt)encodeSize)); pdata->Release(); } void String::AppendString(const wchar_t* pstr, SPInt len) { if (!pstr) return; DataDesc* pdata = GetData(); UPInt oldSize = pdata->GetSize(); UPInt encodeSize = (UPInt)UTF8Util::GetEncodeStringSize(pstr, len); DataDesc* pnewData = AllocDataCopy1(oldSize + (UPInt)encodeSize, 0, pdata->Data, oldSize); UTF8Util::EncodeString(pnewData->Data + oldSize, pstr, len); SetData(pnewData); pdata->Release(); } void String::AppendString(const char* putf8str, SPInt utf8StrSz) { if (!putf8str || !utf8StrSz) return; if (utf8StrSz == -1) utf8StrSz = (SPInt)OVR_strlen(putf8str); DataDesc* pdata = GetData(); UPInt oldSize = pdata->GetSize(); SetData(AllocDataCopy2(oldSize + (UPInt)utf8StrSz, 0, pdata->Data, oldSize, putf8str, (UPInt)utf8StrSz)); pdata->Release(); } void String::AssignString(const InitStruct& src, UPInt size) { DataDesc* poldData = GetData(); DataDesc* pnewData = AllocData(size, 0); src.InitString(pnewData->Data, size); SetData(pnewData); poldData->Release(); } void String::AssignString(const char* putf8str, UPInt size) { DataDesc* poldData = GetData(); SetData(AllocDataCopy1(size, 0, putf8str, size)); poldData->Release(); } void String::operator = (const char* pstr) { AssignString(pstr, pstr ? OVR_strlen(pstr) : 0); } void String::operator = (const wchar_t* pwstr) { DataDesc* poldData = GetData(); UPInt size = pwstr ? (UPInt)UTF8Util::GetEncodeStringSize(pwstr) : 0; DataDesc* pnewData = AllocData(size, 0); UTF8Util::EncodeString(pnewData->Data, pwstr); SetData(pnewData); poldData->Release(); } void String::operator = (const String& src) { DataDesc* psdata = src.GetData(); DataDesc* pdata = GetData(); SetData(psdata); psdata->AddRef(); pdata->Release(); } void String::operator = (const StringBuffer& src) { DataDesc* polddata = GetData(); SetData(AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize())); polddata->Release(); } void String::operator += (const String& src) { DataDesc *pourData = GetData(), *psrcData = src.GetData(); UPInt ourSize = pourData->GetSize(), srcSize = psrcData->GetSize(); UPInt lflag = pourData->GetLengthFlag() & psrcData->GetLengthFlag(); SetData(AllocDataCopy2(ourSize + srcSize, lflag, pourData->Data, ourSize, psrcData->Data, srcSize)); pourData->Release(); } String String::operator + (const char* str) const { String tmp1(*this); tmp1 += (str ? str : ""); return tmp1; } String String::operator + (const String& src) const { String tmp1(*this); tmp1 += src; return tmp1; } void String::Remove(UPInt posAt, SPInt removeLength) { DataDesc* pdata = GetData(); UPInt oldSize = pdata->GetSize(); // Length indicates the number of characters to remove. UPInt length = GetLength(); // If index is past the string, nothing to remove. if (posAt >= length) return; // Otherwise, cap removeLength to the length of the string. if ((posAt + removeLength) > length) removeLength = length - posAt; // Get the byte position of the UTF8 char at position posAt. SPInt bytePos = UTF8Util::GetByteIndex(posAt, pdata->Data, oldSize); SPInt removeSize = UTF8Util::GetByteIndex(removeLength, pdata->Data + bytePos, oldSize-bytePos); SetData(AllocDataCopy2(oldSize - removeSize, pdata->GetLengthFlag(), pdata->Data, bytePos, pData->Data + bytePos + removeSize, (oldSize - bytePos - removeSize))); pdata->Release(); } String String::Substring(UPInt start, UPInt end) const { UPInt length = GetLength(); if ((start >= length) || (start >= end)) return String(); DataDesc* pdata = GetData(); // If size matches, we know the exact index range. if (pdata->LengthIsSize()) return String(pdata->Data + start, end - start); // Get position of starting character. SPInt byteStart = UTF8Util::GetByteIndex(start, pdata->Data, pdata->GetSize()); SPInt byteSize = UTF8Util::GetByteIndex(end - start, pdata->Data + byteStart, pdata->GetSize()-byteStart); return String(pdata->Data + byteStart, (UPInt)byteSize); } void String::Clear() { NullData.AddRef(); GetData()->Release(); SetData(&NullData); } String String::ToUpper() const { UInt32 c; const char* psource = GetData()->Data; const char* pend = psource + GetData()->GetSize(); String str; SPInt bufferOffset = 0; char buffer[512]; while(psource < pend) { do { c = UTF8Util::DecodeNextChar_Advance0(&psource); UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towupper(wchar_t(c))); } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8))); // Append string a piece at a time. str.AppendString(buffer, bufferOffset); bufferOffset = 0; } return str; } String String::ToLower() const { UInt32 c; const char* psource = GetData()->Data; const char* pend = psource + GetData()->GetSize(); String str; SPInt bufferOffset = 0; char buffer[512]; while(psource < pend) { do { c = UTF8Util::DecodeNextChar_Advance0(&psource); UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towlower(wchar_t(c))); } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8))); // Append string a piece at a time. str.AppendString(buffer, bufferOffset); bufferOffset = 0; } return str; } String& String::Insert(const char* substr, UPInt posAt, SPInt strSize) { DataDesc* poldData = GetData(); UPInt oldSize = poldData->GetSize(); UPInt insertSize = (strSize < 0) ? OVR_strlen(substr) : (UPInt)strSize; UPInt byteIndex = (poldData->LengthIsSize()) ? posAt : (UPInt)UTF8Util::GetByteIndex(posAt, poldData->Data, oldSize); OVR_ASSERT(byteIndex <= oldSize); DataDesc* pnewData = AllocDataCopy2(oldSize + insertSize, 0, poldData->Data, byteIndex, substr, insertSize); memcpy(pnewData->Data + byteIndex + insertSize, poldData->Data + byteIndex, oldSize - byteIndex); SetData(pnewData); poldData->Release(); return *this; } /* String& String::Insert(const UInt32* substr, UPInt posAt, SPInt len) { for (SPInt i = 0; i < len; ++i) { UPInt charw = InsertCharAt(substr[i], posAt); posAt += charw; } return *this; } */ UPInt String::InsertCharAt(UInt32 c, UPInt posAt) { char buf[8]; SPInt index = 0; UTF8Util::EncodeChar(buf, &index, c); OVR_ASSERT(index >= 0); buf[(UPInt)index] = 0; Insert(buf, posAt, index); return (UPInt)index; } int String::CompareNoCase(const char* a, const char* b) { return OVR_stricmp(a, b); } int String::CompareNoCase(const char* a, const char* b, SPInt len) { if (len) { SPInt f,l; SPInt slen = len; const char *s = b; do { f = (SPInt)OVR_tolower((int)(*(a++))); l = (SPInt)OVR_tolower((int)(*(b++))); } while (--len && f && (f == l) && *b != 0); if (f == l && (len != 0 || *b != 0)) { f = (SPInt)slen; l = (SPInt)OVR_strlen(s); return int(f - l); } return int(f - l); } else return (0-(int)OVR_strlen(b)); } // ***** Implement hash static functions // Hash function UPInt String::BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed) { const UByte* pdata = (const UByte*) pdataIn; UPInt h = seed; while (size > 0) { size--; h = ((h << 5) + h) ^ (unsigned) pdata[size]; } return h; } // Hash function, case-insensitive UPInt String::BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed) { const UByte* pdata = (const UByte*) pdataIn; UPInt h = seed; while (size > 0) { size--; h = ((h << 5) + h) ^ OVR_tolower(pdata[size]); } // Alternative: "sdbm" hash function, suggested at same web page above. // h = 0; // for bytes { h = (h << 16) + (h << 6) - hash + *p; } return h; } // ***** String Buffer used for Building Strings #define OVR_SBUFF_DEFAULT_GROW_SIZE 512 // Constructors / Destructor. StringBuffer::StringBuffer() : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { } StringBuffer::StringBuffer(UPInt growSize) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { SetGrowSize(growSize); } StringBuffer::StringBuffer(const char* data) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { *this = data; } StringBuffer::StringBuffer(const char* data, UPInt dataSize) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { AppendString(data, dataSize); } StringBuffer::StringBuffer(const String& src) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { AppendString(src.ToCStr(), src.GetSize()); } StringBuffer::StringBuffer(const StringBuffer& src) : pData(NULL), Size(0), BufferSize(src.GetGrowSize()), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { AppendString(src.ToCStr(), src.GetSize()); LengthIsSize = src.LengthIsSize; } StringBuffer::StringBuffer(const wchar_t* data) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { *this = data; } StringBuffer::~StringBuffer() { if (pData) OVR_FREE(pData); } void StringBuffer::SetGrowSize(UPInt growSize) { if (growSize <= 16) GrowSize = 16; else { UByte bits = Alg::UpperBit(UInt32(growSize-1)); UPInt size = 1<= BufferSize) // >= because of trailing zero! (!AB) { BufferSize = (_size + 1 + GrowSize - 1)& ~(GrowSize-1); if (!pData) pData = (char*)OVR_ALLOC(BufferSize); else pData = (char*)OVR_REALLOC(pData, BufferSize); } } void StringBuffer::Resize(UPInt _size) { Reserve(_size); LengthIsSize = false; Size = _size; if (pData) pData[Size] = 0; } void StringBuffer::Clear() { Resize(0); /* if (pData != pEmptyNullData) { OVR_FREE(pHeap, pData); pData = pEmptyNullData; Size = BufferSize = 0; LengthIsSize = false; } */ } // Appends a character void StringBuffer::AppendChar(UInt32 ch) { char buff[8]; UPInt origSize = GetSize(); // Converts ch into UTF8 string and fills it into buff. Also increments index according to the number of bytes // in the UTF8 string. SPInt srcSize = 0; UTF8Util::EncodeChar(buff, &srcSize, ch); OVR_ASSERT(srcSize >= 0); UPInt size = origSize + srcSize; Resize(size); memcpy(pData + origSize, buff, srcSize); } // Append a string void StringBuffer::AppendString(const wchar_t* pstr, SPInt len) { if (!pstr) return; SPInt srcSize = UTF8Util::GetEncodeStringSize(pstr, len); UPInt origSize = GetSize(); UPInt size = srcSize + origSize; Resize(size); UTF8Util::EncodeString(pData + origSize, pstr, len); } void StringBuffer::AppendString(const char* putf8str, SPInt utf8StrSz) { if (!putf8str || !utf8StrSz) return; if (utf8StrSz == -1) utf8StrSz = (SPInt)OVR_strlen(putf8str); UPInt origSize = GetSize(); UPInt size = utf8StrSz + origSize; Resize(size); memcpy(pData + origSize, putf8str, utf8StrSz); } void StringBuffer::operator = (const char* pstr) { pstr = pstr ? pstr : ""; UPInt size = OVR_strlen(pstr); Resize(size); memcpy(pData, pstr, size); } void StringBuffer::operator = (const wchar_t* pstr) { pstr = pstr ? pstr : L""; UPInt size = (UPInt)UTF8Util::GetEncodeStringSize(pstr); Resize(size); UTF8Util::EncodeString(pData, pstr); } void StringBuffer::operator = (const String& src) { Resize(src.GetSize()); memcpy(pData, src.ToCStr(), src.GetSize()); } // Inserts substr at posAt void StringBuffer::Insert(const char* substr, UPInt posAt, SPInt len) { UPInt oldSize = Size; UPInt insertSize = (len < 0) ? OVR_strlen(substr) : (UPInt)len; UPInt byteIndex = LengthIsSize ? posAt : (UPInt)UTF8Util::GetByteIndex(posAt, pData, (SPInt)Size); OVR_ASSERT(byteIndex <= oldSize); Reserve(oldSize + insertSize); memmove(pData + byteIndex + insertSize, pData + byteIndex, oldSize - byteIndex + 1); memcpy (pData + byteIndex, substr, insertSize); LengthIsSize = false; Size = oldSize + insertSize; pData[Size] = 0; } // Inserts character at posAt UPInt StringBuffer::InsertCharAt(UInt32 c, UPInt posAt) { char buf[8]; SPInt len = 0; UTF8Util::EncodeChar(buf, &len, c); OVR_ASSERT(len >= 0); buf[(UPInt)len] = 0; Insert(buf, posAt, len); return (UPInt)len; } } // OVR \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_String.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_String.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_String.h Content : String UTF8 string implementation with copy-on-write semantics (thread-safe for assignment but not modification). Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_String_h #define OVR_String_h #include "OVR_Types.h" #include "OVR_Allocator.h" #include "OVR_UTF8Util.h" #include "OVR_Atomic.h" #include "OVR_Std.h" #include "OVR_Alg.h" namespace OVR { // ***** Classes class String; class StringBuffer; //----------------------------------------------------------------------------------- // ***** String Class // String is UTF8 based string class with copy-on-write implementation // for assignment. class String { protected: enum FlagConstants { //Flag_GetLength = 0x7FFFFFFF, // This flag is set if GetLength() == GetSize() for a string. // Avoid extra scanning is Substring and indexing logic. Flag_LengthIsSizeShift = (sizeof(UPInt)*8 - 1) }; // Internal structure to hold string data struct DataDesc { // Number of bytes. Will be the same as the number of chars if the characters // are ascii, may not be equal to number of chars in case string data is UTF8. UPInt Size; volatile SInt32 RefCount; char Data[1]; void AddRef() { AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); } // Decrement ref count. This needs to be thread-safe, since // a different thread could have also decremented the ref count. // For example, if u start off with a ref count = 2. Now if u // decrement the ref count and check against 0 in different // statements, a different thread can also decrement the ref count // in between our decrement and checking against 0 and will find // the ref count = 0 and delete the object. This will lead to a crash // when context switches to our thread and we'll be trying to delete // an already deleted object. Hence decrementing the ref count and // checking against 0 needs to made an atomic operation. void Release() { if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) OVR_FREE(this); } static UPInt GetLengthFlagBit() { return UPInt(1) << Flag_LengthIsSizeShift; } UPInt GetSize() const { return Size & ~GetLengthFlagBit() ; } UPInt GetLengthFlag() const { return Size & GetLengthFlagBit(); } bool LengthIsSize() const { return GetLengthFlag() != 0; } }; // Heap type of the string is encoded in the lower bits. enum HeapType { HT_Global = 0, // Heap is global. HT_Local = 1, // SF::String_loc: Heap is determined based on string's address. HT_Dynamic = 2, // SF::String_temp: Heap is stored as a part of the class. HT_Mask = 3 }; union { DataDesc* pData; UPInt HeapTypeBits; }; typedef union { DataDesc* pData; UPInt HeapTypeBits; } DataDescUnion; inline HeapType GetHeapType() const { return (HeapType) (HeapTypeBits & HT_Mask); } inline DataDesc* GetData() const { DataDescUnion u; u.pData = pData; u.HeapTypeBits = (u.HeapTypeBits & ~(UPInt)HT_Mask); return u.pData; } inline void SetData(DataDesc* pdesc) { HeapType ht = GetHeapType(); pData = pdesc; OVR_ASSERT((HeapTypeBits & HT_Mask) == 0); HeapTypeBits |= ht; } DataDesc* AllocData(UPInt size, UPInt lengthIsSize); DataDesc* AllocDataCopy1(UPInt size, UPInt lengthIsSize, const char* pdata, UPInt copySize); DataDesc* AllocDataCopy2(UPInt size, UPInt lengthIsSize, const char* pdata1, UPInt copySize1, const char* pdata2, UPInt copySize2); // Special constructor to avoid data initalization when used in derived class. struct NoConstructor { }; String(const NoConstructor&) { } public: // For initializing string with dynamic buffer struct InitStruct { virtual ~InitStruct() { } virtual void InitString(char* pbuffer, UPInt size) const = 0; }; // Constructors / Destructors. String(); String(const char* data); String(const char* data1, const char* pdata2, const char* pdata3 = 0); String(const char* data, UPInt buflen); String(const String& src); String(const StringBuffer& src); String(const InitStruct& src, UPInt size); explicit String(const wchar_t* data); // Destructor (Captain Obvious guarantees!) ~String() { GetData()->Release(); } // Declaration of NullString static DataDesc NullData; // *** General Functions void Clear(); // For casting to a pointer to char. operator const char*() const { return GetData()->Data; } // Pointer to raw buffer. const char* ToCStr() const { return GetData()->Data; } // Returns number of bytes UPInt GetSize() const { return GetData()->GetSize() ; } // Tells whether or not the string is empty bool IsEmpty() const { return GetSize() == 0; } // Returns number of characters UPInt GetLength() const; // Returns character at the specified index UInt32 GetCharAt(UPInt index) const; UInt32 GetFirstCharAt(UPInt index, const char** offset) const; UInt32 GetNextChar(const char** offset) const; // Appends a character void AppendChar(UInt32 ch); // Append a string void AppendString(const wchar_t* pstr, SPInt len = -1); void AppendString(const char* putf8str, SPInt utf8StrSz = -1); // Assigned a string with dynamic data (copied through initializer). void AssignString(const InitStruct& src, UPInt size); // Assigns string with known size. void AssignString(const char* putf8str, UPInt size); // Resize the string to the new size // void Resize(UPInt _size); // Removes the character at posAt void Remove(UPInt posAt, SPInt len = 1); // Returns a String that's a substring of this. // -start is the index of the first UTF8 character you want to include. // -end is the index one past the last UTF8 character you want to include. String Substring(UPInt start, UPInt end) const; // Case-conversion String ToUpper() const; String ToLower() const; // Inserts substr at posAt String& Insert (const char* substr, UPInt posAt, SPInt len = -1); // Inserts character at posAt UPInt InsertCharAt(UInt32 c, UPInt posAt); // Inserts substr at posAt, which is an index of a character (not byte). // Of size is specified, it is in bytes. // String& Insert(const UInt32* substr, UPInt posAt, SPInt size = -1); // Get Byte index of the character at position = index UPInt GetByteIndex(UPInt index) const { return (UPInt)UTF8Util::GetByteIndex(index, GetData()->Data); } // Utility: case-insensitive string compare. stricmp() & strnicmp() are not // ANSI or POSIX, do not seem to appear in Linux. static int OVR_STDCALL CompareNoCase(const char* a, const char* b); static int OVR_STDCALL CompareNoCase(const char* a, const char* b, SPInt len); // Hash function, case-insensitive static UPInt OVR_STDCALL BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed = 5381); // Hash function, case-sensitive static UPInt OVR_STDCALL BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed = 5381); // ***** File path parsing helper functions. // Implemented in OVR_String_FilePath.cpp. // Absolute paths can star with: // - protocols: 'file://', 'http://' // - windows drive: 'c:\' // - UNC share name: '\\share' // - unix root '/' static bool HasAbsolutePath(const char* path); static bool HasExtension(const char* path); static bool HasProtocol(const char* path); bool HasAbsolutePath() const { return HasAbsolutePath(ToCStr()); } bool HasExtension() const { return HasExtension(ToCStr()); } bool HasProtocol() const { return HasProtocol(ToCStr()); } String GetProtocol() const; // Returns protocol, if any, with trailing '://'. String GetPath() const; // Returns path with trailing '/'. String GetFilename() const; // Returns filename, including extension. String GetExtension() const; // Returns extension with a dot. void StripProtocol(); // Strips front protocol, if any, from the string. void StripExtension(); // Strips off trailing extension. // Operators // Assignment void operator = (const char* str); void operator = (const wchar_t* str); void operator = (const String& src); void operator = (const StringBuffer& src); // Addition void operator += (const String& src); void operator += (const char* psrc) { AppendString(psrc); } void operator += (const wchar_t* psrc) { AppendString(psrc); } void operator += (char ch) { AppendChar(ch); } String operator + (const char* str) const; String operator + (const String& src) const; // Comparison bool operator == (const String& str) const { return (OVR_strcmp(GetData()->Data, str.GetData()->Data)== 0); } bool operator != (const String& str) const { return !operator == (str); } bool operator == (const char* str) const { return OVR_strcmp(GetData()->Data, str) == 0; } bool operator != (const char* str) const { return !operator == (str); } bool operator < (const char* pstr) const { return OVR_strcmp(GetData()->Data, pstr) < 0; } bool operator < (const String& str) const { return *this < str.GetData()->Data; } bool operator > (const char* pstr) const { return OVR_strcmp(GetData()->Data, pstr) > 0; } bool operator > (const String& str) const { return *this > str.GetData()->Data; } int CompareNoCase(const char* pstr) const { return CompareNoCase(GetData()->Data, pstr); } int CompareNoCase(const String& str) const { return CompareNoCase(GetData()->Data, str.ToCStr()); } // Accesses raw bytes const char& operator [] (int index) const { OVR_ASSERT(index >= 0 && (UPInt)index < GetSize()); return GetData()->Data[index]; } const char& operator [] (UPInt index) const { OVR_ASSERT(index < GetSize()); return GetData()->Data[index]; } // Case insensitive keys are used to look up insensitive string in hash tables // for SWF files with version before SWF 7. struct NoCaseKey { const String* pStr; NoCaseKey(const String &str) : pStr(&str){}; }; bool operator == (const NoCaseKey& strKey) const { return (CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0); } bool operator != (const NoCaseKey& strKey) const { return !(CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0); } // Hash functor used for strings. struct HashFunctor { UPInt operator()(const String& data) const { UPInt size = data.GetSize(); return String::BernsteinHashFunction((const char*)data, size); } }; // Case-insensitive hash functor used for strings. Supports additional // lookup based on NoCaseKey. struct NoCaseHashFunctor { UPInt operator()(const String& data) const { UPInt size = data.GetSize(); return String::BernsteinHashFunctionCIS((const char*)data, size); } UPInt operator()(const NoCaseKey& data) const { UPInt size = data.pStr->GetSize(); return String::BernsteinHashFunctionCIS((const char*)data.pStr->ToCStr(), size); } }; }; //----------------------------------------------------------------------------------- // ***** String Buffer used for Building Strings class StringBuffer { char* pData; UPInt Size; UPInt BufferSize; UPInt GrowSize; mutable bool LengthIsSize; public: // Constructors / Destructor. StringBuffer(); explicit StringBuffer(UPInt growSize); StringBuffer(const char* data); StringBuffer(const char* data, UPInt buflen); StringBuffer(const String& src); StringBuffer(const StringBuffer& src); explicit StringBuffer(const wchar_t* data); ~StringBuffer(); // Modify grow size used for growing/shrinking the buffer. UPInt GetGrowSize() const { return GrowSize; } void SetGrowSize(UPInt growSize); // *** General Functions // Does not release memory, just sets Size to 0 void Clear(); // For casting to a pointer to char. operator const char*() const { return (pData) ? pData : ""; } // Pointer to raw buffer. const char* ToCStr() const { return (pData) ? pData : ""; } // Returns number of bytes. UPInt GetSize() const { return Size ; } // Tells whether or not the string is empty. bool IsEmpty() const { return GetSize() == 0; } // Returns number of characters UPInt GetLength() const; // Returns character at the specified index UInt32 GetCharAt(UPInt index) const; UInt32 GetFirstCharAt(UPInt index, const char** offset) const; UInt32 GetNextChar(const char** offset) const; // Resize the string to the new size void Resize(UPInt _size); void Reserve(UPInt _size); // Appends a character void AppendChar(UInt32 ch); // Append a string void AppendString(const wchar_t* pstr, SPInt len = -1); void AppendString(const char* putf8str, SPInt utf8StrSz = -1); void AppendFormat(const char* format, ...); // Assigned a string with dynamic data (copied through initializer). //void AssignString(const InitStruct& src, UPInt size); // Inserts substr at posAt void Insert (const char* substr, UPInt posAt, SPInt len = -1); // Inserts character at posAt UPInt InsertCharAt(UInt32 c, UPInt posAt); // Assignment void operator = (const char* str); void operator = (const wchar_t* str); void operator = (const String& src); // Addition void operator += (const String& src) { AppendString(src.ToCStr(),src.GetSize()); } void operator += (const char* psrc) { AppendString(psrc); } void operator += (const wchar_t* psrc) { AppendString(psrc); } void operator += (char ch) { AppendChar(ch); } //String operator + (const char* str) const ; //String operator + (const String& src) const ; // Accesses raw bytes char& operator [] (int index) { OVR_ASSERT(((UPInt)index) < GetSize()); return pData[index]; } char& operator [] (UPInt index) { OVR_ASSERT(index < GetSize()); return pData[index]; } const char& operator [] (int index) const { OVR_ASSERT(((UPInt)index) < GetSize()); return pData[index]; } const char& operator [] (UPInt index) const { OVR_ASSERT(index < GetSize()); return pData[index]; } }; // // Wrapper for string data. The data must have a guaranteed // lifespan throughout the usage of the wrapper. Not intended for // cached usage. Not thread safe. // class StringDataPtr { public: StringDataPtr() : pStr(NULL), Size(0) {} StringDataPtr(const StringDataPtr& p) : pStr(p.pStr), Size(p.Size) {} StringDataPtr(const char* pstr, UPInt sz) : pStr(pstr), Size(sz) {} StringDataPtr(const char* pstr) : pStr(pstr), Size((pstr != NULL) ? OVR_strlen(pstr) : 0) {} explicit StringDataPtr(const String& str) : pStr(str.ToCStr()), Size(str.GetSize()) {} template StringDataPtr(const T (&v)[N]) : pStr(v), Size(N) {} public: const char* ToCStr() const { return pStr; } UPInt GetSize() const { return Size; } bool IsEmpty() const { return GetSize() == 0; } // value is a prefix of this string // Character's values are not compared. bool IsPrefix(const StringDataPtr& value) const { return ToCStr() == value.ToCStr() && GetSize() >= value.GetSize(); } // value is a suffix of this string // Character's values are not compared. bool IsSuffix(const StringDataPtr& value) const { return ToCStr() <= value.ToCStr() && (End()) == (value.End()); } // Find first character. // init_ind - initial index. SPInt FindChar(char c, UPInt init_ind = 0) const { for (UPInt i = init_ind; i < GetSize(); ++i) if (pStr[i] == c) return static_cast(i); return -1; } // Find last character. // init_ind - initial index. SPInt FindLastChar(char c, UPInt init_ind = ~0) const { if (init_ind == (UPInt)~0 || init_ind > GetSize()) init_ind = GetSize(); else ++init_ind; for (UPInt i = init_ind; i > 0; --i) if (pStr[i - 1] == c) return static_cast(i - 1); return -1; } // Create new object and trim size bytes from the left. StringDataPtr GetTrimLeft(UPInt size) const { // Limit trim size to the size of the string. size = Alg::PMin(GetSize(), size); return StringDataPtr(ToCStr() + size, GetSize() - size); } // Create new object and trim size bytes from the right. StringDataPtr GetTrimRight(UPInt size) const { // Limit trim to the size of the string. size = Alg::PMin(GetSize(), size); return StringDataPtr(ToCStr(), GetSize() - size); } // Create new object, which contains next token. // Useful for parsing. StringDataPtr GetNextToken(char separator = ':') const { UPInt cur_pos = 0; const char* cur_str = ToCStr(); for (; cur_pos < GetSize() && cur_str[cur_pos]; ++cur_pos) { if (cur_str[cur_pos] == separator) { break; } } return StringDataPtr(ToCStr(), cur_pos); } // Trim size bytes from the left. StringDataPtr& TrimLeft(UPInt size) { // Limit trim size to the size of the string. size = Alg::PMin(GetSize(), size); pStr += size; Size -= size; return *this; } // Trim size bytes from the right. StringDataPtr& TrimRight(UPInt size) { // Limit trim to the size of the string. size = Alg::PMin(GetSize(), size); Size -= size; return *this; } const char* Begin() const { return ToCStr(); } const char* End() const { return ToCStr() + GetSize(); } // Hash functor used string data pointers struct HashFunctor { UPInt operator()(const StringDataPtr& data) const { return String::BernsteinHashFunction(data.ToCStr(), data.GetSize()); } }; bool operator== (const StringDataPtr& data) const { return (OVR_strncmp(pStr, data.pStr, data.Size) == 0); } protected: const char* pStr; UPInt Size; }; } // OVR #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_StringHash.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_StringHash.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: None Filename : OVR_StringHash.h Content : String hash table used when optional case-insensitive lookup is required. Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_StringHash_h #define OVR_StringHash_h #include "OVR_String.h" #include "OVR_Hash.h" namespace OVR { //----------------------------------------------------------------------------------- // *** StringHash // This is a custom string hash table that supports case-insensitive // searches through special functions such as GetCaseInsensitive, etc. // This class is used for Flash labels, exports and other case-insensitive tables. template > class StringHash : public Hash { public: typedef U ValueType; typedef StringHash SelfType; typedef Hash BaseType; public: void operator = (const SelfType& src) { BaseType::operator = (src); } bool GetCaseInsensitive(const String& key, U* pvalue) const { String::NoCaseKey ikey(key); return BaseType::GetAlt(ikey, pvalue); } // Pointer-returning get variety. const U* GetCaseInsensitive(const String& key) const { String::NoCaseKey ikey(key); return BaseType::GetAlt(ikey); } U* GetCaseInsensitive(const String& key) { String::NoCaseKey ikey(key); return BaseType::GetAlt(ikey); } typedef typename BaseType::Iterator base_iterator; base_iterator FindCaseInsensitive(const String& key) { String::NoCaseKey ikey(key); return BaseType::FindAlt(ikey); } // Set just uses a find and assigns value if found. The key is not modified; // this behavior is identical to Flash string variable assignment. void SetCaseInsensitive(const String& key, const U& value) { base_iterator it = FindCaseInsensitive(key); if (it != BaseType::End()) { it->Second = value; } else { BaseType::Add(key, value); } } }; } // OVR #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_String_FormatUtil.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_String_FormatUtil.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : OVR_String_FormatUtil.cpp Content : String format functions. Created : February 27, 2013 Notes : Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_String.h" #include "OVR_Log.h" namespace OVR { void StringBuffer::AppendFormat(const char* format, ...) { va_list argList; va_start(argList, format); UPInt size = OVR_vscprintf(format, argList); va_end(argList); char* buffer = (char*) OVR_ALLOC(sizeof(char) * (size+1)); va_start(argList, format); UPInt result = OVR_vsprintf(buffer, size+1, format, argList); OVR_UNUSED1(result); va_end(argList); OVR_ASSERT_LOG(result == size, ("Error in OVR_vsprintf")); AppendString(buffer); OVR_FREE(buffer); } } // OVR \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_String_PathUtil.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_String_PathUtil.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : OVR_String_PathUtil.cpp Content : String filename/url helper function Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_String.h" #include "OVR_UTF8Util.h" namespace OVR { //-------------------------------------------------------------------- // ***** Path-Scanner helper function // Scans file path finding filename start and extension start, fills in their addess. void ScanFilePath(const char* url, const char** pfilename, const char** pext) { const char* urlStart = url; const char *filename = 0; const char *lastDot = 0; UInt32 charVal = UTF8Util::DecodeNextChar(&url); while (charVal != 0) { if ((charVal == '/') || (charVal == '\\')) { filename = url; lastDot = 0; } else if (charVal == '.') { lastDot = url - 1; } charVal = UTF8Util::DecodeNextChar(&url); } if (pfilename) { // It was a naked filename if (urlStart && (*urlStart != '.') && *urlStart) *pfilename = urlStart; else *pfilename = filename; } if (pext) { *pext = lastDot; } } // Scans till the end of protocol. Returns first character past protocol, // 0 if not found. // - protocol: 'file://', 'http://' const char* ScanPathProtocol(const char* url) { UInt32 charVal = UTF8Util::DecodeNextChar(&url); UInt32 charVal2; while (charVal != 0) { // Treat a colon followed by a slash as absolute. if (charVal == ':') { charVal2 = UTF8Util::DecodeNextChar(&url); charVal = UTF8Util::DecodeNextChar(&url); if ((charVal == '/') && (charVal2 == '\\')) return url; } charVal = UTF8Util::DecodeNextChar(&url); } return 0; } //-------------------------------------------------------------------- // ***** String Path API implementation bool String::HasAbsolutePath(const char* url) { // Absolute paths can star with: // - protocols: 'file://', 'http://' // - windows drive: 'c:\' // - UNC share name: '\\share' // - unix root '/' // On the other hand, relative paths are: // - directory: 'directory/file' // - this directory: './file' // - parent directory: '../file' // // For now, we don't parse '.' or '..' out, but instead let it be concatenated // to string and let the OS figure it out. This, however, is not good for file // name matching in library/etc, so it should be improved. if (!url || !*url) return true; // Treat empty strings as absolute. UInt32 charVal = UTF8Util::DecodeNextChar(&url); // Fist character of '/' or '\\' means absolute url. if ((charVal == '/') || (charVal == '\\')) return true; while (charVal != 0) { // Treat a colon followed by a slash as absolute. if (charVal == ':') { charVal = UTF8Util::DecodeNextChar(&url); // Protocol or windows drive. Absolute. if ((charVal == '/') || (charVal == '\\')) return true; } else if ((charVal == '/') || (charVal == '\\')) { // Not a first character (else 'if' above the loop would have caught it). // Must be a relative url. break; } charVal = UTF8Util::DecodeNextChar(&url); } // We get here for relative paths. return false; } bool String::HasExtension(const char* path) { const char* ext = 0; ScanFilePath(path, 0, &ext); return ext != 0; } bool String::HasProtocol(const char* path) { return ScanPathProtocol(path) != 0; } String String::GetPath() const { const char* filename = 0; ScanFilePath(ToCStr(), &filename, 0); // Technically we can have extra logic somewhere for paths, // such as enforcing protocol and '/' only based on flags, // but we keep it simple for now. return String(ToCStr(), filename ? (filename-ToCStr()) : GetSize()); } String String::GetProtocol() const { const char* protocolEnd = ScanPathProtocol(ToCStr()); return String(ToCStr(), protocolEnd ? (protocolEnd-ToCStr()) : 0); } String String::GetFilename() const { const char* filename = 0; ScanFilePath(ToCStr(), &filename, 0); return String(filename); } String String::GetExtension() const { const char* ext = 0; ScanFilePath(ToCStr(), 0, &ext); return String(ext); } void String::StripExtension() { const char* ext = 0; ScanFilePath(ToCStr(), 0, &ext); if (ext) { *this = String(ToCStr(), ext-ToCStr()); } } void String::StripProtocol() { const char* protocol = ScanPathProtocol(ToCStr()); if (protocol) AssignString(protocol, OVR_strlen(protocol)); } } // OVR \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_SysFile.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_SysFile.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************** Filename : OVR_SysFile.cpp Content : File wrapper class implementation (Win32) Created : April 5, 1999 Authors : Michael Antonov Copyright : Copyright 2011 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. **************************************************************************/ #define GFILE_CXX // Standard C library (Captain Obvious guarantees!) #include #include "OVR_SysFile.h" namespace OVR { // This is - a dummy file that fails on all calls. class UnopenedFile : public File { public: UnopenedFile() { } ~UnopenedFile() { } virtual const char* GetFilePath() { return 0; } // ** File Information virtual bool IsValid() { return 0; } virtual bool IsWritable() { return 0; } // Return position / file size virtual int Tell() { return 0; } virtual SInt64 LTell() { return 0; } virtual int GetLength() { return 0; } virtual SInt64 LGetLength() { return 0; } // virtual bool Stat(FileStats *pfs) { return 0; } virtual int GetErrorCode() { return Error_FileNotFound; } // ** Stream implementation & I/O virtual int Write(const UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); } virtual int Read(UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); } virtual int SkipBytes(int numBytes) { return 0; OVR_UNUSED(numBytes); } virtual int BytesAvailable() { return 0; } virtual bool Flush() { return 0; } virtual int Seek(int offset, int origin) { return -1; OVR_UNUSED2(offset, origin); } virtual SInt64 LSeek(SInt64 offset, int origin) { return -1; OVR_UNUSED2(offset, origin); } virtual int CopyFromStream(File *pstream, int byteSize) { return -1; OVR_UNUSED2(pstream, byteSize); } virtual bool Close() { return 0; } }; // ***** System File // System file is created to access objects on file system directly // This file can refer directly to path // ** Constructor SysFile::SysFile() : DelegatedFile(0) { pFile = *new UnopenedFile; } File* FileFILEOpen(const String& path, int flags, int mode); // Opens a file SysFile::SysFile(const String& path, int flags, int mode) : DelegatedFile(0) { Open(path, flags, mode); } // ** Open & management // Will fail if file's already open bool SysFile::Open(const String& path, int flags, int mode) { pFile = *FileFILEOpen(path, flags, mode); if ((!pFile) || (!pFile->IsValid())) { pFile = *new UnopenedFile; return 0; } //pFile = *OVR_NEW DelegatedFile(pFile); // MA Testing if (flags & Open_Buffered) pFile = *new BufferedFile(pFile); return 1; } // ** Overrides int SysFile::GetErrorCode() { return pFile ? pFile->GetErrorCode() : Error_FileNotFound; } // Overrides to provide re-open support bool SysFile::IsValid() { return pFile && pFile->IsValid(); } bool SysFile::Close() { if (IsValid()) { DelegatedFile::Close(); pFile = *new UnopenedFile; return 1; } return 0; } } // OVR \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_SysFile.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_SysFile.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: Kernel Filename : OVR_SysFile.h Content : Header for all internal file management - functions and structures to be inherited by OS specific subclasses. Created : September 19, 2012 Notes : Notes : errno may not be preserved across use of GBaseFile member functions : Directories cannot be deleted while files opened from them are in use (For the GetFullName function) Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_SysFile_h #define OVR_SysFile_h #include "OVR_File.h" namespace OVR { // ***** Declared classes class SysFile; //----------------------------------------------------------------------------------- // *** File Statistics // This class contents are similar to _stat, providing // creation, modify and other information about the file. struct FileStat { // No change or create time because they are not available on most systems SInt64 ModifyTime; SInt64 AccessTime; SInt64 FileSize; bool operator== (const FileStat& stat) const { return ( (ModifyTime == stat.ModifyTime) && (AccessTime == stat.AccessTime) && (FileSize == stat.FileSize) ); } }; //----------------------------------------------------------------------------------- // *** System File // System file is created to access objects on file system directly // This file can refer directly to path. // System file can be open & closed several times; however, such use is not recommended // This class is realy a wrapper around an implementation of File interface for a // particular platform. class SysFile : public DelegatedFile { protected: SysFile(const SysFile &source) : DelegatedFile () { OVR_UNUSED(source); } public: // ** Constructor SysFile(); // Opens a file SysFile(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite); // ** Open & management bool Open(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite); OVR_FORCE_INLINE bool Create(const String& path, int mode = Mode_ReadWrite) { return Open(path, Open_ReadWrite|Open_Create, mode); } // Helper function: obtain file statistics information. In GFx, this is used to detect file changes. // Return 0 if function failed, most likely because the file doesn't exist. static bool OVR_CDECL GetFileStat(FileStat* pfileStats, const String& path); // ** Overrides // Overridden to provide re-open support virtual int GetErrorCode(); virtual bool IsValid(); virtual bool Close(); }; } // Scaleform #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_System.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_System.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : OVR_System.cpp Content : General kernel initialization/cleanup, including that of the memory allocator. Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_System.h" #include "OVR_Threads.h" #include "OVR_Timer.h" namespace OVR { // ***** OVR::System Implementation // Initializes System core, installing allocator. void System::Init(Log* log, Allocator *palloc) { if (!Allocator::GetInstance()) { Log::SetGlobalLog(log); Timer::initializeTimerSystem(); Allocator::setInstance(palloc); } else { OVR_DEBUG_LOG(("System::Init failed - duplicate call.")); } } void System::Destroy() { if (Allocator::GetInstance()) { // Wait for all threads to finish; this must be done so that memory // allocator and all destructors finalize correctly. #ifdef OVR_ENABLE_THREADS Thread::FinishAllThreads(); #endif // Shutdown heap and destroy SysAlloc singleton, if any. Allocator::GetInstance()->onSystemShutdown(); Allocator::setInstance(0); Timer::shutdownTimerSystem(); Log::SetGlobalLog(Log::GetDefaultLog()); } else { OVR_DEBUG_LOG(("System::Destroy failed - System not initialized.")); } } // Returns 'true' if system was properly initialized. bool System::IsInitialized() { return Allocator::GetInstance() != 0; } } // OVR \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_System.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_System.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR Filename : OVR_System.h Content : General kernel initialization/cleanup, including that of the memory allocator. Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_System_h #define OVR_System_h #include "OVR_Allocator.h" #include "OVR_Log.h" namespace OVR { // ***** System Core Initialization class // System initialization must take place before any other OVR_Kernel objects are used; // this is done my calling System::Init(). Among other things, this is necessary to // initialize the memory allocator. Similarly, System::Destroy must be // called before program exist for proper cleanup. Both of these tasks can be achieved by // simply creating System object first, allowing its constructor/destructor do the work. // TBD: Require additional System class for Oculus Rift API? class System { public: // System constructor expects allocator to be specified, if it is being substituted. System(Log* log = Log::ConfigureDefaultLog(LogMask_Debug), Allocator* palloc = DefaultAllocator::InitSystemSingleton()) { Init(log, palloc); } ~System() { Destroy(); } // Returns 'true' if system was properly initialized. static bool OVR_CDECL IsInitialized(); // Initializes System core. Users can override memory implementation by passing // a different Allocator here. static void OVR_CDECL Init(Log* log = Log::ConfigureDefaultLog(LogMask_Debug), Allocator *palloc = DefaultAllocator::InitSystemSingleton()); // De-initializes System more, finalizing the threading system and destroying // the global memory allocator. static void OVR_CDECL Destroy(); }; } // OVR #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Threads.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Threads.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: None Filename : OVR_Threads.h Content : Contains thread-related (safe) functionality Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Threads_h #define OVR_Threads_h #include "OVR_Types.h" #include "OVR_Atomic.h" #include "OVR_RefCount.h" #include "OVR_Array.h" // Defines the infinite wait delay timeout #define OVR_WAIT_INFINITE 0xFFFFFFFF // To be defined in the project configuration options #ifdef OVR_ENABLE_THREADS namespace OVR { //----------------------------------------------------------------------------------- // ****** Declared classes // Declared with thread support only class Mutex; class WaitCondition; class Event; // Implementation forward declarations class MutexImpl; class WaitConditionImpl; //----------------------------------------------------------------------------------- // ***** Mutex // Mutex class represents a system Mutex synchronization object that provides access // serialization between different threads, allowing one thread mutually exclusive access // to a resource. Mutex is more heavy-weight then Lock, but supports WaitCondition. class Mutex { friend class WaitConditionImpl; friend class MutexImpl; MutexImpl *pImpl; public: // Constructor/destructor Mutex(bool recursive = 1); ~Mutex(); // Locking functions void DoLock(); bool TryLock(); void Unlock(); // Returns 1 if the mutes is currently locked by another thread // Returns 0 if the mutex is not locked by another thread, and can therefore be acquired. bool IsLockedByAnotherThread(); // Locker class; Used for automatic locking of a mutex withing scope class Locker { public: Mutex *pMutex; Locker(Mutex *pmutex) { pMutex = pmutex; pMutex->DoLock(); } ~Locker() { pMutex->Unlock(); } }; }; //----------------------------------------------------------------------------------- // ***** WaitCondition /* WaitCondition is a synchronization primitive that can be used to implement what is known as a monitor. Dependent threads wait on a wait condition by calling Wait(), and get woken up by other threads that call Notify() or NotifyAll(). The unique feature of this class is that it provides an atomic way of first releasing a Mutex, and then starting a wait on a wait condition. If both the mutex and the wait condition are associated with the same resource, this ensures that any condition checked for while the mutex was locked does not change before the wait on the condition is actually initiated. */ class WaitCondition { friend class WaitConditionImpl; // Internal implementation structure WaitConditionImpl *pImpl; public: // Constructor/destructor WaitCondition(); ~WaitCondition(); // Release mutex and wait for condition. The mutex is re-aquired after the wait. // Delay is specified in milliseconds (1/1000 of a second). 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(); }; //----------------------------------------------------------------------------------- // ***** Event // Event is a wait-able synchronization object similar to Windows event. // Event can be waited on until it's signaled by another thread calling // either SetEvent or PulseEvent. class Event { // Event state, its mutex and the wait condition volatile bool State; volatile bool Temporary; mutable Mutex StateMutex; WaitCondition StateWaitCondition; void updateState(bool newState, bool newTemp, bool mustNotify); public: Event(bool setInitially = 0) : State(setInitially), Temporary(false) { } ~Event() { } // Wait on an event condition until it is set // Delay is specified in milliseconds (1/1000 of a second). bool Wait(unsigned delay = OVR_WAIT_INFINITE); // Set an event, releasing objects waiting on it void SetEvent() { updateState(true, false, true); } // Reset an event, un-signaling it void ResetEvent() { updateState(false, false, false); } // Set and then reset an event once a waiter is released. // If threads are already waiting, they will be notified and released // If threads are not waiting, the event is set until the first thread comes in void PulseEvent() { updateState(true, true, true); } }; //----------------------------------------------------------------------------------- // ***** Thread class // ThreadId uniquely identifies a thread; returned by GetCurrentThreadId() and // Thread::GetThreadId. typedef void* ThreadId; // *** Thread flags // Indicates that the thread is has been started, i.e. Start method has been called, and threads // OnExit() method has not yet been called/returned. #define OVR_THREAD_STARTED 0x01 // This flag is set once the thread has ran, and finished. #define OVR_THREAD_FINISHED 0x02 // This flag is set temporarily if this thread was started suspended. It is used internally. #define OVR_THREAD_START_SUSPENDED 0x08 // This flag is used to ask a thread to exit. Message driven threads will usually check this flag // and finish once it is set. #define OVR_THREAD_EXIT 0x10 class Thread : public RefCountBase { // NOTE: Waitable must be the first base since it implements RefCountImpl. public: // *** Callback functions, can be used instead of overriding Run // Run function prototypes. // Thread function and user handle passed to it, executed by the default // Thread::Run implementation if not null. typedef int (*ThreadFn)(Thread *pthread, void* h); // Thread ThreadFunction1 is executed if not 0, otherwise ThreadFunction2 is tried ThreadFn ThreadFunction; // User handle passes to a thread void* UserHandle; // Thread state to start a thread with enum ThreadState { NotRunning = 0, Running = 1, Suspended = 2 }; // Thread priority enum ThreadPriority { CriticalPriority, HighestPriority, AboveNormalPriority, NormalPriority, BelowNormalPriority, LowestPriority, IdlePriority, }; // Thread constructor parameters struct CreateParams { CreateParams(ThreadFn func = 0, void* hand = 0, UPInt ssize = 128 * 1024, int proc = -1, ThreadState state = NotRunning, ThreadPriority prior = NormalPriority) : threadFunction(func), userHandle(hand), stackSize(ssize), processor(proc), initialState(state), priority(prior) {} ThreadFn threadFunction; // Thread function void* userHandle; // User handle passes to a thread UPInt stackSize; // Thread stack size int processor; // Thread hardware processor ThreadState initialState; // ThreadPriority priority; // Thread priority }; // *** Constructors // A default constructor always creates a thread in NotRunning state, because // the derived class has not yet been initialized. The derived class can call Start explicitly. // "processor" parameter specifies which hardware processor this thread will be run on. // -1 means OS decides this. Implemented only on Win32 Thread(UPInt stackSize = 128 * 1024, int processor = -1); // Constructors that initialize the thread with a pointer to function. // An option to start a thread is available, but it should not be used if classes are derived from Thread. // "processor" parameter specifies which hardware processor this thread will be run on. // -1 means OS decides this. Implemented only on Win32 Thread(ThreadFn threadFunction, void* userHandle = 0, UPInt stackSize = 128 * 1024, int processor = -1, ThreadState initialState = NotRunning); // Constructors that initialize the thread with a create parameters structure. explicit Thread(const CreateParams& params); // Destructor. virtual ~Thread(); // Waits for all Threads to finish; should be called only from the root // application thread. Once this function returns, we know that all other // thread's references to Thread object have been released. static void OVR_CDECL FinishAllThreads(); // *** Overridable Run function for thread processing // - returning from this method will end the execution of the thread // - return value is usually 0 for success virtual int Run(); // Called after return/exit function virtual void OnExit(); // *** Thread management // Starts the thread if its not already running // - internally sets up the threading and calls Run() // - initial state can either be Running or Suspended, NotRunning will just fail and do nothing // - returns the exit code virtual bool Start(ThreadState initialState = Running); // Quits with an exit code virtual void Exit(int exitCode=0); // Suspend the thread until resumed // Returns 1 for success, 0 for failure. bool Suspend(); // Resumes currently suspended thread // Returns 1 for success, 0 for failure. bool Resume(); // Static function to return a pointer to the current thread //static Thread* GetThread(); // *** Thread status query functions bool GetExitFlag() const; void SetExitFlag(bool exitFlag); // Determines whether the thread was running and is now finished bool IsFinished() const; // Determines if the thread is currently suspended bool IsSuspended() const; // Returns current thread state ThreadState GetThreadState() const; // Returns the number of available CPUs on the system static int GetCPUCount(); // Returns the thread exit code. Exit code is initialized to 0, // and set to the return value if Run function after the thread is finished. inline int GetExitCode() const { return ExitCode; } // Returns an OS handle #if defined(OVR_OS_WIN32) void* GetOSHandle() const { return ThreadHandle; } #else pthread_t GetOSHandle() const { return ThreadHandle; } #endif #if defined(OVR_OS_WIN32) ThreadId GetThreadId() const { return IdValue; } #else ThreadId GetThreadId() const { return (ThreadId)GetOSHandle(); } #endif static int GetOSPriority(ThreadPriority); // *** Sleep // Sleep secs seconds static bool Sleep(unsigned secs); // Sleep msecs milliseconds static bool MSleep(unsigned msecs); // *** Debugging functionality #if defined(OVR_OS_WIN32) virtual void SetThreadName( const char* name ); #else virtual void SetThreadName( const char* name ) { OVR_UNUSED(name); } #endif private: #if defined(OVR_OS_WIN32) friend unsigned WINAPI Thread_Win32StartFn(void *pthread); #else friend void *Thread_PthreadStartFn(void * phandle); static int InitAttr; static pthread_attr_t Attr; #endif protected: // Thread state flags AtomicInt ThreadFlags; AtomicInt SuspendCount; UPInt StackSize; // Hardware processor which this thread is running on. int Processor; ThreadPriority Priority; #if defined(OVR_OS_WIN32) void* ThreadHandle; volatile ThreadId IdValue; // System-specific cleanup function called from destructor void CleanupSystemThread(); #else pthread_t ThreadHandle; #endif // Exit code of the thread, as returned by Run. int ExitCode; // Internal run function. int PRun(); // Finishes the thread and releases internal reference to it. void FinishAndRelease(); void Init(const CreateParams& params); // Protected copy constructor Thread(const Thread &source) { OVR_UNUSED(source); } }; // Returns the unique Id of a thread it is called on, intended for // comparison purposes. ThreadId GetCurrentThreadId(); } // OVR #endif // OVR_ENABLE_THREADS #endif // OVR_Threads_h \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Timer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Timer.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : OVR_Timer.cpp Content : Provides static functions for precise timing Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_Timer.h" #if defined (OVR_OS_WIN32) #include #else #include #endif namespace OVR { //----------------------------------------------------------------------------------- // ***** Timer Class UInt64 Timer::GetProfileTicks() { return (GetRawTicks() * MksPerSecond) / GetRawFrequency(); } double Timer::GetProfileSeconds() { static UInt64 StartTime = GetProfileTicks(); return TicksToSeconds(GetProfileTicks()-StartTime); } //------------------------------------------------------------------------ // *** Win32 Specific Timer #if (defined (OVR_OS_WIN32)) CRITICAL_SECTION WinAPI_GetTimeCS; volatile UInt32 WinAPI_OldTime = 0; volatile UInt32 WinAPI_WrapCounter = 0; UInt32 Timer::GetTicksMs() { return timeGetTime(); } UInt64 Timer::GetTicks() { DWORD ticks = timeGetTime(); UInt64 result; // On Win32 QueryPerformanceFrequency is unreliable due to SMP and // performance levels, so use this logic to detect wrapping and track // high bits. ::EnterCriticalSection(&WinAPI_GetTimeCS); if (WinAPI_OldTime > ticks) WinAPI_WrapCounter++; WinAPI_OldTime = ticks; result = (UInt64(WinAPI_WrapCounter) << 32) | ticks; ::LeaveCriticalSection(&WinAPI_GetTimeCS); return result * MksPerMs; } UInt64 Timer::GetRawTicks() { LARGE_INTEGER li; QueryPerformanceCounter(&li); return li.QuadPart; } UInt64 Timer::GetRawFrequency() { static UInt64 perfFreq = 0; if (perfFreq == 0) { LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); perfFreq = freq.QuadPart; } return perfFreq; } void Timer::initializeTimerSystem() { timeBeginPeriod(1); InitializeCriticalSection(&WinAPI_GetTimeCS); } void Timer::shutdownTimerSystem() { DeleteCriticalSection(&WinAPI_GetTimeCS); timeEndPeriod(1); } #else // !OVR_OS_WIN32 //------------------------------------------------------------------------ // *** Standard OS Timer UInt32 Timer::GetTicksMs() { return (UInt32)(GetProfileTicks() / 1000); } // The profile ticks implementation is just fine for a normal timer. UInt64 Timer::GetTicks() { return GetProfileTicks(); } void Timer::initializeTimerSystem() { } void Timer::shutdownTimerSystem() { } UInt64 Timer::GetRawTicks() { // TODO: prefer rdtsc when available? // Return microseconds. struct timeval tv; UInt64 result; gettimeofday(&tv, 0); result = (UInt64)tv.tv_sec * 1000000; result += tv.tv_usec; return result; } UInt64 Timer::GetRawFrequency() { return MksPerSecond; } #endif // !OVR_OS_WIN32 } // OVR \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Timer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Timer.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR Filename : OVR_Timer.h Content : Provides static functions for precise timing Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Timer_h #define OVR_Timer_h #include "OVR_Types.h" namespace OVR { //----------------------------------------------------------------------------------- // ***** Timer // Timer class defines a family of static functions used for application // timing and profiling. class Timer { public: enum { MsPerSecond = 1000, // Milliseconds in one second. MksPerMs = 1000, // Microseconds in one millisecond. MksPerSecond = MsPerSecond * MksPerMs }; // ***** Timing APIs for Application // These APIs should be used to guide animation and other program functions // that require precision. // Returns ticks in milliseconds, as a 32-bit number. May wrap around every // 49.2 days. Use either time difference of two values of GetTicks to avoid // wrap-around. GetTicksMs may perform better then GetTicks. static UInt32 OVR_STDCALL GetTicksMs(); // GetTicks returns general-purpose high resolution application timer value, // measured in microseconds (mks, or 1/1000000 of a second). The actual precision // is system-specific and may be much lower, such as 1 ms. static UInt64 OVR_STDCALL GetTicks(); // ***** Profiling APIs. // These functions should be used for profiling, but may have system specific // artifacts that make them less appropriate for general system use. // On Win32, for example these rely on QueryPerformanceConter may have // problems with thread-core switching and power modes. // Return a hi-res timer value in mks (1/1000000 of a sec). // Generally you want to call this at the start and end of an // operation, and pass the difference to // TicksToSeconds() to find out how long the operation took. static UInt64 OVR_STDCALL GetProfileTicks(); // More convenient zero-based profile timer in seconds. First call initializes // the "zero" value; future calls return the difference. Not thread safe for first call. // Due to low precision of Double, may malfunction after long runtime. static double OVR_STDCALL GetProfileSeconds(); // Get the raw cycle counter value, providing the maximum possible timer resolution. static UInt64 OVR_STDCALL GetRawTicks(); static UInt64 OVR_STDCALL GetRawFrequency(); // ***** Tick and time unit conversion. // Convert micro-second ticks value into seconds value. static inline double TicksToSeconds(UInt64 ticks) { return static_cast(ticks) * (1.0 / (double)MksPerSecond); } // Convert Raw or frequency-unit ticks to seconds based on specified frequency. static inline double RawTicksToSeconds(UInt64 rawTicks, UInt64 rawFrequency) { return static_cast(rawTicks) * rawFrequency; } private: friend class System; // System called during program startup/shutdown. static void initializeTimerSystem(); static void shutdownTimerSystem(); }; } // Scaleform::Timer #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_Types.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_Types.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Types.h Content : Standard library defines and simple types Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Types_H #define OVR_Types_H //----------------------------------------------------------------------------------- // ****** Operating System // // Type definitions exist for the following operating systems: (OVR_OS_x) // // WIN32 - Win32 (Windows 95/98/ME and Windows NT/2000/XP) // DARWIN - Darwin OS (Mac OS X) // LINUX - Linux // ANDROID - Android // IPHONE - iPhone #if (defined(__APPLE__) && (defined(__GNUC__) ||\ defined(__xlC__) || defined(__xlc__))) || defined(__MACOS__) # if (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) || defined(__IPHONE_OS_VERSION_MIN_REQUIRED)) # define OVR_OS_IPHONE # else # define OVR_OS_DARWIN # define OVR_OS_MAC # endif #elif (defined(WIN64) || defined(_WIN64) || defined(__WIN64__)) # define OVR_OS_WIN32 #elif (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)) # define OVR_OS_WIN32 #elif defined(__linux__) || defined(__linux) # define OVR_OS_LINUX #else # define OVR_OS_OTHER #endif #if defined(ANDROID) # define OVR_OS_ANDROID #endif //----------------------------------------------------------------------------------- // ***** CPU Architecture // // The following CPUs are defined: (OVR_CPU_x) // // X86 - x86 (IA-32) // X86_64 - x86_64 (amd64) // PPC - PowerPC // PPC64 - PowerPC64 // MIPS - MIPS // OTHER - CPU for which no special support is present or needed #if defined(__x86_64__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) # define OVR_CPU_X86_64 # define OVR_64BIT_POINTERS #elif defined(__i386__) || defined(OVR_OS_WIN32) # define OVR_CPU_X86 #elif defined(__powerpc64__) # define OVR_CPU_PPC64 #elif defined(__ppc__) # define OVR_CPU_PPC #elif defined(__mips__) || defined(__MIPSEL__) # define OVR_CPU_MIPS #elif defined(__arm__) # define OVR_CPU_ARM #else # define OVR_CPU_OTHER #endif //----------------------------------------------------------------------------------- // ***** Co-Processor Architecture // // The following co-processors are defined: (OVR_CPU_x) // // SSE - Available on all modern x86 processors. // Altivec - Available on all modern ppc processors. // Neon - Available on some armv7+ processors. #if defined(__SSE__) || defined(OVR_OS_WIN32) # define OVR_CPU_SSE #endif // __SSE__ #if defined( __ALTIVEC__ ) # define OVR_CPU_ALTIVEC #endif // __ALTIVEC__ #if defined(__ARM_NEON__) # define OVR_CPU_ARM_NEON #endif // __ARM_NEON__ //----------------------------------------------------------------------------------- // ***** Compiler // // The following compilers are defined: (OVR_CC_x) // // MSVC - Microsoft Visual C/C++ // INTEL - Intel C++ for Linux / Windows // GNU - GNU C++ // ARM - ARM C/C++ #if defined(__INTEL_COMPILER) // Intel 4.0 = 400 // Intel 5.0 = 500 // Intel 6.0 = 600 // Intel 8.0 = 800 // Intel 9.0 = 900 # define OVR_CC_INTEL __INTEL_COMPILER #elif defined(_MSC_VER) // MSVC 5.0 = 1100 // MSVC 6.0 = 1200 // MSVC 7.0 (VC2002) = 1300 // MSVC 7.1 (VC2003) = 1310 // MSVC 8.0 (VC2005) = 1400 // MSVC 9.0 (VC2008) = 1500 // MSVC 10.0 (VC2010) = 1600 # define OVR_CC_MSVC _MSC_VER #elif defined(__GNUC__) # define OVR_CC_GNU #elif defined(__CC_ARM) # define OVR_CC_ARM #else # error "Oculus does not support this Compiler" #endif //----------------------------------------------------------------------------------- // ***** Compiler Warnings // Disable MSVC warnings #if defined(OVR_CC_MSVC) # pragma warning(disable : 4127) // Inconsistent dll linkage # pragma warning(disable : 4530) // Exception handling # if (OVR_CC_MSVC<1300) # pragma warning(disable : 4514) // Unreferenced inline function has been removed # pragma warning(disable : 4710) // Function not inlined # pragma warning(disable : 4714) // _force_inline not inlined # pragma warning(disable : 4786) // Debug variable name longer than 255 chars # endif // (OVR_CC_MSVC<1300) #endif // (OVR_CC_MSVC) // *** Linux Unicode - must come before Standard Includes #ifdef OVR_OS_LINUX // Use glibc unicode functions on linux. # ifndef _GNU_SOURCE # define _GNU_SOURCE # endif #endif //----------------------------------------------------------------------------------- // ***** Standard Includes // #include #include #include // MSVC Based Memory Leak checking - for now #if defined(OVR_CC_MSVC) && defined(OVR_BUILD_DEBUG) # define _CRTDBG_MAP_ALLOC # include # include // Uncomment this to help debug memory leaks under Visual Studio in OVR apps only. // This shouldn't be defined in customer releases. # ifndef OVR_DEFINE_NEW # define OVR_DEFINE_NEW new(__FILE__, __LINE__) # define new OVR_DEFINE_NEW # endif #endif //----------------------------------------------------------------------------------- // ***** Type definitions for Common Systems namespace OVR { typedef char Char; // Pointer-sized integer typedef size_t UPInt; typedef ptrdiff_t SPInt; #if defined(OVR_OS_WIN32) typedef char SByte; // 8 bit Integer (Byte) typedef unsigned char UByte; typedef short SInt16; // 16 bit Integer (Word) typedef unsigned short UInt16; typedef long SInt32; // 32 bit Integer typedef unsigned long UInt32; typedef __int64 SInt64; // 64 bit Integer (QWord) typedef unsigned __int64 UInt64; #elif defined(OVR_OS_MAC) || defined(OVR_OS_IPHONE) || defined(OVR_CC_GNU) typedef int SByte __attribute__((__mode__ (__QI__))); typedef unsigned int UByte __attribute__((__mode__ (__QI__))); typedef int SInt16 __attribute__((__mode__ (__HI__))); typedef unsigned int UInt16 __attribute__((__mode__ (__HI__))); typedef int SInt32 __attribute__((__mode__ (__SI__))); typedef unsigned int UInt32 __attribute__((__mode__ (__SI__))); typedef int SInt64 __attribute__((__mode__ (__DI__))); typedef unsigned int UInt64 __attribute__((__mode__ (__DI__))); #else #include typedef int8_t SByte; typedef uint8_t UByte; typedef int16_t SInt16; typedef uint16_t UInt16; typedef int32_t SInt32; typedef uint32_t UInt32; typedef int64_t SInt64; typedef uint64_t UInt64; #endif // ***** BaseTypes Namespace // BaseTypes namespace is explicitly declared to allow base types to be used // by customers directly without other contents of OVR namespace. // // Its is expected that GFx samples will declare 'using namespace OVR::BaseTypes' // to allow using these directly without polluting the target scope with other // OVR declarations, such as Ptr<>, String or Mutex. namespace BaseTypes { using OVR::UPInt; using OVR::SPInt; using OVR::UByte; using OVR::SByte; using OVR::UInt16; using OVR::SInt16; using OVR::UInt32; using OVR::SInt32; using OVR::UInt64; using OVR::SInt64; } // OVR::BaseTypes } // OVR //----------------------------------------------------------------------------------- // ***** Macro Definitions // // We define the following: // // OVR_BYTE_ORDER - Defined to either OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN // OVR_FORCE_INLINE - Forces inline expansion of function // OVR_ASM - Assembly language prefix // OVR_STR - Prefixes string with L"" if building unicode // // OVR_STDCALL - Use stdcall calling convention (Pascal arg order) // OVR_CDECL - Use cdecl calling convention (C argument order) // OVR_FASTCALL - Use fastcall calling convention (registers) // // Byte order constants, OVR_BYTE_ORDER is defined to be one of these. #define OVR_LITTLE_ENDIAN 1 #define OVR_BIG_ENDIAN 2 // Force inline substitute - goes before function declaration #if defined(OVR_CC_MSVC) # define OVR_FORCE_INLINE __forceinline #elif defined(OVR_CC_GNU) # define OVR_FORCE_INLINE __attribute__((always_inline)) inline #else # define OVR_FORCE_INLINE inline #endif // OVR_CC_MSVC #if defined(OVR_OS_WIN32) // ***** Win32 // Byte order #define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN // Calling convention - goes after function return type but before function name #ifdef __cplusplus_cli # define OVR_FASTCALL __stdcall #else # define OVR_FASTCALL __fastcall #endif #define OVR_STDCALL __stdcall #define OVR_CDECL __cdecl // Assembly macros #if defined(OVR_CC_MSVC) # define OVR_ASM _asm #else # define OVR_ASM asm #endif // (OVR_CC_MSVC) #ifdef UNICODE # define OVR_STR(str) L##str #else # define OVR_STR(str) str #endif // UNICODE #else // **** Standard systems #if (defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN))|| \ (defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)) # define OVR_BYTE_ORDER OVR_BIG_ENDIAN #elif (defined(__ARMEB__) || defined(OVR_CPU_PPC) || defined(OVR_CPU_PPC64)) # define OVR_BYTE_ORDER OVR_BIG_ENDIAN #else # define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN #endif // Assembly macros #define OVR_ASM __asm__ #define OVR_ASM_PROC(procname) OVR_ASM #define OVR_ASM_END OVR_ASM // Calling convention - goes after function return type but before function name #define OVR_FASTCALL #define OVR_STDCALL #define OVR_CDECL #endif // defined(OVR_OS_WIN32) //----------------------------------------------------------------------------------- // ***** OVR_DEBUG_BREAK, OVR_ASSERT // // If not in debug build, macros do nothing #ifndef OVR_BUILD_DEBUG # define OVR_DEBUG_BREAK ((void)0) # define OVR_ASSERT(p) ((void)0) #else // Microsoft Win32 specific debugging support #if defined(OVR_OS_WIN32) # ifdef OVR_CPU_X86 # if defined(__cplusplus_cli) # define OVR_DEBUG_BREAK do { __debugbreak(); } while(0) # elif defined(OVR_CC_GNU) # define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0) # else # define OVR_DEBUG_BREAK do { OVR_ASM int 3 } while (0) # endif # else # define OVR_DEBUG_BREAK do { __debugbreak(); } while(0) # endif // Unix specific debugging support #elif defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64) # define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0) #else # define OVR_DEBUG_BREAK do { *((int *) 0) = 1; } while(0) #endif // This will cause compiler breakpoint #define OVR_ASSERT(p) do { if (!(p)) { OVR_DEBUG_BREAK; } } while(0) #endif // OVR_BUILD_DEBUG // Compile-time assert; produces compiler error if condition is false #define OVR_COMPILER_ASSERT(x) { int zero = 0; switch(zero) {case 0: case x:;} } //----------------------------------------------------------------------------------- // ***** OVR_UNUSED - Unused Argument handling // Macro to quiet compiler warnings about unused parameters/variables. #if defined(OVR_CC_GNU) # define OVR_UNUSED(a) do {__typeof__ (&a) __attribute__ ((unused)) __tmp = &a; } while(0) #else # define OVR_UNUSED(a) (a) #endif #define OVR_UNUSED1(a1) OVR_UNUSED(a1) #define OVR_UNUSED2(a1,a2) OVR_UNUSED(a1); OVR_UNUSED(a2) #define OVR_UNUSED3(a1,a2,a3) OVR_UNUSED2(a1,a2); OVR_UNUSED(a3) #define OVR_UNUSED4(a1,a2,a3,a4) OVR_UNUSED3(a1,a2,a3); OVR_UNUSED(a4) #define OVR_UNUSED5(a1,a2,a3,a4,a5) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED(a5) #define OVR_UNUSED6(a1,a2,a3,a4,a5,a6) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED2(a5,a6) #define OVR_UNUSED7(a1,a2,a3,a4,a5,a6,a7) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED3(a5,a6,a7) #define OVR_UNUSED8(a1,a2,a3,a4,a5,a6,a7,a8) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED4(a5,a6,a7,a8) #define OVR_UNUSED9(a1,a2,a3,a4,a5,a6,a7,a8,a9) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED5(a5,a6,a7,a8,a9) //----------------------------------------------------------------------------------- // ***** Configuration Macros // SF Build type #ifdef OVR_BUILD_DEBUG # define OVR_BUILD_STRING "Debug" #else # define OVR_BUILD_STRING "Release" #endif //// Enables SF Debugging information //# define OVR_BUILD_DEBUG // OVR_DEBUG_STATEMENT injects a statement only in debug builds. // OVR_DEBUG_SELECT injects first argument in debug builds, second argument otherwise. #ifdef OVR_BUILD_DEBUG #define OVR_DEBUG_STATEMENT(s) s #define OVR_DEBUG_SELECT(d, nd) d #else #define OVR_DEBUG_STATEMENT(s) #define OVR_DEBUG_SELECT(d, nd) nd #endif #define OVR_ENABLE_THREADS // // Prevents OVR from defining new within // type macros, so developers can override // new using the #define new new(...) trick // - used with OVR_DEFINE_NEW macro //# define OVR_BUILD_DEFINE_NEW // #endif // OVR_Types_h \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_UTF8Util.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_UTF8Util.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************** Filename : OVR_UTF8Util.cpp Content : UTF8 Unicode character encoding/decoding support Created : September 19, 2012 Notes : Notes : Much useful info at "UTF-8 and Unicode FAQ" http://www.cl.cam.ac.uk/~mgk25/unicode.html Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_UTF8Util.h" namespace OVR { namespace UTF8Util { SPInt OVR_STDCALL GetLength(const char* buf, SPInt buflen) { const char* p = buf; SPInt length = 0; if (buflen != -1) { while (p - buf < buflen) { // We should be able to have ASStrings with 0 in the middle. UTF8Util::DecodeNextChar_Advance0(&p); length++; } } else { while (UTF8Util::DecodeNextChar_Advance0(&p)) length++; } return length; } UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length) { const char* buf = putf8str; UInt32 c = 0; if (length != -1) { while (buf - putf8str < length) { c = UTF8Util::DecodeNextChar_Advance0(&buf); if (index == 0) return c; index--; } return c; } do { c = UTF8Util::DecodeNextChar_Advance0(&buf); index--; if (c == 0) { // We've hit the end of the string; don't go further. OVR_ASSERT(index == 0); return c; } } while (index >= 0); return c; } SPInt OVR_STDCALL GetByteIndex(SPInt index, const char *putf8str, SPInt length) { const char* buf = putf8str; if (length != -1) { while ((buf - putf8str) < length && index > 0) { UTF8Util::DecodeNextChar_Advance0(&buf); index--; } return buf-putf8str; } while (index > 0) { UInt32 c = UTF8Util::DecodeNextChar_Advance0(&buf); index--; if (c == 0) return buf-putf8str; }; return buf-putf8str; } int OVR_STDCALL GetEncodeCharSize(UInt32 ucs_character) { if (ucs_character <= 0x7F) return 1; else if (ucs_character <= 0x7FF) return 2; else if (ucs_character <= 0xFFFF) return 3; else if (ucs_character <= 0x1FFFFF) return 4; else if (ucs_character <= 0x3FFFFFF) return 5; else if (ucs_character <= 0x7FFFFFFF) return 6; else return 0; } UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer) { UInt32 uc; char c; // Security considerations: // // Changed, this is now only the case for DecodeNextChar: // - If we hit a zero byte, we want to return 0 without stepping // the buffer pointer past the 0. th // // If we hit an "overlong sequence"; i.e. a character encoded // in a longer multibyte string than is necessary, then we // need to discard the character. This is so attackers can't // disguise dangerous characters or character sequences -- // there is only one valid encoding for each character. // // If we decode characters { 0xD800 .. 0xDFFF } or { 0xFFFE, // 0xFFFF } then we ignore them; they are not valid in UTF-8. // This isn't actually an invalid character; it's a valid char that // looks like an inverted question mark. #define INVALID_CHAR 0x0FFFD #define FIRST_BYTE(mask, shift) \ uc = (c & (mask)) << (shift); #define NEXT_BYTE(shift) \ c = **putf8Buffer; \ if (c == 0) return 0; /* end of buffer, do not advance */ \ if ((c & 0xC0) != 0x80) return INVALID_CHAR; /* standard check */ \ (*putf8Buffer)++; \ uc |= (c & 0x3F) << shift; c = **putf8Buffer; (*putf8Buffer)++; if (c == 0) return 0; // End of buffer. if ((c & 0x80) == 0) return (UInt32) c; // Conventional 7-bit ASCII. // Multi-byte sequences. if ((c & 0xE0) == 0xC0) { // Two-byte sequence. FIRST_BYTE(0x1F, 6); NEXT_BYTE(0); if (uc < 0x80) return INVALID_CHAR; // overlong return uc; } else if ((c & 0xF0) == 0xE0) { // Three-byte sequence. FIRST_BYTE(0x0F, 12); NEXT_BYTE(6); NEXT_BYTE(0); if (uc < 0x800) return INVALID_CHAR; // overlong // Not valid ISO 10646, but Flash requires these to work // see AS3 test e15_5_3_2_3 for String.fromCharCode().charCodeAt(0) // if (uc >= 0x0D800 && uc <= 0x0DFFF) return INVALID_CHAR; // if (uc == 0x0FFFE || uc == 0x0FFFF) return INVALID_CHAR; // not valid ISO 10646 return uc; } else if ((c & 0xF8) == 0xF0) { // Four-byte sequence. FIRST_BYTE(0x07, 18); NEXT_BYTE(12); NEXT_BYTE(6); NEXT_BYTE(0); if (uc < 0x010000) return INVALID_CHAR; // overlong return uc; } else if ((c & 0xFC) == 0xF8) { // Five-byte sequence. FIRST_BYTE(0x03, 24); NEXT_BYTE(18); NEXT_BYTE(12); NEXT_BYTE(6); NEXT_BYTE(0); if (uc < 0x0200000) return INVALID_CHAR; // overlong return uc; } else if ((c & 0xFE) == 0xFC) { // Six-byte sequence. FIRST_BYTE(0x01, 30); NEXT_BYTE(24); NEXT_BYTE(18); NEXT_BYTE(12); NEXT_BYTE(6); NEXT_BYTE(0); if (uc < 0x04000000) return INVALID_CHAR; // overlong return uc; } else { // Invalid. return INVALID_CHAR; } } void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* pindex, UInt32 ucs_character) { if (ucs_character <= 0x7F) { // Plain single-byte ASCII. pbuffer[(*pindex)++] = (char) ucs_character; } else if (ucs_character <= 0x7FF) { // Two bytes. pbuffer[(*pindex)++] = 0xC0 | (char)(ucs_character >> 6); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else if (ucs_character <= 0xFFFF) { // Three bytes. pbuffer[(*pindex)++] = 0xE0 | (char)(ucs_character >> 12); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else if (ucs_character <= 0x1FFFFF) { // Four bytes. pbuffer[(*pindex)++] = 0xF0 | (char)(ucs_character >> 18); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else if (ucs_character <= 0x3FFFFFF) { // Five bytes. pbuffer[(*pindex)++] = 0xF8 | (char)(ucs_character >> 24); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else if (ucs_character <= 0x7FFFFFFF) { // Six bytes. pbuffer[(*pindex)++] = 0xFC | (char)(ucs_character >> 30); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 24) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else { // Invalid char; don't encode anything. } } SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length) { SPInt len = 0; if (length != -1) for (int i = 0; i < length; i++) { len += GetEncodeCharSize(pchar[i]); } else for (int i = 0;; i++) { if (pchar[i] == 0) return len; len += GetEncodeCharSize(pchar[i]); } return len; } void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length) { SPInt ofs = 0; if (length != -1) { for (int i = 0; i < length; i++) { EncodeChar(pbuff, &ofs, pchar[i]); } } else { for (int i = 0;; i++) { if (pchar[i] == 0) break; EncodeChar(pbuff, &ofs, pchar[i]); } } pbuff[ofs] = 0; } UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen) { wchar_t *pbegin = pbuff; if (bytesLen == -1) { while (1) { UInt32 ch = DecodeNextChar_Advance0(&putf8str); if (ch == 0) break; else if (ch >= 0xFFFF) ch = 0xFFFD; *pbuff++ = wchar_t(ch); } } else { const char* p = putf8str; while ((p - putf8str) < bytesLen) { UInt32 ch = DecodeNextChar_Advance0(&p); if (ch >= 0xFFFF) ch = 0xFFFD; *pbuff++ = wchar_t(ch); } } *pbuff = 0; return pbuff - pbegin; } #ifdef UTF8_UNIT_TEST // Compile this test case with something like: // // gcc utf8.cpp -g -I.. -DUTF8_UNIT_TEST -lstdc++ -o utf8_test // // or // // cl utf8.cpp -Zi -Od -DUTF8_UNIT_TEST -I.. // // If possible, try running the test program with the first arg // pointing at the file: // // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt // // and examine the results by eye to make sure they are acceptable to // you. #include "base/utility.h" #include bool check_equal(const char* utf8_in, const UInt32* ucs_in) { for (;;) { UInt32 next_ucs = *ucs_in++; UInt32 next_ucs_from_utf8 = utf8::decode_next_unicode_character(&utf8_in); if (next_ucs != next_ucs_from_utf8) { return false; } if (next_ucs == 0) { OVR_ASSERT(next_ucs_from_utf8 == 0); break; } } return true; } void log_ascii(const char* line) { for (;;) { unsigned char c = (unsigned char) *line++; if (c == 0) { // End of line. return; } else if (c != '\n' && (c < 32 || c > 127)) { // Non-printable as plain ASCII. printf("<0x%02X>", (int) c); } else { printf("%c", c); } } } void log_ucs(const UInt32* line) { for (;;) { UInt32 uc = *line++; if (uc == 0) { // End of line. return; } else if (uc != '\n' && (uc < 32 || uc > 127)) { // Non-printable as plain ASCII. printf("", uc); } else { printf("%c", (char) uc); } } } // Simple canned test. int main(int argc, const char* argv[]) { { const char* test8 = "Ignacio Castaño"; const UInt32 test32[] = { 0x49, 0x67, 0x6E, 0x61, 0x63, 0x69, 0x6F, 0x20, 0x43, 0x61, 0x73, 0x74, 0x61, 0xF1, 0x6F, 0x00 }; OVR_ASSERT(check_equal(test8, test32)); } // If user passed an arg, try reading the file as UTF-8 encoded text. if (argc > 1) { const char* filename = argv[1]; FILE* fp = fopen(filename, "rb"); if (fp == NULL) { printf("Can't open file '%s'\n", filename); return 1; } // Read lines from the file, encode/decode them, and highlight discrepancies. const int LINE_SIZE = 200; // max line size char line_buffer_utf8[LINE_SIZE]; char reencoded_utf8[6 * LINE_SIZE]; UInt32 line_buffer_ucs[LINE_SIZE]; int byte_counter = 0; for (;;) { int c = fgetc(fp); if (c == EOF) { // Done. break; } line_buffer_utf8[byte_counter++] = c; if (c == '\n' || byte_counter >= LINE_SIZE - 2) { // End of line. Process the line. line_buffer_utf8[byte_counter++] = 0; // terminate. // Decode into UCS. const char* p = line_buffer_utf8; UInt32* q = line_buffer_ucs; for (;;) { UInt32 uc = UTF8Util::DecodeNextChar(&p); *q++ = uc; OVR_ASSERT(q < line_buffer_ucs + LINE_SIZE); OVR_ASSERT(p < line_buffer_utf8 + LINE_SIZE); if (uc == 0) break; } // Encode back into UTF-8. q = line_buffer_ucs; int index = 0; for (;;) { UInt32 uc = *q++; OVR_ASSERT(index < LINE_SIZE * 6 - 6); int last_index = index; UTF8Util::EncodeChar(reencoded_utf8, &index, uc); OVR_ASSERT(index <= last_index + 6); if (uc == 0) break; } // This can be useful for debugging. #if 0 // Show the UCS and the re-encoded UTF-8. log_ucs(line_buffer_ucs); log_ascii(reencoded_utf8); #endif // 0 OVR_ASSERT(check_equal(line_buffer_utf8, line_buffer_ucs)); OVR_ASSERT(check_equal(reencoded_utf8, line_buffer_ucs)); // Start next line. byte_counter = 0; } } fclose(fp); } return 0; } #endif // UTF8_UNIT_TEST }} // namespace UTF8Util::OVR \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Kernel/OVR_UTF8Util.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Kernel/OVR_UTF8Util.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_UTF8Util.h Content : UTF8 Unicode character encoding/decoding support Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_UTF8Util_h #define OVR_UTF8Util_h #include "OVR_Types.h" namespace OVR { namespace UTF8Util { //----------------------------------------------------------------------------------- // *** UTF8 string length and indexing. // Determines the length of UTF8 string in characters. // If source length is specified (in bytes), null 0 character is counted properly. SPInt OVR_STDCALL GetLength(const char* putf8str, SPInt length = -1); // Gets a decoded UTF8 character at index; you can access up to the index returned // by GetLength. 0 will be returned for out of bounds access. UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length = -1); // Converts UTF8 character index into byte offset. // -1 is returned if index was out of bounds. SPInt OVR_STDCALL GetByteIndex(SPInt index, const char* putf8str, SPInt length = -1); // *** 16-bit Unicode string Encoding/Decoding routines. // Determines the number of bytes necessary to encode a string. // Does not count the terminating 0 (null) character. SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length = -1); // Encodes a unicode (UCS-2 only) string into a buffer. The size of buffer must be at // least GetEncodeStringSize() + 1. void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length = -1); // Decode UTF8 into a wchar_t buffer. Must have GetLength()+1 characters available. // Characters over 0xFFFF are replaced with 0xFFFD. // Returns the length of resulting string (number of characters) UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen = -1); // *** Individual character Encoding/Decoding. // Determined the number of bytes necessary to encode a UCS character. int OVR_STDCALL GetEncodeCharSize(UInt32 ucsCharacter); // Encodes the given UCS character into the given UTF-8 buffer. // Writes the data starting at buffer[offset], and // increments offset by the number of bytes written. // May write up to 6 bytes, so make sure there's room in the buffer void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* poffset, UInt32 ucsCharacter); // Return the next Unicode character in the UTF-8 encoded buffer. // Invalid UTF-8 sequences produce a U+FFFD character as output. // Advances *utf8_buffer past the character returned. Pointer advance // occurs even if the terminating 0 character is hit, since that allows // strings with middle '\0' characters to be supported. UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer); // Safer version of DecodeNextChar, which doesn't advance pointer if // null character is hit. inline UInt32 DecodeNextChar(const char** putf8Buffer) { UInt32 ch = DecodeNextChar_Advance0(putf8Buffer); if (ch == 0) (*putf8Buffer)--; return ch; } }} // OVR::UTF8Util #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_Device.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_Device.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,619 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Device.h +Content : Definition of HMD-related Device interfaces +Created : September 21, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Device_h +#define OVR_Device_h + +#include "OVR_DeviceConstants.h" +#include "OVR_DeviceHandle.h" +#include "OVR_DeviceMessages.h" +#include "OVR_HIDDeviceBase.h" + +#include "Kernel/OVR_Atomic.h" +#include "Kernel/OVR_RefCount.h" +#include "Kernel/OVR_String.h" + +namespace OVR { + +// Declared externally +class Profile; +class ProfileManager; // << Should be renamed for consistency + +// Forward declarations +class SensorDevice; +class DeviceCommon; +class DeviceManager; + +// MessageHandler is a base class from which users derive to receive messages, +// its OnMessage handler will be called for messages once it is installed on +// a device. Same message handler can be installed on multiple devices. +class MessageHandler +{ + friend class MessageHandlerImpl; +public: + MessageHandler(); + virtual ~MessageHandler(); + + // Returns 'true' if handler is currently installed on any devices. + bool IsHandlerInstalled() const; + + // Should be called from derived class destructor to avoid handler + // being called after it exits. + void RemoveHandlerFromDevices(); + + // Returns a pointer to the internal lock object that is locked by a + // background thread while OnMessage() is called. + // This lock guaranteed to survive until ~MessageHandler. + Lock* GetHandlerLock() const; + + + virtual void OnMessage(const Message&) { } + + // Determines if handler supports a specific message type. Can + // be used to filter out entire message groups. The result + // returned by this function shouldn't change after handler creation. + virtual bool SupportsMessageType(MessageType) const { return true; } + +private: + UPInt Internal[4]; +}; + + +//------------------------------------------------------------------------------------- +// ***** DeviceBase + +// DeviceBase is the base class for all OVR Devices. It provides the following basic +// functionality: +// - Reports device type, manager, and associated parent (if any). +// - Supports installable message handlers, which are notified of device events. +// - Device objects are created through DeviceHandle::CreateDevice or more commonly +// through DeviceEnumerator<>::CreateDevice. +// - Created devices are reference counted, starting with RefCount of 1. +// - Device is resources are cleaned up when it is Released, although its handles +// may survive longer if referenced. + +class DeviceBase : public NewOverrideBase +{ + friend class DeviceHandle; + friend class DeviceManagerImpl; +public: + + // Enumerating DeviceBase enumerates all devices. + enum { EnumDeviceType = Device_All }; + + virtual ~DeviceBase() { } + virtual void AddRef(); + virtual void Release(); + + virtual DeviceBase* GetParent() const; + virtual DeviceManager* GetManager() const; + + virtual void SetMessageHandler(MessageHandler* handler); + virtual MessageHandler* GetMessageHandler() const; + + virtual DeviceType GetType() const; + virtual bool GetDeviceInfo(DeviceInfo* info) const; + + // returns the MessageHandler's lock + Lock* GetHandlerLock() const; +protected: + // Internal + virtual DeviceCommon* getDeviceCommon() const = 0; +}; + + +//------------------------------------------------------------------------------------- +// ***** DeviceInfo + +// DeviceInfo describes a device and its capabilities, obtained by calling +// GetDeviceInfo. This base class only contains device-independent functionality; +// users will normally use a derived HMDInfo or SensorInfo classes for more +// extensive device info. + +class DeviceInfo +{ +public: + DeviceInfo() : InfoClassType(Device_None), Type(Device_None), Version(0) + { ProductName[0] = Manufacturer[0] = 0; } + + enum { MaxNameLength = 32 }; + + // Type of device for which DeviceInfo is intended. + // This will be set to Device_HMD for HMDInfo structure, note that this may be + // different form the actual device type since (Device_None) is valid. + const DeviceType InfoClassType; + // Type of device this describes. This must be the same as InfoClassType when + // InfoClassType != Device_None. + DeviceType Type; + // Name string describing the product: "Oculus Rift DK1", etc. + char ProductName[MaxNameLength]; + char Manufacturer[MaxNameLength]; + unsigned Version; + +protected: + DeviceInfo(DeviceType type) : InfoClassType(type), Type(type), Version(0) + { ProductName[0] = Manufacturer[0] = 0; } + void operator = (const DeviceInfo&) { OVR_ASSERT(0); } // Assignment not allowed. +}; + + +//------------------------------------------------------------------------------------- +// DeviceEnumerationArgs provides device enumeration argumenrs for DeviceManager::EnumerateDevicesEx. +class DeviceEnumerationArgs +{ +public: + DeviceEnumerationArgs(DeviceType enumType, bool availableOnly) + : EnumType(enumType), AvailableOnly(availableOnly) + { } + + // Helper; returns true if args match our enumeration criteria. + bool MatchRule(DeviceType type, bool available) const + { + return ((EnumType == type) || (EnumType == Device_All)) && + (available || !AvailableOnly); + } + +protected: + DeviceType EnumType; + bool AvailableOnly; +}; + + +// DeviceEnumerator<> is used to enumerate and create devices of specified class, +// it is returned by calling MeviceManager::EnumerateDevices. Initially, the enumerator will +// refer to the first device of specified type. Additional devices can be accessed by +// calling Next(). + +template +class DeviceEnumerator : public DeviceHandle +{ + friend class DeviceManager; + friend class DeviceManagerImpl; +public: + DeviceEnumerator() + : DeviceHandle(), EnumArgs(Device_None, true) { } + + // Next advances enumeration to the next device that first criteria. + // Returns false if no more devices exist that match enumeration criteria. + bool Next() { return enumerateNext(EnumArgs); } + + // Creates an instance of the device referenced by enumerator; returns null + // if enumerator does not refer to a valid device or device is unavailable. + // If device was already created, the same object with incremented ref-count is returned. + T* CreateDevice() { return static_cast(DeviceHandle::CreateDevice()); } + +protected: + DeviceEnumerator(const DeviceHandle &dev, const DeviceEnumerationArgs& args) + : DeviceHandle(dev), EnumArgs(args) + { } + + DeviceEnumerationArgs EnumArgs; +}; + +//------------------------------------------------------------------------------------- +// ***** DeviceManager + +// DeviceManager maintains and provides access to devices supported by OVR, such as +// HMDs and sensors. A single instance of DeviceManager is normally created at +// program startup, allowing devices to be enumerated and created. DeviceManager is +// reference counted and is AddRefed by its created child devices, causing it to +// always be the last object that is released. +// +// Install MessageHandler on DeviceManager to detect when devices are inserted or removed. +// +// The following code will create the manager and its first available HMDDevice, +// and then release it when not needed: +// +// DeviceManager* manager = DeviceManager::Create(); +// HMDDevice* hmd = manager->EnumerateDevices().CreateDevice(); +// +// if (hmd) hmd->Release(); +// if (manager) manager->Release(); + + +class DeviceManager : public DeviceBase +{ +public: + + DeviceManager() + { } + + // DeviceBase implementation. + virtual DeviceType GetType() const { return Device_Manager; } + virtual DeviceManager* GetManager() const { return const_cast(this); } + + // Every DeviceManager has an associated profile manager, which us used to store + // user settings that may affect device behavior. + virtual ProfileManager* GetProfileManager() const = 0; + + + // EnumerateDevices enumerates all of the available devices of the specified class, + // returning an enumerator that references the first device. An empty enumerator is + // returned if no devices are available. The following APIs are exposed through + // DeviceEnumerator: + // DeviceEnumerator::GetType() - Check device type. Returns Device_None + // if no device was found/pointed to. + // DeviceEnumerator::GetDeviceInfo() - Get more information on device. + // DeviceEnumerator::CreateDevice() - Create an instance of device. + // DeviceEnumerator::Next() - Move onto next device. + template + DeviceEnumerator EnumerateDevices(bool availableOnly = true) + { + // TBD: A cleaner (but less efficient) alternative is though enumeratorFromHandle. + DeviceEnumerator<> e = EnumerateDevicesEx(DeviceEnumerationArgs((DeviceType)D::EnumDeviceType, availableOnly)); + return *reinterpret_cast*>(&e); + } + + // EnumerateDevicesEx provides internal implementation for device enumeration, enumerating + // devices based on dynamically specified DeviceType in DeviceEnumerationArgs. + // End users should call DeumerateDevices<>() instead. + virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args) = 0; + + // Creates a new DeviceManager. Only one instance of DeviceManager should be created at a time. + static DeviceManager* Create(); + + // Static constant for this device type, used in template cast type checks. + enum { EnumDeviceType = Device_Manager }; + + + + // Adds a device (DeviceCreateDesc*) into Devices. Returns NULL, + // if unsuccessful or device is already in the list. + virtual Ptr AddDevice_NeedsLock(const DeviceCreateDesc& createDesc) = 0; + +protected: + DeviceEnumerator<> enumeratorFromHandle(const DeviceHandle& h, const DeviceEnumerationArgs& args) + { return DeviceEnumerator<>(h, args); } + + DeviceManager* getThis() { return this; } +}; + + + +//------------------------------------------------------------------------------------- +// ***** HMDInfo + +// This structure describes various aspects of the HMD allowing us to configure rendering. +// +// Currently included data: +// - Physical screen dimensions, resolution, and eye distances. +// (some of these will be configurable with a tool in the future). +// These arguments allow us to properly setup projection across HMDs. +// - DisplayDeviceName for identifying HMD screen; system-specific interpretation. +// +// TBD: +// - Power on/ off? +// - Sensor rates and capabilities +// - Distortion radius/variables +// - Screen update frequency +// - Distortion needed flag +// - Update modes: +// Set update mode: Stereo (both sides together), mono (same in both eyes), +// Alternating, Alternating scan-lines. + +class HMDInfo : public DeviceInfo +{ +public: + // Size of the entire screen, in pixels. + unsigned HResolution, VResolution; + // Physical dimensions of the active screen in meters. Can be used to calculate + // projection center while considering IPD. + float HScreenSize, VScreenSize; + // Physical offset from the top of the screen to the eye center, in meters. + // This will usually, but not necessarily be half of VScreenSize. + float VScreenCenter; + // Distance from the eye to screen surface, in meters. + // Useful for calculating FOV and projection. + float EyeToScreenDistance; + // Distance between physical lens centers useful for calculating distortion center. + float LensSeparationDistance; + // Configured distance between the user's eye centers, in meters. Defaults to 0.064. + float InterpupillaryDistance; + + // Radial distortion correction coefficients. + // The distortion assumes that the input texture coordinates will be scaled + // by the following equation: + // uvResult = uvInput * (K0 + K1 * uvLength^2 + K2 * uvLength^4) + // Where uvInput is the UV vector from the center of distortion in direction + // of the mapped pixel, uvLength is the magnitude of that vector, and uvResult + // the corresponding location after distortion. + float DistortionK[4]; + + float ChromaAbCorrection[4]; + + // Desktop coordinate position of the screen (can be negative; may not be present on all platforms) + int DesktopX, DesktopY; + + // Windows: + // "\\\\.\\DISPLAY3", etc. Can be used in EnumDisplaySettings/CreateDC. + char DisplayDeviceName[32]; + + // MacOS: + long DisplayId; + + + HMDInfo() + : DeviceInfo(Device_HMD), + HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0), + VScreenCenter(0), EyeToScreenDistance(0), + LensSeparationDistance(0), InterpupillaryDistance(0), + DesktopX(0), DesktopY(0), DisplayId(0) + { + DisplayDeviceName[0] = 0; + memset(DistortionK, 0, sizeof(DistortionK)); + DistortionK[0] = 1; + ChromaAbCorrection[0] = ChromaAbCorrection[2] = 1; + ChromaAbCorrection[1] = ChromaAbCorrection[3] = 0; + } + + // Operator = copies local fields only (base class must be correct already) + void operator = (const HMDInfo& src) + { + HResolution = src.HResolution; + VResolution = src.VResolution; + HScreenSize = src.HScreenSize; + VScreenSize = src.VScreenSize; + VScreenCenter = src.VScreenCenter; + EyeToScreenDistance = src.EyeToScreenDistance; + LensSeparationDistance = src.LensSeparationDistance; + InterpupillaryDistance = src.InterpupillaryDistance; + DistortionK[0] = src.DistortionK[0]; + DistortionK[1] = src.DistortionK[1]; + DistortionK[2] = src.DistortionK[2]; + DistortionK[3] = src.DistortionK[3]; + ChromaAbCorrection[0] = src.ChromaAbCorrection[0]; + ChromaAbCorrection[1] = src.ChromaAbCorrection[1]; + ChromaAbCorrection[2] = src.ChromaAbCorrection[2]; + ChromaAbCorrection[3] = src.ChromaAbCorrection[3]; + DesktopX = src.DesktopX; + DesktopY = src.DesktopY; + memcpy(DisplayDeviceName, src.DisplayDeviceName, sizeof(DisplayDeviceName)); + DisplayId = src.DisplayId; + } + + bool IsSameDisplay(const HMDInfo& o) const + { + return DisplayId == o.DisplayId && + String::CompareNoCase(DisplayDeviceName, + o.DisplayDeviceName) == 0; + } + +}; + + +// HMDDevice represents an Oculus HMD device unit. An instance of this class +// is typically created from the DeviceManager. +// After HMD device is created, we its sensor data can be obtained by +// first creating a Sensor object and then. + +// TBD: +// - Configure Sensor +// - APIs to set On-Screen message, other states? + +class HMDDevice : public DeviceBase +{ +public: + HMDDevice() + { } + + // Static constant for this device type, used in template cast type checks. + enum { EnumDeviceType = Device_HMD }; + + virtual DeviceType GetType() const { return Device_HMD; } + + // Creates a sensor associated with this HMD. + virtual SensorDevice* GetSensor() = 0; + + + // Requests the currently used profile. This profile affects the + // settings reported by HMDInfo. + virtual Profile* GetProfile() const = 0; + // Obtains the currently used profile name. This is initialized to the default + // profile name, if any; it can then be changed per-device by SetProfileName. + virtual const char* GetProfileName() const = 0; + // Sets the profile user name, changing the data returned by GetProfileInfo. + virtual bool SetProfileName(const char* name) = 0; + + + // Disconnects from real HMD device. This HMDDevice remains as 'fake' HMD. + // SensorDevice ptr is used to restore the 'fake' HMD (can be NULL). + HMDDevice* Disconnect(SensorDevice*); + + // Returns 'true' if HMD device is a 'fake' HMD (was created this way or + // 'Disconnect' method was called). + bool IsDisconnected() const; +}; + + +//------------------------------------------------------------------------------------- +// ***** SensorRange & SensorInfo + +// SensorRange specifies maximum value ranges that SensorDevice hardware is configured +// to detect. Although this range doesn't affect the scale of MessageBodyFrame values, +// physical motions whose positive or negative magnitude is outside the specified range +// may get clamped or misreported. Setting lower values may result in higher precision +// tracking. +struct SensorRange +{ + SensorRange(float maxAcceleration = 0.0f, float maxRotationRate = 0.0f, + float maxMagneticField = 0.0f) + : MaxAcceleration(maxAcceleration), MaxRotationRate(maxRotationRate), + MaxMagneticField(maxMagneticField) + { } + + // Maximum detected acceleration in m/s^2. Up to 8*G equivalent support guaranteed, + // where G is ~9.81 m/s^2. + // Oculus DK1 HW has thresholds near: 2, 4 (default), 8, 16 G. + float MaxAcceleration; + // Maximum detected angular velocity in rad/s. Up to 8*Pi support guaranteed. + // Oculus DK1 HW thresholds near: 1, 2, 4, 8 Pi (default). + float MaxRotationRate; + // Maximum detectable Magnetic field strength in Gauss. Up to 2.5 Gauss support guaranteed. + // Oculus DK1 HW thresholds near: 0.88, 1.3, 1.9, 2.5 gauss. + float MaxMagneticField; +}; + +// SensorInfo describes capabilities of the sensor device. +class SensorInfo : public DeviceInfo +{ +public: + SensorInfo() : DeviceInfo(Device_Sensor), VendorId(0), ProductId(0) + { + SerialNumber[0] = 0; + } + + // HID Vendor and ProductId of the device. + UInt16 VendorId; + UInt16 ProductId; + // MaxRanges report maximum sensor range values supported by HW. + SensorRange MaxRanges; + // Sensor (and display) serial number. + char SerialNumber[20]; + +private: + void operator = (const SensorInfo&) { OVR_ASSERT(0); } // Assignment not allowed. +}; + + +//------------------------------------------------------------------------------------- +// ***** SensorDevice + +// SensorDevice is an interface to sensor data. +// Install a MessageHandler of SensorDevice instance to receive MessageBodyFrame +// notifications. +// +// TBD: Add Polling API? More HID interfaces? + +class SensorDevice : public HIDDeviceBase, public DeviceBase +{ +public: + SensorDevice() + { } + + // Static constant for this device type, used in template cast type checks. + enum { EnumDeviceType = Device_Sensor }; + + virtual DeviceType GetType() const { return Device_Sensor; } + + + // CoordinateFrame defines whether messages come in the coordinate frame + // of the sensor device or HMD, which has a different internal sensor. + // Sensors obtained form the HMD will automatically use HMD coordinates. + enum CoordinateFrame + { + Coord_Sensor = 0, + Coord_HMD = 1 + }; + + virtual void SetCoordinateFrame(CoordinateFrame coordframe) = 0; + virtual CoordinateFrame GetCoordinateFrame() const = 0; + + // Sets report rate (in Hz) of MessageBodyFrame messages (delivered through MessageHandler::OnMessage call). + // Currently supported maximum rate is 1000Hz. If the rate is set to 500 or 333 Hz then OnMessage will be + // called twice or thrice at the same 'tick'. + // If the rate is < 333 then the OnMessage / MessageBodyFrame will be called three + // times for each 'tick': the first call will contain averaged values, the second + // and third calls will provide with most recent two recorded samples. + virtual void SetReportRate(unsigned rateHz) = 0; + // Returns currently set report rate, in Hz. If 0 - error occurred. + // Note, this value may be different from the one provided for SetReportRate. The return + // value will contain the actual rate. + virtual unsigned GetReportRate() const = 0; + + // Sets maximum range settings for the sensor described by SensorRange. + // The function will fail if you try to pass values outside Maximum supported + // by the HW, as described by SensorInfo. + // Pass waitFlag == true to wait for command completion. For waitFlag == true, + // returns true if the range was applied successfully (no HW error). + // For waitFlag = false, return 'true' means that command was enqueued successfully. + virtual bool SetRange(const SensorRange& range, bool waitFlag = false) = 0; + + // Return the current sensor range settings for the device. These may not exactly + // match the values applied through SetRange. + virtual void GetRange(SensorRange* range) const = 0; +}; + +//------------------------------------------------------------------------------------- +// ***** LatencyTestConfiguration +// LatencyTestConfiguration specifies configuration information for the Oculus Latency Tester device. +struct LatencyTestConfiguration +{ + LatencyTestConfiguration(const Color& threshold, bool sendSamples = false) + : Threshold(threshold), SendSamples(sendSamples) + { + } + + // The color threshold for triggering a detected display change. + Color Threshold; + // Flag specifying whether we wish to receive a stream of color values from the sensor. + bool SendSamples; +}; + +//------------------------------------------------------------------------------------- +// ***** LatencyTestDisplay +// LatencyTestDisplay sets the mode and contents of the Latency Tester LED display. +// See the 'Latency Tester Specification' document for more details. +struct LatencyTestDisplay +{ + LatencyTestDisplay(UByte mode, UInt32 value) + : Mode(mode), Value(value) + { + } + + UByte Mode; // The display mode that we wish to select. + UInt32 Value; // The value to display. +}; + +//------------------------------------------------------------------------------------- +// ***** LatencyTestDevice + +// LatencyTestDevice provides an interface to the Oculus Latency Tester which is used to test 'motion to photon' latency. +class LatencyTestDevice : public HIDDeviceBase, public DeviceBase +{ +public: + LatencyTestDevice() + { } + + // Static constant for this device type, used in template cast type checks. + enum { EnumDeviceType = Device_LatencyTester }; + + virtual DeviceType GetType() const { return Device_LatencyTester; } + + // Specifies configuration information including the threshold for triggering a detected color change, + // and a flag to enable a stream of sensor values (typically used for debugging). + virtual bool SetConfiguration(const LatencyTestConfiguration& configuration, bool waitFlag = false) = 0; + + // Get configuration information from device. + virtual bool GetConfiguration(LatencyTestConfiguration* configuration) = 0; + + // Used to calibrate the latency tester at the start of a test. Display the specified color on the screen + // beneath the latency tester and then call this method. Calibration information is lost + // when power is removed from the device. + virtual bool SetCalibrate(const Color& calibrationColor, bool waitFlag = false) = 0; + + // Triggers the start of a measurement. This starts the millisecond timer on the device and + // causes it to respond with the 'MessageLatencyTestStarted' message. + virtual bool SetStartTest(const Color& targetColor, bool waitFlag = false) = 0; + + // Used to set the value displayed on the LED display panel. + virtual bool SetDisplay(const LatencyTestDisplay& display, bool waitFlag = false) = 0; + + virtual DeviceBase* GetDevice() { return this; } +}; + +} // namespace OVR + +#endif diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_DeviceConstants.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_DeviceConstants.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,38 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_DeviceConstants.h +Content : Device constants +Created : February 5, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_DeviceConstants_h +#define OVR_DeviceConstants_h + +namespace OVR { + + +//------------------------------------------------------------------------------------- +// Different device types supported by OVR; this type is reported by DeviceBase::GetType. +// +enum DeviceType +{ + Device_None = 0, + Device_Manager = 1, + Device_HMD = 2, + Device_Sensor = 3, + Device_LatencyTester = 4, + Device_All = 0xFF // Set for enumeration only, to enumerate all device types. +}; + +} // namespace OVR + +#endif diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_DeviceHandle.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_DeviceHandle.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,174 @@ +/************************************************************************************ + +Filename : OVR_DeviceHandle.cpp +Content : Implementation of device handle class +Created : February 5, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_DeviceHandle.h" + +#include "OVR_DeviceImpl.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +// ***** DeviceHandle + +DeviceHandle::DeviceHandle(DeviceCreateDesc* impl) : pImpl(impl) +{ + if (pImpl) + pImpl->AddRef(); +} + +DeviceHandle::DeviceHandle(const DeviceHandle& src) : pImpl(src.pImpl) +{ + if (pImpl) + pImpl->AddRef(); +} + +DeviceHandle::~DeviceHandle() +{ + if (pImpl) + pImpl->Release(); +} + +void DeviceHandle::operator = (const DeviceHandle& src) +{ + if (src.pImpl) + src.pImpl->AddRef(); + if (pImpl) + pImpl->Release(); + pImpl = src.pImpl; +} + +DeviceBase* DeviceHandle::GetDevice_AddRef() const +{ + if (pImpl && pImpl->pDevice) + { + pImpl->pDevice->AddRef(); + return pImpl->pDevice; + } + return NULL; +} + +// Returns true, if the handle contains the same device ptr +// as specified in the parameter. +bool DeviceHandle::IsDevice(DeviceBase* pdev) const +{ + return (pdev && pImpl && pImpl->pDevice) ? + pImpl->pDevice == pdev : false; +} + +DeviceType DeviceHandle::GetType() const +{ + return pImpl ? pImpl->Type : Device_None; +} + +bool DeviceHandle::GetDeviceInfo(DeviceInfo* info) const +{ + return pImpl ? pImpl->GetDeviceInfo(info) : false; +} +bool DeviceHandle::IsAvailable() const +{ + // This isn't "atomically safe", but the function only returns the + // recent state that may change. + return pImpl ? (pImpl->Enumerated && pImpl->pLock->pManager) : false; +} + +bool DeviceHandle::IsCreated() const +{ + return pImpl ? (pImpl->pDevice != 0) : false; +} + +DeviceBase* DeviceHandle::CreateDevice() +{ + if (!pImpl) + return 0; + + DeviceBase* device = 0; + Ptr manager= 0; + + // Since both manager and device pointers can only be destroyed during a lock, + // hold it while checking for availability. + // AddRef to manager so that it doesn't get released on us. + { + Lock::Locker deviceLockScope(pImpl->GetLock()); + + if (pImpl->pDevice) + { + pImpl->pDevice->AddRef(); + return pImpl->pDevice; + } + manager = pImpl->GetManagerImpl(); + } + + if (manager) + { + if (manager->GetThreadId() != OVR::GetCurrentThreadId()) + { + // Queue up a CreateDevice request. This fills in '&device' with AddRefed value, + // or keep it at null. + manager->GetThreadQueue()->PushCallAndWaitResult( + manager.GetPtr(), &DeviceManagerImpl::CreateDevice_MgrThread, + &device, pImpl, (DeviceBase*)0); + } + else + device = manager->CreateDevice_MgrThread(pImpl, (DeviceBase*)0); + } + return device; +} + +void DeviceHandle::Clear() +{ + if (pImpl) + { + pImpl->Release(); + pImpl = 0; + } +} + +bool DeviceHandle::enumerateNext(const DeviceEnumerationArgs& args) +{ + if (GetType() == Device_None) + return false; + + Ptr managerKeepAlive; + Lock::Locker lockScope(pImpl->GetLock()); + + DeviceCreateDesc* next = pImpl; + // If manager was destroyed, we get removed from the list. + if (!pImpl->pNext) + return false; + + managerKeepAlive = next->GetManagerImpl(); + OVR_ASSERT(managerKeepAlive); + + do { + next = next->pNext; + + if (managerKeepAlive->Devices.IsNull(next)) + { + pImpl->Release(); + pImpl = 0; + return false; + } + + } while(!args.MatchRule(next->Type, next->Enumerated)); + + next->AddRef(); + pImpl->Release(); + pImpl = next; + + return true; +} + +} // namespace OVR + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_DeviceHandle.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_DeviceHandle.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,97 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_DeviceHandle.h +Content : Handle to a device that was enumerated +Created : February 5, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_DeviceHandle_h +#define OVR_DeviceHandle_h + +#include "OVR_DeviceConstants.h" + +namespace OVR { + +class DeviceBase; +class DeviceInfo; + +// Internal +class DeviceCreateDesc; +class DeviceEnumerationArgs; + + +//------------------------------------------------------------------------------------- +// ***** DeviceHandle + +// DeviceHandle references a specific device that was enumerated; it can be assigned +// directly from DeviceEnumerator. +// +// Devices represented by DeviceHandle are not necessarily created or available. +// A device may become unavailable if, for example, it its unplugged. If the device +// is available, it can be created by calling CreateDevice. +// + +class DeviceHandle +{ + friend class DeviceManager; + friend class DeviceManagerImpl; + template friend class HIDDeviceImpl; + +public: + DeviceHandle() : pImpl(0) { } + DeviceHandle(const DeviceHandle& src); + ~DeviceHandle(); + + void operator = (const DeviceHandle& src); + + bool operator == (const DeviceHandle& other) const { return pImpl == other.pImpl; } + bool operator != (const DeviceHandle& other) const { return pImpl != other.pImpl; } + + // operator bool() returns true if Handle/Enumerator points to a valid device. + operator bool () const { return GetType() != Device_None; } + + // Returns existing device, or NULL if !IsCreated. The returned ptr is + // addref-ed. + DeviceBase* GetDevice_AddRef() const; + DeviceType GetType() const; + bool GetDeviceInfo(DeviceInfo* info) const; + bool IsAvailable() const; + bool IsCreated() const; + // Returns true, if the handle contains the same device ptr + // as specified in the parameter. + bool IsDevice(DeviceBase*) const; + + // Creates a device, or returns AddRefed pointer if one is already created. + // New devices start out with RefCount of 1. + DeviceBase* CreateDevice(); + + // Creates a device, or returns AddRefed pointer if one is already created. + // New devices start out with RefCount of 1. DeviceT is used to cast the + // DeviceBase* to a concreete type. + template + DeviceT* CreateDeviceTyped() const + { + return static_cast(DeviceHandle(*this).CreateDevice()); + } + + // Resets the device handle to uninitialized state. + void Clear(); + +protected: + explicit DeviceHandle(DeviceCreateDesc* impl); + bool enumerateNext(const DeviceEnumerationArgs& args); + DeviceCreateDesc* pImpl; +}; + +} // namespace OVR + +#endif diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_DeviceImpl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_DeviceImpl.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,790 @@ +/************************************************************************************ + +Filename : OVR_DeviceImpl.h +Content : Partial back-end independent implementation of Device interfaces +Created : October 10, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_DeviceImpl.h" +#include "Kernel/OVR_Atomic.h" +#include "Kernel/OVR_Log.h" +#include "Kernel/OVR_System.h" + +#include "OVR_DeviceImpl.h" +#include "OVR_SensorImpl.h" +#include "OVR_Profile.h" + +namespace OVR { + + +//------------------------------------------------------------------------------------- +// ***** SharedLock + +// This is a general purpose globally shared Lock implementation that should probably be +// moved to Kernel. +// May in theory busy spin-wait if we hit contention on first lock creation, +// but this shouldn't matter in practice since Lock* should be cached. + + +enum { LockInitMarker = 0xFFFFFFFF }; + +Lock* SharedLock::GetLockAddRef() +{ + int oldUseCount; + + do { + oldUseCount = UseCount; + if (oldUseCount == LockInitMarker) + continue; + + if (oldUseCount == 0) + { + // Initialize marker + if (AtomicOps::CompareAndSet_Sync(&UseCount, 0, LockInitMarker)) + { + Construct(Buffer); + do { } + while (!AtomicOps::CompareAndSet_Sync(&UseCount, LockInitMarker, 1)); + return toLock(); + } + continue; + } + + } while (!AtomicOps::CompareAndSet_NoSync(&UseCount, oldUseCount, oldUseCount + 1)); + + return toLock(); +} + +void SharedLock::ReleaseLock(Lock* plock) +{ + OVR_UNUSED(plock); + OVR_ASSERT(plock == toLock()); + + int oldUseCount; + + do { + oldUseCount = UseCount; + OVR_ASSERT(oldUseCount != LockInitMarker); + + if (oldUseCount == 1) + { + // Initialize marker + if (AtomicOps::CompareAndSet_Sync(&UseCount, 1, LockInitMarker)) + { + Destruct(toLock()); + + do { } + while (!AtomicOps::CompareAndSet_Sync(&UseCount, LockInitMarker, 0)); + + return; + } + continue; + } + + } while (!AtomicOps::CompareAndSet_NoSync(&UseCount, oldUseCount, oldUseCount - 1)); +} + + + +//------------------------------------------------------------------------------------- +// ***** MessageHandler + +// Threading notes: +// The OnMessage() handler and SetMessageHandler are currently synchronized +// through a separately stored shared Lock object to avoid calling the handler +// from background thread while it's being removed. + +static SharedLock MessageHandlerSharedLock; + + +class MessageHandlerImpl +{ +public: + MessageHandlerImpl() + : pLock(MessageHandlerSharedLock.GetLockAddRef()) + { + } + ~MessageHandlerImpl() + { + MessageHandlerSharedLock.ReleaseLock(pLock); + pLock = 0; + } + + static MessageHandlerImpl* FromHandler(MessageHandler* handler) + { return (MessageHandlerImpl*)&handler->Internal; } + static const MessageHandlerImpl* FromHandler(const MessageHandler* handler) + { return (const MessageHandlerImpl*)&handler->Internal; } + + // This lock is held while calling a handler and when we are applied/ + // removed from a device. + Lock* pLock; + // List of device we are applied to. + List UseList; +}; + + +MessageHandlerRef::MessageHandlerRef(DeviceBase* device) + : pLock(MessageHandlerSharedLock.GetLockAddRef()), pDevice(device), pHandler(0) +{ +} + +MessageHandlerRef::~MessageHandlerRef() +{ + { + Lock::Locker lockScope(pLock); + if (pHandler) + { + pHandler = 0; + RemoveNode(); + } + } + MessageHandlerSharedLock.ReleaseLock(pLock); + pLock = 0; +} + +void MessageHandlerRef::SetHandler(MessageHandler* handler) +{ + OVR_ASSERT(!handler || + MessageHandlerImpl::FromHandler(handler)->pLock == pLock); + Lock::Locker lockScope(pLock); + SetHandler_NTS(handler); +} + +void MessageHandlerRef::SetHandler_NTS(MessageHandler* handler) +{ + if (pHandler != handler) + { + if (pHandler) + RemoveNode(); + pHandler = handler; + + if (handler) + { + MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(handler); + handlerImpl->UseList.PushBack(this); + } + // TBD: Call notifier on device? + } +} + + +MessageHandler::MessageHandler() +{ + OVR_COMPILER_ASSERT(sizeof(Internal) > sizeof(MessageHandlerImpl)); + Construct(Internal); +} + +MessageHandler::~MessageHandler() +{ + MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); + { + Lock::Locker lockedScope(handlerImpl->pLock); + OVR_ASSERT_LOG(handlerImpl->UseList.IsEmpty(), + ("~MessageHandler %p - Handler still active; call RemoveHandlerFromDevices", this)); + } + + Destruct(handlerImpl); +} + +bool MessageHandler::IsHandlerInstalled() const +{ + const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); + Lock::Locker lockedScope(handlerImpl->pLock); + return handlerImpl->UseList.IsEmpty() != true; +} + + +void MessageHandler::RemoveHandlerFromDevices() +{ + MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); + Lock::Locker lockedScope(handlerImpl->pLock); + + while(!handlerImpl->UseList.IsEmpty()) + { + MessageHandlerRef* use = handlerImpl->UseList.GetFirst(); + use->SetHandler_NTS(0); + } +} + +Lock* MessageHandler::GetHandlerLock() const +{ + const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); + return handlerImpl->pLock; +} + + +//------------------------------------------------------------------------------------- +// ***** DeviceBase + + +// Delegate relevant implementation to DeviceRectord to avoid re-implementation in +// every derived Device. +void DeviceBase::AddRef() +{ + getDeviceCommon()->DeviceAddRef(); +} +void DeviceBase::Release() +{ + getDeviceCommon()->DeviceRelease(); +} +DeviceBase* DeviceBase::GetParent() const +{ + return getDeviceCommon()->pParent.GetPtr(); +} +DeviceManager* DeviceBase::GetManager() const +{ + return getDeviceCommon()->pCreateDesc->GetManagerImpl(); +} + +void DeviceBase::SetMessageHandler(MessageHandler* handler) +{ + getDeviceCommon()->HandlerRef.SetHandler(handler); +} +MessageHandler* DeviceBase::GetMessageHandler() const +{ + return getDeviceCommon()->HandlerRef.GetHandler(); +} + +DeviceType DeviceBase::GetType() const +{ + return getDeviceCommon()->pCreateDesc->Type; +} + +bool DeviceBase::GetDeviceInfo(DeviceInfo* info) const +{ + return getDeviceCommon()->pCreateDesc->GetDeviceInfo(info); + //info->Name[0] = 0; + //return false; +} + +// returns the MessageHandler's lock +Lock* DeviceBase::GetHandlerLock() const +{ + return getDeviceCommon()->HandlerRef.GetLock(); +} + +// Derive DeviceManagerCreateDesc to provide abstract function implementation. +class DeviceManagerCreateDesc : public DeviceCreateDesc +{ +public: + DeviceManagerCreateDesc(DeviceFactory* factory) + : DeviceCreateDesc(factory, Device_Manager) { } + + // We don't need there on Manager since it isn't assigned to DeviceHandle. + virtual DeviceCreateDesc* Clone() const { return 0; } + virtual MatchResult MatchDevice(const DeviceCreateDesc&, + DeviceCreateDesc**) const { return Match_None; } + virtual DeviceBase* NewDeviceInstance() { return 0; } + virtual bool GetDeviceInfo(DeviceInfo*) const { return false; } +}; + +//------------------------------------------------------------------------------------- +// ***** DeviceManagerImpl + +DeviceManagerImpl::DeviceManagerImpl() + : DeviceImpl(CreateManagerDesc(), 0) + //,DeviceCreateDescList(pCreateDesc ? pCreateDesc->pLock : 0) +{ + if (pCreateDesc) + { + pCreateDesc->pLock->pManager = this; + } +} + +DeviceManagerImpl::~DeviceManagerImpl() +{ + // Shutdown must've been called. + OVR_ASSERT(!pCreateDesc->pDevice); + + // Remove all factories + while(!Factories.IsEmpty()) + { + DeviceFactory* factory = Factories.GetFirst(); + factory->RemovedFromManager(); + factory->RemoveNode(); + } +} + +DeviceCreateDesc* DeviceManagerImpl::CreateManagerDesc() +{ + DeviceCreateDesc* managerDesc = new DeviceManagerCreateDesc(0); + if (managerDesc) + { + managerDesc->pLock = *new DeviceManagerLock; + } + return managerDesc; +} + +bool DeviceManagerImpl::Initialize(DeviceBase* parent) +{ + OVR_UNUSED(parent); + if (!pCreateDesc || !pCreateDesc->pLock) + return false; + + pProfileManager = *ProfileManager::Create(); + + return true; +} + +void DeviceManagerImpl::Shutdown() +{ + // Remove all device descriptors from list while the lock is held. + // Some descriptors may survive longer due to handles. + while(!Devices.IsEmpty()) + { + DeviceCreateDesc* devDesc = Devices.GetFirst(); + OVR_ASSERT(!devDesc->pDevice); // Manager shouldn't be dying while Device exists. + devDesc->Enumerated = false; + devDesc->RemoveNode(); + devDesc->pNext = devDesc->pPrev = 0; + + if (devDesc->HandleCount == 0) + { + delete devDesc; + } + } + Devices.Clear(); + + // These must've been cleared by caller. + OVR_ASSERT(pCreateDesc->pDevice == 0); + OVR_ASSERT(pCreateDesc->pLock->pManager == 0); + + pProfileManager.Clear(); +} + + +// Callbacks for DeviceCreation/Release +DeviceBase* DeviceManagerImpl::CreateDevice_MgrThread(DeviceCreateDesc* createDesc, DeviceBase* parent) +{ + // Calls to DeviceManagerImpl::CreateDevice are enqueued with wait while holding pManager, + // so 'this' must remain valid. + OVR_ASSERT(createDesc->pLock->pManager); + + Lock::Locker devicesLock(GetLock()); + + // If device already exists, just AddRef to it. + if (createDesc->pDevice) + { + createDesc->pDevice->AddRef(); + return createDesc->pDevice; + } + + if (!parent) + parent = this; + + DeviceBase* device = createDesc->NewDeviceInstance(); + + if (device) + { + if (device->getDeviceCommon()->Initialize(parent)) + { + createDesc->pDevice = device; + } + else + { + // Don't go through Release() to avoid PushCall behaviour, + // as it is not needed here. + delete device; + device = 0; + } + } + + return device; +} + +Void DeviceManagerImpl::ReleaseDevice_MgrThread(DeviceBase* device) +{ + // descKeepAlive will keep ManagerLock object alive as well, + // allowing us to exit gracefully. + Ptr descKeepAlive; + Lock::Locker devicesLock(GetLock()); + DeviceCommon* devCommon = device->getDeviceCommon(); + + while(1) + { + UInt32 refCount = devCommon->RefCount; + + if (refCount > 1) + { + if (devCommon->RefCount.CompareAndSet_NoSync(refCount, refCount-1)) + { + // We decreented from initial count higher then 1; + // nothing else to do. + return 0; + } + } + else if (devCommon->RefCount.CompareAndSet_NoSync(1, 0)) + { + // { 1 -> 0 } decrement succeded. Destroy this device. + break; + } + } + + // At this point, may be releasing the device manager itself. + // This does not matter, however, since shutdown logic is the same + // in both cases. DeviceManager::Shutdown with begin shutdown process for + // the internal manager thread, which will eventually destroy itself. + // TBD: Clean thread shutdown. + descKeepAlive = devCommon->pCreateDesc; + descKeepAlive->pDevice = 0; + devCommon->Shutdown(); + delete device; + return 0; +} + + + +Void DeviceManagerImpl::EnumerateAllFactoryDevices() +{ + // 1. Mark matching devices as NOT enumerated. + // 2. Call factory to enumerate all HW devices, adding any device that + // was not matched. + // 3. Remove non-matching devices. + + Lock::Locker deviceLock(GetLock()); + + DeviceCreateDesc* devDesc, *nextdevDesc; + + // 1. + for(devDesc = Devices.GetFirst(); + !Devices.IsNull(devDesc); devDesc = devDesc->pNext) + { + //if (devDesc->pFactory == factory) + devDesc->Enumerated = false; + } + + // 2. + DeviceFactory* factory = Factories.GetFirst(); + while(!Factories.IsNull(factory)) + { + EnumerateFactoryDevices(factory); + factory = factory->pNext; + } + + + // 3. + for(devDesc = Devices.GetFirst(); + !Devices.IsNull(devDesc); devDesc = nextdevDesc) + { + // In case 'devDesc' gets removed. + nextdevDesc = devDesc->pNext; + + // Note, device might be not enumerated since it is opened and + // in use! Do NOT notify 'device removed' in this case (!AB) + if (!devDesc->Enumerated) + { + // This deletes the devDesc for HandleCount == 0 due to Release in DeviceHandle. + CallOnDeviceRemoved(devDesc); + + /* + if (devDesc->HandleCount == 0) + { + // Device must be dead if it ever existed, since it AddRefs to us. + // ~DeviceCreateDesc removes its node from list. + OVR_ASSERT(!devDesc->pDevice); + delete devDesc; + } + */ + } + } + + return 0; +} + +Ptr DeviceManagerImpl::AddDevice_NeedsLock( + const DeviceCreateDesc& createDesc) +{ + // If found, mark as enumerated and we are done. + DeviceCreateDesc* descCandidate = 0; + + for(DeviceCreateDesc* devDesc = Devices.GetFirst(); + !Devices.IsNull(devDesc); devDesc = devDesc->pNext) + { + DeviceCreateDesc::MatchResult mr = devDesc->MatchDevice(createDesc, &descCandidate); + if (mr == DeviceCreateDesc::Match_Found) + { + devDesc->Enumerated = true; + if (!devDesc->pDevice) + CallOnDeviceAdded(devDesc); + return devDesc; + } + } + + // Update candidate (this may involve writing fields to HMDDevice createDesc). + if (descCandidate) + { + bool newDevice = false; + if (descCandidate->UpdateMatchedCandidate(createDesc, &newDevice)) + { + descCandidate->Enumerated = true; + if (!descCandidate->pDevice || newDevice) + CallOnDeviceAdded(descCandidate); + return descCandidate; + } + } + + // If not found, add new device. + // - This stores a new descriptor with + // {pDevice = 0, HandleCount = 1, Enumerated = true} + DeviceCreateDesc* desc = createDesc.Clone(); + desc->pLock = pCreateDesc->pLock; + Devices.PushBack(desc); + desc->Enumerated = true; + + CallOnDeviceAdded(desc); + + return desc; +} + +Ptr DeviceManagerImpl::FindDevice( + const String& path, + DeviceType deviceType) +{ + Lock::Locker deviceLock(GetLock()); + DeviceCreateDesc* devDesc; + + for (devDesc = Devices.GetFirst(); + !Devices.IsNull(devDesc); devDesc = devDesc->pNext) + { + if ((deviceType == Device_None || deviceType == devDesc->Type) && + devDesc->MatchDevice(path)) + return devDesc; + } + return NULL; +} + +Ptr DeviceManagerImpl::FindHIDDevice(const HIDDeviceDesc& hidDevDesc) +{ + Lock::Locker deviceLock(GetLock()); + DeviceCreateDesc* devDesc; + + for (devDesc = Devices.GetFirst(); + !Devices.IsNull(devDesc); devDesc = devDesc->pNext) + { + if (devDesc->MatchHIDDevice(hidDevDesc)) + return devDesc; + } + return NULL; +} + +void DeviceManagerImpl::DetectHIDDevice(const HIDDeviceDesc& hidDevDesc) +{ + Lock::Locker deviceLock(GetLock()); + DeviceFactory* factory = Factories.GetFirst(); + while(!Factories.IsNull(factory)) + { + if (factory->DetectHIDDevice(this, hidDevDesc)) + break; + factory = factory->pNext; + } + +} + +// Enumerates devices for a particular factory. +Void DeviceManagerImpl::EnumerateFactoryDevices(DeviceFactory* factory) +{ + + class FactoryEnumerateVisitor : public DeviceFactory::EnumerateVisitor + { + DeviceManagerImpl* pManager; + DeviceFactory* pFactory; + public: + FactoryEnumerateVisitor(DeviceManagerImpl* manager, DeviceFactory* factory) + : pManager(manager), pFactory(factory) { } + + virtual void Visit(const DeviceCreateDesc& createDesc) + { + pManager->AddDevice_NeedsLock(createDesc); + } + }; + + FactoryEnumerateVisitor newDeviceVisitor(this, factory); + factory->EnumerateDevices(newDeviceVisitor); + + + return 0; +} + + +DeviceEnumerator<> DeviceManagerImpl::EnumerateDevicesEx(const DeviceEnumerationArgs& args) +{ + Lock::Locker deviceLock(GetLock()); + + if (Devices.IsEmpty()) + return DeviceEnumerator<>(); + + DeviceCreateDesc* firstDeviceDesc = Devices.GetFirst(); + DeviceEnumerator<> e = enumeratorFromHandle(DeviceHandle(firstDeviceDesc), args); + + if (!args.MatchRule(firstDeviceDesc->Type, firstDeviceDesc->Enumerated)) + { + e.Next(); + } + + return e; +} + +//------------------------------------------------------------------------------------- +// ***** DeviceCommon + +void DeviceCommon::DeviceAddRef() +{ + RefCount++; +} + +void DeviceCommon::DeviceRelease() +{ + while(1) + { + UInt32 refCount = RefCount; + OVR_ASSERT(refCount > 0); + + if (refCount == 1) + { + DeviceManagerImpl* manager = pCreateDesc->GetManagerImpl(); + ThreadCommandQueue* queue = manager->GetThreadQueue(); + + // Enqueue ReleaseDevice for {1 -> 0} transition with no wait. + // We pass our reference ownership into the queue to destroy. + // It's in theory possible for another thread to re-steal our device reference, + // but that is checked for atomically in DeviceManagerImpl::ReleaseDevice. + if (!queue->PushCall(manager, &DeviceManagerImpl::ReleaseDevice_MgrThread, + pCreateDesc->pDevice)) + { + // PushCall shouldn't fail because background thread runs while manager is + // alive and we are holding Manager alive through pParent chain. + OVR_ASSERT(false); + } + + // Warning! At his point everything, including manager, may be dead. + break; + } + else if (RefCount.CompareAndSet_NoSync(refCount, refCount-1)) + { + break; + } + } +} + + + +//------------------------------------------------------------------------------------- +// ***** DeviceCreateDesc + + +void DeviceCreateDesc::AddRef() +{ + // Technically, HandleCount { 0 -> 1 } transition can only happen during Lock, + // but we leave this to caller to worry about (happens during enumeration). + HandleCount++; +} + +void DeviceCreateDesc::Release() +{ + while(1) + { + UInt32 handleCount = HandleCount; + // HandleCount must obviously be >= 1, since we are releasing it. + OVR_ASSERT(handleCount > 0); + + // {1 -> 0} transition may cause us to be destroyed, so require a lock. + if (handleCount == 1) + { + Ptr lockKeepAlive; + Lock::Locker deviceLockScope(GetLock()); + + if (!HandleCount.CompareAndSet_NoSync(handleCount, 0)) + continue; + + OVR_ASSERT(pDevice == 0); + + // Destroy *this if the manager was destroyed already, or Enumerated + // is false (device no longer available). + if (!GetManagerImpl() || !Enumerated) + { + lockKeepAlive = pLock; + + // Remove from manager list (only matters for !Enumerated). + if (pNext) + { + RemoveNode(); + pNext = pPrev = 0; + } + + delete this; + } + + // Available DeviceCreateDesc may survive with { HandleCount == 0 }, + // in case it might be enumerated again later. + break; + } + else if (HandleCount.CompareAndSet_NoSync(handleCount, handleCount-1)) + { + break; + } + } +} + +HMDDevice* HMDDevice::Disconnect(SensorDevice* psensor) +{ + if (!psensor) + return NULL; + + OVR::DeviceManager* manager = GetManager(); + if (manager) + { + //DeviceManagerImpl* mgrImpl = static_cast(manager); + Ptr desc = getDeviceCommon()->pCreateDesc; + if (desc) + { + class Visitor : public DeviceFactory::EnumerateVisitor + { + Ptr Desc; + public: + Visitor(DeviceCreateDesc* desc) : Desc(desc) {} + virtual void Visit(const DeviceCreateDesc& createDesc) + { + Lock::Locker lock(Desc->GetLock()); + Desc->UpdateMatchedCandidate(createDesc); + } + } visitor(desc); + //SensorDeviceImpl* sImpl = static_cast(psensor); + + SensorDisplayInfoImpl displayInfo; + + if (psensor->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize)) + { + displayInfo.Unpack(); + + // If we got display info, try to match / create HMDDevice as well + // so that sensor settings give preference. + if (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) + { + SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(displayInfo, visitor); + } + } + } + } + return this; +} + +bool HMDDevice::IsDisconnected() const +{ + OVR::HMDInfo info; + GetDeviceInfo(&info); + // if strlen(info.DisplayDeviceName) == 0 then + // this HMD is 'fake' (created using sensor). + return (strlen(info.DisplayDeviceName) == 0); +} + + +} // namespace OVR + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_DeviceImpl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_DeviceImpl.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,432 @@ +/************************************************************************************ + +Filename : OVR_DeviceImpl.h +Content : Partial back-end independent implementation of Device interfaces +Created : October 10, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_DeviceImpl_h +#define OVR_DeviceImpl_h + +#include "OVR_Device.h" +#include "Kernel/OVR_Atomic.h" +#include "Kernel/OVR_Log.h" +#include "Kernel/OVR_System.h" + +#include "Kernel/OVR_Threads.h" +#include "OVR_ThreadCommandQueue.h" +#include "OVR_HIDDevice.h" + +namespace OVR { + +class DeviceManagerImpl; +class DeviceFactory; + +enum +{ + Oculus_VendorId = 0x2833 +}; + +//------------------------------------------------------------------------------------- +// Globally shared Lock implementation used for MessageHandlers. + +class SharedLock +{ +public: + SharedLock() : UseCount(0) {} + + Lock* GetLockAddRef(); + void ReleaseLock(Lock* plock); + +private: + Lock* toLock() { return (Lock*)Buffer; } + + // UseCount and max alignment. + volatile int UseCount; + UInt64 Buffer[(sizeof(Lock)+sizeof(UInt64)-1)/sizeof(UInt64)]; +}; + + +// Wrapper for MessageHandler that includes synchronization logic. +// References to MessageHandlers are organized in a list to allow for them to +// easily removed with MessageHandler::RemoveAllHandlers. +class MessageHandlerRef : public ListNode +{ +public: + MessageHandlerRef(DeviceBase* device); + ~MessageHandlerRef(); + + void SetHandler(MessageHandler* hander); + + // Not-thread-safe version + void SetHandler_NTS(MessageHandler* hander); + + void Call(const Message& msg) + { + Lock::Locker lockScope(pLock); + if (pHandler) + pHandler->OnMessage(msg); + } + + Lock* GetLock() const { return pLock; } + + // GetHandler() is not thread safe if used out of order across threads; nothing can be done + // about that. + MessageHandler* GetHandler() const { return pHandler; } + DeviceBase* GetDevice() const { return pDevice; } + +private: + Lock* pLock; // Cached global handler lock. + DeviceBase* pDevice; + MessageHandler* pHandler; +}; + + + +//------------------------------------------------------------------------------------- + +// DeviceManagerLock is a synchronization lock used by DeviceManager for Devices +// and is allocated separately for potentially longer lifetime. +// +// DeviceManagerLock is used for all of the following: +// - Adding/removing devices +// - Reporting manager lifetime (pManager != 0) for DeviceHandles +// - Protecting device creation/shutdown. + +class DeviceManagerLock : public RefCountBase +{ +public: + Lock CreateLock; + DeviceManagerImpl* pManager; + + DeviceManagerLock() : pManager(0) { } +}; + + +// DeviceCreateDesc provides all of the information needed to create any device, a derived +// instance of this class is created by DeviceFactory during enumeration. +// - DeviceCreateDesc may or may not be a part of DeviceManager::Devices list (check pNext != 0). +// - Referenced and kept alive by DeviceHandle. + +class DeviceCreateDesc : public ListNode, public NewOverrideBase +{ + void operator = (const DeviceCreateDesc&) { } // Assign not supported; suppress MSVC warning. +public: + DeviceCreateDesc(DeviceFactory* factory, DeviceType type) + : pFactory(factory), Type(type), pLock(0), HandleCount(0), pDevice(0), Enumerated(true) + { + pNext = pPrev = 0; + } + + virtual ~DeviceCreateDesc() + { + OVR_ASSERT(!pDevice); + if (pNext) + RemoveNode(); + } + + DeviceManagerImpl* GetManagerImpl() const { return pLock->pManager; } + Lock* GetLock() const { return &pLock->CreateLock; } + + // DeviceCreateDesc reference counting is tied to Devices list management, + // see comments for HandleCount. + void AddRef(); + void Release(); + + + // *** Device creation/matching Interface + + + // Cloning copies us to an allocated object when new device is enumerated. + virtual DeviceCreateDesc* Clone() const = 0; + // Creates a new device instance without Initializing it; the + // later is done my Initialize()/Shutdown() methods of the device itself. + virtual DeviceBase* NewDeviceInstance() = 0; + // Override to return device-specific info. + virtual bool GetDeviceInfo(DeviceInfo* info) const = 0; + + + enum MatchResult + { + Match_None, + Match_Found, + Match_Candidate + }; + + // Override to return Match_Found if descriptor matches our device. + // Match_Candidate can be returned, with pcandicate update, if this may be a match + // but more searching is necessary. If this is the case UpdateMatchedCandidate will be called. + virtual MatchResult MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc** pcandidate) const = 0; + + // Called for matched candidate after all potential matches are iterated. + // Used to update HMDevice creation arguments from Sensor. + // Optional return param 'newDeviceFlag' will be set to true if the + // 'desc' refers to a new device; false, otherwise. + // Return 'false' to create new object, 'true' if done with this argument. + virtual bool UpdateMatchedCandidate( + const DeviceCreateDesc& desc, bool* newDeviceFlag = NULL) + { OVR_UNUSED2(desc, newDeviceFlag); return false; } + + // Matches HID device to the descriptor. + virtual bool MatchHIDDevice(const HIDDeviceDesc&) const { return false; } + + // Matches device by path. + virtual bool MatchDevice(const String& /*path*/) { return false; } +//protected: + DeviceFactory* const pFactory; + const DeviceType Type; + + // List in which this descriptor lives. pList->CreateLock required if added/removed. + Ptr pLock; + + // Strong references to us: Incremented by Device, DeviceHandles & Enumerators. + // May be 0 if device not created and there are no handles. + // Following transitions require pList->CreateLock: + // {1 -> 0}: May delete & remove handle if no longer available. + // {0 -> 1}: Device creation is only possible if manager is still alive. + AtomicInt HandleCount; + // If not null, points to our created device instance. Modified during lock only. + DeviceBase* pDevice; + // True if device is marked as available during enumeration. + bool Enumerated; +}; + + + +// Common data present in the implementation of every DeviceBase. +// Injected by DeviceImpl. +class DeviceCommon +{ +public: + AtomicInt RefCount; + Ptr pCreateDesc; + Ptr pParent; + MessageHandlerRef HandlerRef; + + DeviceCommon(DeviceCreateDesc* createDesc, DeviceBase* device, DeviceBase* parent) + : RefCount(1), pCreateDesc(createDesc), pParent(parent), HandlerRef(device) + { + } + + // Device reference counting delegates to Manager thread to actually kill devices. + void DeviceAddRef(); + void DeviceRelease(); + + Lock* GetLock() const { return pCreateDesc->GetLock(); } + + virtual bool Initialize(DeviceBase* parent) = 0; + virtual void Shutdown() = 0; +}; + + +//------------------------------------------------------------------------------------- +// DeviceImpl address DeviceRecord implementation to a device base class B. +// B must be derived form DeviceBase. + +template +class DeviceImpl : public B, public DeviceCommon +{ +public: + DeviceImpl(DeviceCreateDesc* createDesc, DeviceBase* parent) + : DeviceCommon(createDesc, getThis(), parent) + { + } + + // Convenience method to avoid manager access typecasts. + DeviceManagerImpl* GetManagerImpl() const { return pCreateDesc->pLock->pManager; } + + // Inline to avoid warnings. + DeviceImpl* getThis() { return this; } + + // Common implementation delegate to avoid virtual inheritance and dynamic casts. + virtual DeviceCommon* getDeviceCommon() const { return (DeviceCommon*)this; } + + /* + virtual void AddRef() { pCreateDesc->DeviceAddRef(); } + virtual void Release() { pCreateDesc->DeviceRelease(); } + virtual DeviceBase* GetParent() const { return pParent.GetPtr(); } + virtual DeviceManager* GetManager() const { return pCreateDesc->pLock->pManager;} + virtual void SetMessageHandler(MessageHandler* handler) { HanderRef.SetHandler(handler); } + virtual MessageHandler* GetMessageHandler() const { return HanderRef.GetHandler(); } + virtual DeviceType GetType() const { return pCreateDesc->Type; } + virtual DeviceType GetType() const { return pCreateDesc->Type; } + */ +}; + + +//------------------------------------------------------------------------------------- +// ***** DeviceFactory + +// DeviceFactory is maintained in DeviceManager for each separately-enumerable +// device type; factories allow separation of unrelated enumeration code. + +class DeviceFactory : public ListNode, public NewOverrideBase +{ +public: + + DeviceFactory() : pManager(0) + { + pNext = pPrev = 0; + } + virtual ~DeviceFactory() { } + + DeviceManagerImpl* GetManagerImpl() { return pManager; } + + // Notifiers called when we are added to/removed from a device. + virtual bool AddedToManager(DeviceManagerImpl* manager) + { + OVR_ASSERT(pManager == 0); + pManager = manager; + return true; + } + + virtual void RemovedFromManager() + { + pManager = 0; + } + + + // *** Device Enumeration/Creation Support + + // Passed to EnumerateDevices to be informed of every device detected. + class EnumerateVisitor + { + public: + virtual void Visit(const DeviceCreateDesc& createDesc) = 0; + }; + + // Enumerates factory devices by notifying EnumerateVisitor about every + // device that is present. + virtual void EnumerateDevices(EnumerateVisitor& visitor) = 0; + + // Matches vendorId/productId pair with the factory; returns 'true' + // if the factory can handle the device. + virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const + { + OVR_UNUSED2(vendorId, productId); + return false; + } + + // Detects the HID device and adds the DeviceCreateDesc into Devices list, if + // the device belongs to this factory. Returns 'false', if not. + virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc) + { + OVR_UNUSED2(pdevMgr, desc); + return false; + } + +protected: + DeviceManagerImpl* pManager; +}; + + +//------------------------------------------------------------------------------------- +// ***** DeviceManagerImpl + +// DeviceManagerImpl is a partial default DeviceManager implementation that +// maintains a list of devices and supports their enumeration. + +class DeviceManagerImpl : public DeviceImpl, public ThreadCommandQueue +{ +public: + DeviceManagerImpl(); + ~DeviceManagerImpl(); + + // Constructor helper function to create Descriptor and manager lock during initialization. + static DeviceCreateDesc* CreateManagerDesc(); + + // DeviceManagerImpl provides partial implementation of Initialize/Shutdown that must + // be called by the platform-specific derived class. + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + + // Every DeviceManager has an associated profile manager, which is used to store + // user settings that may affect device behavior. + virtual ProfileManager* GetProfileManager() const { return pProfileManager.GetPtr(); } + + // Override to return ThreadCommandQueue implementation used to post commands + // to the background device manager thread (that must be created by Initialize). + virtual ThreadCommandQueue* GetThreadQueue() = 0; + + // Returns the thread id of the DeviceManager. + virtual ThreadId GetThreadId() const = 0; + + virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args); + + + // + void AddFactory(DeviceFactory* factory) + { + // This lock is only needed if we call AddFactory after manager thread creation. + Lock::Locker scopeLock(GetLock()); + Factories.PushBack(factory); + factory->AddedToManager(this); + } + + void CallOnDeviceAdded(DeviceCreateDesc* desc) + { + HandlerRef.Call(MessageDeviceStatus(Message_DeviceAdded, this, DeviceHandle(desc))); + } + void CallOnDeviceRemoved(DeviceCreateDesc* desc) + { + HandlerRef.Call(MessageDeviceStatus(Message_DeviceRemoved, this, DeviceHandle(desc))); + } + + // Helper to access Common data for a device. + static DeviceCommon* GetDeviceCommon(DeviceBase* device) + { + return device->getDeviceCommon(); + } + + + // Background-thread callbacks for DeviceCreation/Release. These + DeviceBase* CreateDevice_MgrThread(DeviceCreateDesc* createDesc, DeviceBase* parent = 0); + Void ReleaseDevice_MgrThread(DeviceBase* device); + + + // Calls EnumerateDevices() on all factories + virtual Void EnumerateAllFactoryDevices(); + // Enumerates devices for a particular factory. + virtual Void EnumerateFactoryDevices(DeviceFactory* factory); + + virtual HIDDeviceManager* GetHIDDeviceManager() const + { + return HidDeviceManager; + } + + // Adds device (DeviceCreateDesc*) into Devices. Returns NULL, + // if unsuccessful or device is already in the list. + virtual Ptr AddDevice_NeedsLock(const DeviceCreateDesc& createDesc); + + // Finds a device descriptor by path and optional type. + Ptr FindDevice(const String& path, DeviceType = Device_None); + + // Finds HID device by HIDDeviceDesc. + Ptr FindHIDDevice(const HIDDeviceDesc&); + void DetectHIDDevice(const HIDDeviceDesc&); + + // Manager Lock-protected list of devices. + List Devices; + + // Factories used to detect and manage devices. + List Factories; + +protected: + Ptr HidDeviceManager; + Ptr pProfileManager; +}; + + +} // namespace OVR + +#endif diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_DeviceMessages.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_DeviceMessages.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,162 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_DeviceMessages.h +Content : Definition of messages generated by devices +Created : February 5, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_DeviceMessages_h +#define OVR_DeviceMessages_h + +#include "OVR_DeviceConstants.h" +#include "OVR_DeviceHandle.h" + +#include "Kernel/OVR_Math.h" +#include "Kernel/OVR_Array.h" +#include "Kernel/OVR_Color.h" + +namespace OVR { + +class DeviceBase; +class DeviceHandle; + + +#define OVR_MESSAGETYPE(devName, msgIndex) ((Device_##devName << 8) | msgIndex) + +// MessageType identifies the structure of the Message class; based on the message, +// casting can be used to obtain the exact value. +enum MessageType +{ + // Used for unassigned message types. + Message_None = 0, + + // Device Manager Messages + Message_DeviceAdded = OVR_MESSAGETYPE(Manager, 0), // A new device is detected by manager. + Message_DeviceRemoved = OVR_MESSAGETYPE(Manager, 1), // Existing device has been plugged/unplugged. + // Sensor Messages + Message_BodyFrame = OVR_MESSAGETYPE(Sensor, 0), // Emitted by sensor at regular intervals. + // Latency Tester Messages + Message_LatencyTestSamples = OVR_MESSAGETYPE(LatencyTester, 0), + Message_LatencyTestColorDetected = OVR_MESSAGETYPE(LatencyTester, 1), + Message_LatencyTestStarted = OVR_MESSAGETYPE(LatencyTester, 2), + Message_LatencyTestButton = OVR_MESSAGETYPE(LatencyTester, 3), + +}; + +//------------------------------------------------------------------------------------- +// Base class for all messages. +class Message +{ +public: + Message(MessageType type = Message_None, + DeviceBase* pdev = 0) : Type(type), pDevice(pdev) + { } + + MessageType Type; // What kind of message this is. + DeviceBase* pDevice; // Device that emitted the message. +}; + + +// Sensor BodyFrame notification. +// Sensor uses Right-Handed coordinate system to return results, with the following +// axis definitions: +// - Y Up positive +// - X Right Positive +// - Z Back Positive +// Rotations a counter-clockwise (CCW) while looking in the negative direction +// of the axis. This means they are interpreted as follows: +// - Roll is rotation around Z, counter-clockwise (tilting left) in XY plane. +// - Yaw is rotation around Y, positive for turning left. +// - Pitch is rotation around X, positive for pitching up. + +class MessageBodyFrame : public Message +{ +public: + MessageBodyFrame(DeviceBase* dev) + : Message(Message_BodyFrame, dev), Temperature(0.0f), TimeDelta(0.0f) + { + } + + Vector3f Acceleration; // Acceleration in m/s^2. + Vector3f RotationRate; // Angular velocity in rad/s^2. + Vector3f MagneticField; // Magnetic field strength in Gauss. + float Temperature; // Temperature reading on sensor surface, in degrees Celsius. + float TimeDelta; // Time passed since last Body Frame, in seconds. +}; + +// Sent when we receive a device status changes (e.g.: +// Message_DeviceAdded, Message_DeviceRemoved). +class MessageDeviceStatus : public Message +{ +public: + MessageDeviceStatus(MessageType type, DeviceBase* dev, const DeviceHandle &hdev) + : Message(type, dev), Handle(hdev) { } + + DeviceHandle Handle; +}; + +//------------------------------------------------------------------------------------- +// ***** Latency Tester + +// Sent when we receive Latency Tester samples. +class MessageLatencyTestSamples : public Message +{ +public: + MessageLatencyTestSamples(DeviceBase* dev) + : Message(Message_LatencyTestSamples, dev) + { + } + + Array Samples; +}; + +// Sent when a Latency Tester 'color detected' event occurs. +class MessageLatencyTestColorDetected : public Message +{ +public: + MessageLatencyTestColorDetected(DeviceBase* dev) + : Message(Message_LatencyTestColorDetected, dev) + { + } + + UInt16 Elapsed; + Color DetectedValue; + Color TargetValue; +}; + +// Sent when a Latency Tester 'change color' event occurs. +class MessageLatencyTestStarted : public Message +{ +public: + MessageLatencyTestStarted(DeviceBase* dev) + : Message(Message_LatencyTestStarted, dev) + { + } + + Color TargetValue; +}; + +// Sent when a Latency Tester 'button' event occurs. +class MessageLatencyTestButton : public Message +{ +public: + MessageLatencyTestButton(DeviceBase* dev) + : Message(Message_LatencyTestButton, dev) + { + } + +}; + + +} // namespace OVR + +#endif diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_HIDDevice.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_HIDDevice.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,143 @@ +/************************************************************************************ + +Filename : OVR_HIDDevice.h +Content : Cross platform HID device interface. +Created : February 22, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_HIDDevice_h +#define OVR_HIDDevice_h + +#include "OVR_HIDDeviceBase.h" + +#include "Kernel/OVR_RefCount.h" +#include "Kernel/OVR_String.h" +#include "Kernel/OVR_Timer.h" + +namespace OVR { + +class HIDDevice; +class DeviceManager; + +// HIDDeviceDesc contains interesting attributes of a HID device, including a Path +// that can be used to create it. +struct HIDDeviceDesc +{ + UInt16 VendorId; + UInt16 ProductId; + UInt16 VersionNumber; + UInt16 Usage; + UInt16 UsagePage; + String Path; // Platform specific. + String Manufacturer; + String Product; + String SerialNumber; +}; + +// HIDEnumerateVisitor exposes a Visit interface called for every detected device +// by HIDDeviceManager::Enumerate. +class HIDEnumerateVisitor +{ +public: + + // Should return true if we are interested in supporting + // this HID VendorId and ProductId pair. + virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) + { OVR_UNUSED2(vendorId, productId); return true; } + + // Override to get notified about available device. Will only be called for + // devices that matched MatchVendorProduct. + virtual void Visit(HIDDevice&, const HIDDeviceDesc&) { } +}; + + +//------------------------------------------------------------------------------------- +// ***** HIDDeviceManager + +// Internal manager for enumerating and opening HID devices. +// If an OVR::DeviceManager is created then an OVR::HIDDeviceManager will automatically be created and can be accessed from the +// DeviceManager by calling 'GetHIDDeviceManager()'. When using HIDDeviceManager in standalone mode, the client must call +// 'Create' below. +class HIDDeviceManager : public RefCountBase +{ +public: + + // Creates a new HIDDeviceManager. Only one instance of HIDDeviceManager should be created at a time. + static HIDDeviceManager* Create(); + + // Enumerate HID devices using a HIDEnumerateVisitor derived visitor class. + virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor) = 0; + + // Open a HID device with the specified path. + virtual HIDDevice* Open(const String& path) = 0; + +protected: + HIDDeviceManager() + { } +}; + +//------------------------------------------------------------------------------------- +// ***** HIDDevice + +// HID device object. This is designed to be operated in synchronous +// and asynchronous modes. With no handler set, input messages will be +// stored and can be retrieved by calling 'Read' or 'ReadBlocking'. +class HIDDevice : public RefCountBase, public HIDDeviceBase +{ +public: + + HIDDevice() + : Handler(NULL) + { + } + + virtual ~HIDDevice() {} + + virtual bool SetFeatureReport(UByte* data, UInt32 length) = 0; + virtual bool GetFeatureReport(UByte* data, UInt32 length) = 0; + +// Not yet implemented. +/* + virtual bool Write(UByte* data, UInt32 length) = 0; + + virtual bool Read(UByte* pData, UInt32 length, UInt32 timeoutMilliS) = 0; + virtual bool ReadBlocking(UByte* pData, UInt32 length) = 0; +*/ + + class HIDHandler + { + public: + virtual void OnInputReport(UByte* pData, UInt32 length) + { OVR_UNUSED2(pData, length); } + + virtual UInt64 OnTicks(UInt64 ticksMks) + { OVR_UNUSED1(ticksMks); return Timer::MksPerSecond * 1000; ; } + + enum HIDDeviceMessageType + { + HIDDeviceMessage_DeviceAdded = 0, + HIDDeviceMessage_DeviceRemoved = 1 + }; + + virtual void OnDeviceMessage(HIDDeviceMessageType messageType) + { OVR_UNUSED1(messageType); } + }; + + void SetHandler(HIDHandler* handler) + { Handler = handler; } + +protected: + HIDHandler* Handler; +}; + +} // namespace OVR + +#endif diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_HIDDeviceBase.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_HIDDeviceBase.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,40 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_HIDDeviceBase.h +Content : Definition of HID device interface. +Created : March 11, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_HIDDeviceBase_h +#define OVR_HIDDeviceBase_h + +#include "Kernel/OVR_Types.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +// ***** HIDDeviceBase + +// Base interface for HID devices. +class HIDDeviceBase +{ +public: + + virtual ~HIDDeviceBase() { } + + virtual bool SetFeatureReport(UByte* data, UInt32 length) = 0; + virtual bool GetFeatureReport(UByte* data, UInt32 length) = 0; +}; + +} // namespace OVR + +#endif diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_HIDDeviceImpl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_HIDDeviceImpl.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,203 @@ +/************************************************************************************ + +Filename : OVR_HIDDeviceImpl.h +Content : Implementation of HIDDevice. +Created : March 7, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_HIDDeviceImpl_h +#define OVR_HIDDeviceImpl_h + +//#include "OVR_Device.h" +#include "OVR_DeviceImpl.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +class HIDDeviceCreateDesc : public DeviceCreateDesc +{ +public: + HIDDeviceCreateDesc(DeviceFactory* factory, DeviceType type, const HIDDeviceDesc& hidDesc) + : DeviceCreateDesc(factory, type), HIDDesc(hidDesc) { } + HIDDeviceCreateDesc(const HIDDeviceCreateDesc& other) + : DeviceCreateDesc(other.pFactory, other.Type), HIDDesc(other.HIDDesc) { } + + virtual bool MatchDevice(const String& path) + { + // should it be case insensitive? + return HIDDesc.Path.CompareNoCase(path) == 0; + } + + HIDDeviceDesc HIDDesc; +}; + +//------------------------------------------------------------------------------------- +template +class HIDDeviceImpl : public DeviceImpl, public HIDDevice::HIDHandler +{ +public: + HIDDeviceImpl(HIDDeviceCreateDesc* createDesc, DeviceBase* parent) + : DeviceImpl(createDesc, parent) + { + } + + // HIDDevice::Handler interface. + virtual void OnDeviceMessage(HIDDeviceMessageType messageType) + { + MessageType handlerMessageType; + switch (messageType) { + case HIDDeviceMessage_DeviceAdded: + handlerMessageType = Message_DeviceAdded; + break; + + case HIDDeviceMessage_DeviceRemoved: + handlerMessageType = Message_DeviceRemoved; + break; + + default: OVR_ASSERT(0); return; + } + + // Do device notification. + { + Lock::Locker scopeLock(this->HandlerRef.GetLock()); + + if (this->HandlerRef.GetHandler()) + { + MessageDeviceStatus status(handlerMessageType, this, OVR::DeviceHandle(this->pCreateDesc)); + this->HandlerRef.GetHandler()->OnMessage(status); + } + } + + // Do device manager notification. + DeviceManagerImpl* manager = this->GetManagerImpl(); + switch (handlerMessageType) { + case Message_DeviceAdded: + manager->CallOnDeviceAdded(this->pCreateDesc); + break; + + case Message_DeviceRemoved: + manager->CallOnDeviceRemoved(this->pCreateDesc); + break; + + default:; + } + } + + virtual bool Initialize(DeviceBase* parent) + { + // Open HID device. + HIDDeviceDesc& hidDesc = *getHIDDesc(); + HIDDeviceManager* pManager = GetHIDDeviceManager(); + + + HIDDevice* device = pManager->Open(hidDesc.Path); + if (!device) + { + return false; + } + + InternalDevice = *device; + InternalDevice->SetHandler(this); + + // AddRef() to parent, forcing chain to stay alive. + DeviceImpl::pParent = parent; + + return true; + } + + virtual void Shutdown() + { + InternalDevice->SetHandler(NULL); + + // Remove the handler, if any. + this->HandlerRef.SetHandler(0); + + DeviceImpl::pParent.Clear(); + } + + DeviceManager* GetDeviceManager() + { + return DeviceImpl::pCreateDesc->GetManagerImpl(); + } + + HIDDeviceManager* GetHIDDeviceManager() + { + return DeviceImpl::pCreateDesc->GetManagerImpl()->GetHIDDeviceManager(); + } + + + struct WriteData + { + enum { BufferSize = 64 }; + UByte Buffer[64]; + UPInt Size; + + WriteData(UByte* data, UPInt size) : Size(size) + { + OVR_ASSERT(size <= BufferSize); + memcpy(Buffer, data, size); + } + }; + + bool SetFeatureReport(UByte* data, UInt32 length) + { + WriteData writeData(data, length); + + // Push call with wait. + bool result = false; + + ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue(); + if (!pQueue->PushCallAndWaitResult(this, &HIDDeviceImpl::setFeatureReport, &result, writeData)) + return false; + + return result; + } + + bool setFeatureReport(const WriteData& data) + { + return InternalDevice->SetFeatureReport((UByte*) data.Buffer, (UInt32) data.Size); + } + + bool GetFeatureReport(UByte* data, UInt32 length) + { + bool result = false; + + ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue(); + if (!pQueue->PushCallAndWaitResult(this, &HIDDeviceImpl::getFeatureReport, &result, data, length)) + return false; + + return result; + } + + bool getFeatureReport(UByte* data, UInt32 length) + { + return InternalDevice->GetFeatureReport(data, length); + } + +protected: + HIDDevice* GetInternalDevice() const + { + return InternalDevice; + } + + HIDDeviceDesc* getHIDDesc() const + { return &getCreateDesc()->HIDDesc; } + + HIDDeviceCreateDesc* getCreateDesc() const + { return (HIDDeviceCreateDesc*) &(*DeviceImpl::pCreateDesc); } + +private: + Ptr InternalDevice; +}; + +} // namespace OVR + +#endif diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_JSON.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_JSON.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1056 @@ +/************************************************************************************ + +PublicHeader: None +Filename : OVR_JSON.h +Content : JSON format reader and writer +Created : April 9, 2013 +Author : Brant Lewis +Notes : + The code is a derivative of the cJSON library written by Dave Gamble and subject + to the following permissive copyright. + + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "OVR_JSON.h" +#include "Kernel/OVR_SysFile.h" +#include "Kernel/OVR_Log.h" + +namespace OVR { + + +//----------------------------------------------------------------------------- +// Create a new copy of a string +static char* JSON_strdup(const char* str) +{ + UPInt len = OVR_strlen(str) + 1; + char* copy = (char*)OVR_ALLOC(len); + if (!copy) + return 0; + memcpy(copy, str, len); + return copy; +} + + +//----------------------------------------------------------------------------- +// Render the number from the given item into a string. +static char* PrintNumber(double d) +{ + char *str; + //double d=item->valuedouble; + int valueint = (int)d; + if (fabs(((double)valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) + { + str=(char*)OVR_ALLOC(21); // 2^64+1 can be represented in 21 chars. + if (str) + OVR_sprintf(str, 21, "%d", valueint); + } + else + { + str=(char*)OVR_ALLOC(64); // This is a nice tradeoff. + if (str) + { + if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60) + OVR_sprintf(str, 64, "%.0f", d); + else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) + OVR_sprintf(str, 64, "%e", d); + else + OVR_sprintf(str, 64, "%f", d); + } + } + return str; +} + +// Parse the input text into an un-escaped cstring, and populate item. +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +// Helper to assign error sting and return 0. +const char* AssignError(const char** perror, const char *errorMessage) +{ + if (perror) + *perror = errorMessage; + return 0; +} + +//----------------------------------------------------------------------------- +// ***** JSON Node class + +JSON::JSON(JSONItemType itemType) + : Type(itemType), dValue(0.0) +{ +} + +JSON::~JSON() +{ + JSON* child = Children.GetFirst(); + while (!Children.IsNull(child)) + { + child->RemoveNode(); + child->Release(); + child = Children.GetFirst(); + } +} + +//----------------------------------------------------------------------------- +// Parse the input text to generate a number, and populate the result into item +// Returns the text position after the parsed number +const char* JSON::parseNumber(const char *num) +{ + const char* num_start = num; + double n=0, sign=1, scale=0; + int subscale = 0, + signsubscale = 1; + + // Could use sscanf for this? + if (*num=='-') + sign=-1,num++; // Has sign? + if (*num=='0') + num++; // is zero + + if (*num>='1' && *num<='9') + { + do + { + n=(n*10.0)+(*num++ -'0'); + } + while (*num>='0' && *num<='9'); // Number? + } + + if (*num=='.' && num[1]>='0' && num[1]<='9') + { + num++; + do + { + n=(n*10.0)+(*num++ -'0'); + scale--; + } + while (*num>='0' && *num<='9'); // Fractional part? + } + + if (*num=='e' || *num=='E') // Exponent? + { + num++; + if (*num=='+') + num++; + else if (*num=='-') + { + signsubscale=-1; + num++; // With sign? + } + + while (*num>='0' && *num<='9') + subscale=(subscale*10)+(*num++ - '0'); // Number? + } + + // Number = +/- number.fraction * 10^+/- exponent + n = sign*n*pow(10.0,(scale+subscale*signsubscale)); + + // Assign parsed value. + Type = JSON_Number; + dValue = n; + Value.AssignString(num_start, num - num_start); + + return num; +} + +// Parses a hex string up to the specified number of digits. +// Returns the first character after the string. +const char* ParseHex(unsigned* val, unsigned digits, const char* str) +{ + *val = 0; + + for(unsigned digitCount = 0; digitCount < digits; digitCount++, str++) + { + unsigned v = *str; + + if ((v >= '0') && (v <= '9')) + v -= '0'; + else if ((v >= 'a') && (v <= 'f')) + v = 10 + v - 'a'; + else if ((v >= 'A') && (v <= 'F')) + v = 10 + v - 'A'; + else + break; + + *val = *val * 16 + v; + } + + return str; +} + +//----------------------------------------------------------------------------- +// Parses the input text into a string item and returns the text position after +// the parsed string +const char* JSON::parseString(const char* str, const char** perror) +{ + const char* ptr = str+1; + const char* p; + char* ptr2; + char* out; + int len=0; + unsigned uc, uc2; + + if (*str!='\"') + { + return AssignError(perror, "Syntax Error: Missing quote"); + } + + while (*ptr!='\"' && *ptr && ++len) + { + if (*ptr++ == '\\') ptr++; // Skip escaped quotes. + } + + // This is how long we need for the string, roughly. + out=(char*)OVR_ALLOC(len+1); + if (!out) + return 0; + + ptr = str+1; + ptr2= out; + + while (*ptr!='\"' && *ptr) + { + if (*ptr!='\\') + { + *ptr2++ = *ptr++; + } + else + { + ptr++; + switch (*ptr) + { + case 'b': *ptr2++ = '\b'; break; + case 'f': *ptr2++ = '\f'; break; + case 'n': *ptr2++ = '\n'; break; + case 'r': *ptr2++ = '\r'; break; + case 't': *ptr2++ = '\t'; break; + + // Transcode utf16 to utf8. + case 'u': + + // Get the unicode char. + p = ParseHex(&uc, 4, ptr + 1); + if (ptr != p) + ptr = p - 1; + + if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) + break; // Check for invalid. + + // UTF16 surrogate pairs. + if (uc>=0xD800 && uc<=0xDBFF) + { + if (ptr[1]!='\\' || ptr[2]!='u') + break; // Missing second-half of surrogate. + + p= ParseHex(&uc2, 4, ptr + 3); + if (ptr != p) + ptr = p - 1; + + if (uc2<0xDC00 || uc2>0xDFFF) + break; // Invalid second-half of surrogate. + + uc = 0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); + } + + len=4; + + if (uc<0x80) + len=1; + else if (uc<0x800) + len=2; + else if (uc<0x10000) + len=3; + + ptr2+=len; + + switch (len) + { + case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 1: *--ptr2 = (char)(uc | firstByteMark[len]); + } + ptr2+=len; + break; + + default: + *ptr2++ = *ptr; + break; + } + ptr++; + } + } + + *ptr2 = 0; + if (*ptr=='\"') + ptr++; + + // Make a copy of the string + Value=out; + OVR_FREE(out); + Type=JSON_String; + + return ptr; +} + +//----------------------------------------------------------------------------- +// Render the string provided to an escaped version that can be printed. +char* PrintString(const char* str) +{ + const char *ptr; + char *ptr2,*out; + int len=0; + unsigned char token; + + if (!str) + return JSON_strdup(""); + ptr=str; + + token=*ptr; + while (token && ++len)\ + { + if (strchr("\"\\\b\f\n\r\t",token)) + len++; + else if (token<32) + len+=5; + ptr++; + token=*ptr; + } + + int buff_size = len+3; + out=(char*)OVR_ALLOC(buff_size); + if (!out) + return 0; + + ptr2 = out; + ptr = str; + *ptr2++ = '\"'; + + while (*ptr) + { + if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') + *ptr2++=*ptr++; + else + { + *ptr2++='\\'; + switch (token=*ptr++) + { + case '\\': *ptr2++='\\'; break; + case '\"': *ptr2++='\"'; break; + case '\b': *ptr2++='b'; break; + case '\f': *ptr2++='f'; break; + case '\n': *ptr2++='n'; break; + case '\r': *ptr2++='r'; break; + case '\t': *ptr2++='t'; break; + default: + OVR_sprintf(ptr2, buff_size - (ptr2-out), "u%04x",token); + ptr2+=5; + break; // Escape and print. + } + } + } + *ptr2++='\"'; + *ptr2++=0; + return out; +} + +//----------------------------------------------------------------------------- +// Utility to jump whitespace and cr/lf +static const char* skip(const char* in) +{ + while (in && *in && (unsigned char)*in<=' ') + in++; + return in; +} + +//----------------------------------------------------------------------------- +// Parses the supplied buffer of JSON text and returns a JSON object tree +// The returned object must be Released after use +JSON* JSON::Parse(const char* buff, const char** perror) +{ + const char* end = 0; + JSON* json = new JSON(); + + if (!json) + { + AssignError(perror, "Error: Failed to allocate memory"); + return 0; + } + + end = json->parseValue(skip(buff), perror); + if (!end) + { + json->Release(); + return NULL; + } // parse failure. ep is set. + + return json; +} + +//----------------------------------------------------------------------------- +// Parser core - when encountering text, process appropriately. +const char* JSON::parseValue(const char* buff, const char** perror) +{ + if (perror) + *perror = 0; + + if (!buff) + return NULL; // Fail on null. + + if (!strncmp(buff,"null",4)) + { + Type = JSON_Null; + return buff+4; + } + if (!strncmp(buff,"false",5)) + { + Type = JSON_Bool; + Value = "false"; + dValue = 0; + return buff+5; + } + if (!strncmp(buff,"true",4)) + { + Type = JSON_Bool; + Value = "true"; + dValue = 1; + return buff+4; + } + if (*buff=='\"') + { + return parseString(buff, perror); + } + if (*buff=='-' || (*buff>='0' && *buff<='9')) + { + return parseNumber(buff); + } + if (*buff=='[') + { + return parseArray(buff, perror); + } + if (*buff=='{') + { + return parseObject(buff, perror); + } + + return AssignError(perror, "Syntax Error: Invalid syntax"); +} + + +//----------------------------------------------------------------------------- +// Render a value to text. +char* JSON::PrintValue(int depth, bool fmt) +{ + char *out=0; + + switch (Type) + { + case JSON_Null: out = JSON_strdup("null"); break; + case JSON_Bool: + if (dValue == 0) + out = JSON_strdup("false"); + else + out = JSON_strdup("true"); + break; + case JSON_Number: out = PrintNumber(dValue); break; + case JSON_String: out = PrintString(Value); break; + case JSON_Array: out = PrintArray(depth, fmt); break; + case JSON_Object: out = PrintObject(depth, fmt); break; + case JSON_None: OVR_ASSERT_LOG(false, ("Bad JSON type.")); break; + } + return out; +} + +//----------------------------------------------------------------------------- +// Build an array object from input text and returns the text position after +// the parsed array +const char* JSON::parseArray(const char* buff, const char** perror) +{ + JSON *child; + if (*buff!='[') + { + return AssignError(perror, "Syntax Error: Missing opening bracket"); + } + + Type=JSON_Array; + buff=skip(buff+1); + + if (*buff==']') + return buff+1; // empty array. + + child = new JSON(); + if (!child) + return 0; // memory fail + Children.PushBack(child); + + buff=skip(child->parseValue(skip(buff), perror)); // skip any spacing, get the buff. + if (!buff) + return 0; + + while (*buff==',') + { + JSON *new_item = new JSON(); + if (!new_item) + return AssignError(perror, "Error: Failed to allocate memory"); + + Children.PushBack(new_item); + + buff=skip(new_item->parseValue(skip(buff+1), perror)); + if (!buff) + return AssignError(perror, "Error: Failed to allocate memory"); + } + + if (*buff==']') + return buff+1; // end of array + + return AssignError(perror, "Syntax Error: Missing ending bracket"); +} + +//----------------------------------------------------------------------------- +// Render an array to text. The returned text must be freed +char* JSON::PrintArray(int depth, bool fmt) +{ + char **entries; + char * out = 0,*ptr,*ret; + SPInt len = 5; + + bool fail = false; + + // How many entries in the array? + int numentries = GetItemCount(); + if (!numentries) + { + out=(char*)OVR_ALLOC(3); + if (out) + OVR_strcpy(out, 3, "[]"); + return out; + } + // Allocate an array to hold the values for each + entries=(char**)OVR_ALLOC(numentries*sizeof(char*)); + if (!entries) + return 0; + memset(entries,0,numentries*sizeof(char*)); + + //// Retrieve all the results: + JSON* child = Children.GetFirst(); + for (int i=0; iPrintValue(depth+1, fmt); + entries[i]=ret; + if (ret) + len+=OVR_strlen(ret)+2+(fmt?1:0); + else + { + fail = true; + break; + } + child = Children.GetNext(child); + } + + // If we didn't fail, try to malloc the output string + if (!fail) + out=(char*)OVR_ALLOC(len); + // If that fails, we fail. + if (!out) + fail = true; + + // Handle failure. + if (fail) + { + for (int i=0; iparseString(skip(buff), perror)); + if (!buff) + return 0; + child->Name = child->Value; + child->Value.Clear(); + + if (*buff!=':') + { + return AssignError(perror, "Syntax Error: Missing colon"); + } + + buff=skip(child->parseValue(skip(buff+1), perror)); // skip any spacing, get the value. + if (!buff) + return 0; + + while (*buff==',') + { + child = new JSON(); + if (!child) + return 0; // memory fail + + Children.PushBack(child); + + buff=skip(child->parseString(skip(buff+1), perror)); + if (!buff) + return 0; + + child->Name=child->Value; + child->Value.Clear(); + + if (*buff!=':') + { + return AssignError(perror, "Syntax Error: Missing colon"); + } // fail! + + // Skip any spacing, get the value. + buff=skip(child->parseValue(skip(buff+1), perror)); + if (!buff) + return 0; + } + + if (*buff=='}') + return buff+1; // end of array + + return AssignError(perror, "Syntax Error: Missing closing brace"); +} + +//----------------------------------------------------------------------------- +// Render an object to text. The returned string must be freed +char* JSON::PrintObject(int depth, bool fmt) +{ + char** entries = 0, **names = 0; + char* out = 0; + char* ptr, *ret, *str; + SPInt len = 7, i = 0, j; + bool fail = false; + + // Count the number of entries. + int numentries = GetItemCount(); + + // Explicitly handle empty object case + if (numentries == 0) + { + out=(char*)OVR_ALLOC(fmt?depth+3:3); + if (!out) + return 0; + ptr=out; + *ptr++='{'; + + if (fmt) + { + *ptr++='\n'; + for (i=0;iName); + entries[i++] = ret = child->PrintValue(depth, fmt); + + if (str && ret) + { + len += OVR_strlen(ret)+OVR_strlen(str)+2+(fmt?2+depth:0); + } + else + { + fail = true; + break; + } + + child = Children.GetNext(child); + } + + // Try to allocate the output string + if (!fail) + out=(char*)OVR_ALLOC(len); + if (!out) + fail=true; + + // Handle failure + if (fail) + { + for (i=0;ipNext) + count++; + return count; +} + +JSON* JSON::GetItemByIndex(unsigned index) +{ + unsigned i = 0; + JSON* child = 0; + + if (!Children.IsEmpty()) + { + child = Children.GetFirst(); + + while (i < index) + { + if (Children.IsNull(child->pNext)) + { + child = 0; + break; + } + child = child->pNext; + i++; + } + } + + return child; +} + +// Returns the child item with the given name or NULL if not found +JSON* JSON::GetItemByName(const char* name) +{ + JSON* child = 0; + + if (!Children.IsEmpty()) + { + child = Children.GetFirst(); + + while (OVR_strcmp(child->Name, name) != 0) + { + if (Children.IsNull(child->pNext)) + { + child = 0; + break; + } + child = child->pNext; + } + } + + return child; +} + +//----------------------------------------------------------------------------- +// Adds a new item to the end of the child list +void JSON::AddItem(const char *string, JSON *item) +{ + if (!item) + return; + + item->Name = string; + Children.PushBack(item); +} + +/* + +// Removes and frees the items at the given index +void JSON::DeleteItem(unsigned int index) +{ + unsigned int num_items = 0; + JSON* child = Children.GetFirst(); + while (!Children.IsNull(child) && num_items < index) + { + num_items++; + child = Children.GetNext(child); + } + + if (!Children.IsNull(child)) + + child->RemoveNode(); + child->Release(); + } +} + +// Replaces and frees the item at the give index with the new item +void JSON::ReplaceItem(unsigned int index, JSON* new_item) +{ + unsigned int num_items = 0; + JSON* child = Children.GetFirst(); + while (!Children.IsNull(child) && num_items < index) + { + num_items++; + child = Children.GetNext(child); + } + + if (!Children.IsNull(child)) + { + child->ReplaceNodeWith(new_item); + child->Release(); + } +} +*/ + +// Helper function to simplify creation of a typed object +JSON* JSON::createHelper(JSONItemType itemType, double dval, const char* strVal) +{ + JSON *item = new JSON(itemType); + if (item) + { + item->dValue = dval; + if (strVal) + item->Value = strVal; + } + return item; +} + + +//----------------------------------------------------------------------------- +// Adds an element to an array object type +void JSON::AddArrayElement(JSON *item) +{ + if (!item) + return; + + Children.PushBack(item); +} + + +// Returns the size of an array +int JSON::GetArraySize() +{ + if (Type == JSON_Array) + return GetItemCount(); + else + return 0; +} + +// Returns the number value an the give array index +double JSON::GetArrayNumber(int index) +{ + if (Type == JSON_Array) + { + JSON* number = GetItemByIndex(index); + return number ? number->dValue : 0.0; + } + else + { + return 0; + } +} + +// Returns the string value at the given array index +const char* JSON::GetArrayString(int index) +{ + if (Type == JSON_Array) + { + JSON* number = GetItemByIndex(index); + return number ? number->Value : 0; + } + else + { + return 0; + } +} + +//----------------------------------------------------------------------------- +// Loads and parses the given JSON file pathname and returns a JSON object tree. +// The returned object must be Released after use. +JSON* JSON::Load(const char* path, const char** perror) +{ + SysFile f; + if (!f.Open(path, File::Open_Read, File::Mode_Read)) + { + AssignError(perror, "Failed to open file"); + return NULL; + } + + int len = f.GetLength(); + UByte* buff = (UByte*)OVR_ALLOC(len); + int bytes = f.Read(buff, len); + f.Close(); + + if (bytes == 0 || bytes != len) + { + OVR_FREE(buff); + return NULL; + } + + JSON* json = JSON::Parse((char*)buff, perror); + OVR_FREE(buff); + return json; +} + +//----------------------------------------------------------------------------- +// Serializes the JSON object and writes to the give file path +bool JSON::Save(const char* path) +{ + SysFile f; + if (!f.Open(path, File::Open_Write | File::Open_Create | File::Open_Truncate, File::Mode_Write)) + return false; + + char* text = PrintValue(0, true); + if (text) + { + SPInt len = OVR_strlen(text); + OVR_ASSERT(len < (SPInt)(int)len); + + int bytes = f.Write((UByte*)text, (int)len); + f.Close(); + OVR_FREE(text); + return (bytes == len); + } + else + { + return false; + } +} + +} diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_JSON.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_JSON.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,143 @@ +/************************************************************************************ + +PublicHeader: None +Filename : OVR_JSON.h +Content : JSON format reader and writer +Created : April 9, 2013 +Author : Brant Lewis +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_JSON_H +#define OVR_JSON_H + +#include "Kernel/OVR_RefCount.h" +#include "Kernel/OVR_String.h" +#include "Kernel/OVR_List.h" + +namespace OVR { + +// JSONItemType describes the type of JSON item, specifying the type of +// data that can be obtained from it. +enum JSONItemType +{ + JSON_None = 0, + JSON_Null = 1, + JSON_Bool = 2, + JSON_Number = 3, + JSON_String = 4, + JSON_Array = 5, + JSON_Object = 6 +}; + + +//----------------------------------------------------------------------------- +// ***** JSON + +// JSON object represents a JSON node that can be either a root of the JSON tree +// or a child item. Every node has a type that describes what is is. +// New JSON trees are typically loaded JSON::Load or created with JSON::Parse. + +class JSON : public RefCountBase, public ListNode +{ +protected: + List Children; + +public: + JSONItemType Type; // Type of this JSON node. + String Name; // Name part of the {Name, Value} pair in a parent object. + String Value; + double dValue; + +public: + ~JSON(); + + // *** Creation of NEW JSON objects + + static JSON* CreateObject() { return new JSON(JSON_Object);} + static JSON* CreateNull() { return new JSON(JSON_Null); } + static JSON* CreateArray() { return new JSON(JSON_Array); } + static JSON* CreateBool(bool b) { return createHelper(JSON_Bool, b ? 1.0 : 0.0); } + static JSON* CreateNumber(double num) { return createHelper(JSON_Number, num); } + static JSON* CreateString(const char *s) { return createHelper(JSON_String, 0.0, s); } + + // Creates a new JSON object from parsing string. + // Returns null pointer and fills in *perror in case of parse error. + static JSON* Parse(const char* buff, const char** perror = 0); + + // Loads and parses a JSON object from a file. + // Returns 0 and assigns perror with error message on fail. + static JSON* Load(const char* path, const char** perror = 0); + + // Saves a JSON object to a file. + bool Save(const char* path); + + + // *** Object Member Access + + // These provide access to child items of the list. + bool HasItems() const { return Children.IsEmpty(); } + // Returns first/last child item, or null if child list is empty + JSON* GetFirstItem() { return (!Children.IsEmpty()) ? Children.GetFirst() : 0; } + JSON* GetLastItem() { return (!Children.IsEmpty()) ? Children.GetLast() : 0; } + + // Counts the number of items in the object; these methods are inefficient. + unsigned GetItemCount() const; + JSON* GetItemByIndex(unsigned i); + JSON* GetItemByName(const char* name); + + // Returns next item in a list of children; 0 if no more items exist. + JSON* GetNextItem(JSON* item) { return Children.IsNull(item->pNext) ? 0 : item->pNext; } + JSON* GetPrevItem(JSON* item) { return Children.IsNull(item->pPrev) ? 0 : item->pPrev; } + + + // Child item access functions + void AddItem(const char *string, JSON* item); + void AddNullItem(const char* name) { AddItem(name, CreateNull()); } + void AddBoolItem(const char* name, bool b) { AddItem(name, CreateBool(b)); } + void AddNumberItem(const char* name, double n) { AddItem(name, CreateNumber(n)); } + void AddStringItem(const char* name, const char* s) { AddItem(name, CreateString(s)); } +// void ReplaceItem(unsigned index, JSON* new_item); +// void DeleteItem(unsigned index); + + // *** Array Element Access + + // Add new elements to the end of array. + void AddArrayElement(JSON *item); + void AddArrayNumber(double n) { AddArrayElement(CreateNumber(n)); } + void AddArrayString(const char* s) { AddArrayElement(CreateString(s)); } + + // Accessed array elements; currently inefficient. + int GetArraySize(); + double GetArrayNumber(int index); + const char* GetArrayString(int index); + + +protected: + JSON(JSONItemType itemType = JSON_Object); + + static JSON* createHelper(JSONItemType itemType, double dval, const char* strVal = 0); + + // JSON Parsing helper functions. + const char* parseValue(const char *buff, const char** perror); + const char* parseNumber(const char *num); + const char* parseArray(const char* value, const char** perror); + const char* parseObject(const char* value, const char** perror); + const char* parseString(const char* str, const char** perror); + + char* PrintValue(int depth, bool fmt); + char* PrintObject(int depth, bool fmt); + char* PrintArray(int depth, bool fmt); +}; + + +} + +#endif diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_LatencyTestImpl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_LatencyTestImpl.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,774 @@ +/************************************************************************************ + +Filename : OVR_LatencyTestImpl.cpp +Content : Oculus Latency Tester device implementation. +Created : March 7, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_LatencyTestImpl.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +// ***** Oculus Latency Tester specific packet data structures + +enum { + LatencyTester_VendorId = Oculus_VendorId, + LatencyTester_ProductId = 0x0101, +}; + +// Reported data is little-endian now +static UInt16 DecodeUInt16(const UByte* buffer) +{ + return (UInt16(buffer[1]) << 8) | UInt16(buffer[0]); +} + +/* Unreferenced +static SInt16 DecodeSInt16(const UByte* buffer) +{ + return (SInt16(buffer[1]) << 8) | SInt16(buffer[0]); +}*/ + +static void UnpackSamples(const UByte* buffer, UByte* r, UByte* g, UByte* b) +{ + *r = buffer[0]; + *g = buffer[1]; + *b = buffer[2]; +} + +// Messages we handle. +enum LatencyTestMessageType +{ + LatencyTestMessage_None = 0, + LatencyTestMessage_Samples = 1, + LatencyTestMessage_ColorDetected = 2, + LatencyTestMessage_TestStarted = 3, + LatencyTestMessage_Button = 4, + LatencyTestMessage_Unknown = 0x100, + LatencyTestMessage_SizeError = 0x101, +}; + +struct LatencyTestSample +{ + UByte Value[3]; +}; + +struct LatencyTestSamples +{ + UByte SampleCount; + UInt16 Timestamp; + + LatencyTestSample Samples[20]; + + LatencyTestMessageType Decode(const UByte* buffer, int size) + { + if (size < 64) + { + return LatencyTestMessage_SizeError; + } + + SampleCount = buffer[1]; + Timestamp = DecodeUInt16(buffer + 2); + + for (UByte i = 0; i < SampleCount; i++) + { + UnpackSamples(buffer + 4 + (3 * i), &Samples[i].Value[0], &Samples[i].Value[1], &Samples[i].Value[2]); + } + + return LatencyTestMessage_Samples; + } +}; + +struct LatencyTestSamplesMessage +{ + LatencyTestMessageType Type; + LatencyTestSamples Samples; +}; + +bool DecodeLatencyTestSamplesMessage(LatencyTestSamplesMessage* message, UByte* buffer, int size) +{ + memset(message, 0, sizeof(LatencyTestSamplesMessage)); + + if (size < 64) + { + message->Type = LatencyTestMessage_SizeError; + return false; + } + + switch (buffer[0]) + { + case LatencyTestMessage_Samples: + message->Type = message->Samples.Decode(buffer, size); + break; + + default: + message->Type = LatencyTestMessage_Unknown; + break; + } + + return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None); +} + +struct LatencyTestColorDetected +{ + UInt16 CommandID; + UInt16 Timestamp; + UInt16 Elapsed; + UByte TriggerValue[3]; + UByte TargetValue[3]; + + LatencyTestMessageType Decode(const UByte* buffer, int size) + { + if (size < 13) + return LatencyTestMessage_SizeError; + + CommandID = DecodeUInt16(buffer + 1); + Timestamp = DecodeUInt16(buffer + 3); + Elapsed = DecodeUInt16(buffer + 5); + memcpy(TriggerValue, buffer + 7, 3); + memcpy(TargetValue, buffer + 10, 3); + + return LatencyTestMessage_ColorDetected; + } +}; + +struct LatencyTestColorDetectedMessage +{ + LatencyTestMessageType Type; + LatencyTestColorDetected ColorDetected; +}; + +bool DecodeLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message, UByte* buffer, int size) +{ + memset(message, 0, sizeof(LatencyTestColorDetectedMessage)); + + if (size < 13) + { + message->Type = LatencyTestMessage_SizeError; + return false; + } + + switch (buffer[0]) + { + case LatencyTestMessage_ColorDetected: + message->Type = message->ColorDetected.Decode(buffer, size); + break; + + default: + message->Type = LatencyTestMessage_Unknown; + break; + } + + return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None); +} + +struct LatencyTestStarted +{ + UInt16 CommandID; + UInt16 Timestamp; + UByte TargetValue[3]; + + LatencyTestMessageType Decode(const UByte* buffer, int size) + { + if (size < 8) + return LatencyTestMessage_SizeError; + + CommandID = DecodeUInt16(buffer + 1); + Timestamp = DecodeUInt16(buffer + 3); + memcpy(TargetValue, buffer + 5, 3); + + return LatencyTestMessage_TestStarted; + } +}; + +struct LatencyTestStartedMessage +{ + LatencyTestMessageType Type; + LatencyTestStarted TestStarted; +}; + +bool DecodeLatencyTestStartedMessage(LatencyTestStartedMessage* message, UByte* buffer, int size) +{ + memset(message, 0, sizeof(LatencyTestStartedMessage)); + + if (size < 8) + { + message->Type = LatencyTestMessage_SizeError; + return false; + } + + switch (buffer[0]) + { + case LatencyTestMessage_TestStarted: + message->Type = message->TestStarted.Decode(buffer, size); + break; + + default: + message->Type = LatencyTestMessage_Unknown; + break; + } + + return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None); +} + +struct LatencyTestButton +{ + UInt16 CommandID; + UInt16 Timestamp; + + LatencyTestMessageType Decode(const UByte* buffer, int size) + { + if (size < 5) + return LatencyTestMessage_SizeError; + + CommandID = DecodeUInt16(buffer + 1); + Timestamp = DecodeUInt16(buffer + 3); + + return LatencyTestMessage_Button; + } +}; + +struct LatencyTestButtonMessage +{ + LatencyTestMessageType Type; + LatencyTestButton Button; +}; + +bool DecodeLatencyTestButtonMessage(LatencyTestButtonMessage* message, UByte* buffer, int size) +{ + memset(message, 0, sizeof(LatencyTestButtonMessage)); + + if (size < 5) + { + message->Type = LatencyTestMessage_SizeError; + return false; + } + + switch (buffer[0]) + { + case LatencyTestMessage_Button: + message->Type = message->Button.Decode(buffer, size); + break; + + default: + message->Type = LatencyTestMessage_Unknown; + break; + } + + return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None); +} + +struct LatencyTestConfigurationImpl +{ + enum { PacketSize = 5 }; + UByte Buffer[PacketSize]; + + OVR::LatencyTestConfiguration Configuration; + + LatencyTestConfigurationImpl(const OVR::LatencyTestConfiguration& configuration) + : Configuration(configuration) + { + Pack(); + } + + void Pack() + { + Buffer[0] = 5; + Buffer[1] = UByte(Configuration.SendSamples); + Buffer[2] = Configuration.Threshold.R; + Buffer[3] = Configuration.Threshold.G; + Buffer[4] = Configuration.Threshold.B; + } + + void Unpack() + { + Configuration.SendSamples = Buffer[1] != 0 ? true : false; + Configuration.Threshold.R = Buffer[2]; + Configuration.Threshold.G = Buffer[3]; + Configuration.Threshold.B = Buffer[4]; + } +}; + +struct LatencyTestCalibrateImpl +{ + enum { PacketSize = 4 }; + UByte Buffer[PacketSize]; + + Color CalibrationColor; + + LatencyTestCalibrateImpl(const Color& calibrationColor) + : CalibrationColor(calibrationColor) + { + Pack(); + } + + void Pack() + { + Buffer[0] = 7; + Buffer[1] = CalibrationColor.R; + Buffer[2] = CalibrationColor.G; + Buffer[3] = CalibrationColor.B; + } + + void Unpack() + { + CalibrationColor.R = Buffer[1]; + CalibrationColor.G = Buffer[2]; + CalibrationColor.B = Buffer[3]; + } +}; + +struct LatencyTestStartTestImpl +{ + enum { PacketSize = 6 }; + UByte Buffer[PacketSize]; + + Color TargetColor; + + LatencyTestStartTestImpl(const Color& targetColor) + : TargetColor(targetColor) + { + Pack(); + } + + void Pack() + { + UInt16 commandID = 1; + + Buffer[0] = 8; + Buffer[1] = UByte(commandID & 0xFF); + Buffer[2] = UByte(commandID >> 8); + Buffer[3] = TargetColor.R; + Buffer[4] = TargetColor.G; + Buffer[5] = TargetColor.B; + } + + void Unpack() + { +// UInt16 commandID = Buffer[1] | (UInt16(Buffer[2]) << 8); + TargetColor.R = Buffer[3]; + TargetColor.G = Buffer[4]; + TargetColor.B = Buffer[5]; + } +}; + +struct LatencyTestDisplayImpl +{ + enum { PacketSize = 6 }; + UByte Buffer[PacketSize]; + + OVR::LatencyTestDisplay Display; + + LatencyTestDisplayImpl(const OVR::LatencyTestDisplay& display) + : Display(display) + { + Pack(); + } + + void Pack() + { + Buffer[0] = 9; + Buffer[1] = Display.Mode; + Buffer[2] = UByte(Display.Value & 0xFF); + Buffer[3] = UByte((Display.Value >> 8) & 0xFF); + Buffer[4] = UByte((Display.Value >> 16) & 0xFF); + Buffer[5] = UByte((Display.Value >> 24) & 0xFF); + } + + void Unpack() + { + Display.Mode = Buffer[1]; + Display.Value = UInt32(Buffer[2]) | + (UInt32(Buffer[3]) << 8) | + (UInt32(Buffer[4]) << 16) | + (UInt32(Buffer[5]) << 24); + } +}; + +//------------------------------------------------------------------------------------- +// ***** LatencyTestDeviceFactory + +LatencyTestDeviceFactory LatencyTestDeviceFactory::Instance; + +void LatencyTestDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) +{ + + class LatencyTestEnumerator : public HIDEnumerateVisitor + { + // Assign not supported; suppress MSVC warning. + void operator = (const LatencyTestEnumerator&) { } + + DeviceFactory* pFactory; + EnumerateVisitor& ExternalVisitor; + public: + LatencyTestEnumerator(DeviceFactory* factory, EnumerateVisitor& externalVisitor) + : pFactory(factory), ExternalVisitor(externalVisitor) { } + + virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) + { + return pFactory->MatchVendorProduct(vendorId, productId); + } + + virtual void Visit(HIDDevice& device, const HIDDeviceDesc& desc) + { + OVR_UNUSED(device); + + LatencyTestDeviceCreateDesc createDesc(pFactory, desc); + ExternalVisitor.Visit(createDesc); + } + }; + + LatencyTestEnumerator latencyTestEnumerator(this, visitor); + GetManagerImpl()->GetHIDDeviceManager()->Enumerate(&latencyTestEnumerator); +} + +bool LatencyTestDeviceFactory::MatchVendorProduct(UInt16 vendorId, UInt16 productId) const +{ + return ((vendorId == LatencyTester_VendorId) && (productId == LatencyTester_ProductId)); +} + +bool LatencyTestDeviceFactory::DetectHIDDevice(DeviceManager* pdevMgr, + const HIDDeviceDesc& desc) +{ + if (MatchVendorProduct(desc.VendorId, desc.ProductId)) + { + LatencyTestDeviceCreateDesc createDesc(this, desc); + return pdevMgr->AddDevice_NeedsLock(createDesc).GetPtr() != NULL; + } + return false; +} + +//------------------------------------------------------------------------------------- +// ***** LatencyTestDeviceCreateDesc + +DeviceBase* LatencyTestDeviceCreateDesc::NewDeviceInstance() +{ + return new LatencyTestDeviceImpl(this); +} + +bool LatencyTestDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_LatencyTester) && + (info->InfoClassType != Device_None)) + return false; + + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, HIDDesc.Product.ToCStr()); + OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, HIDDesc.Manufacturer.ToCStr()); + info->Type = Device_LatencyTester; + info->Version = 0; + + if (info->InfoClassType == Device_LatencyTester) + { + SensorInfo* sinfo = (SensorInfo*)info; + sinfo->VendorId = HIDDesc.VendorId; + sinfo->ProductId = HIDDesc.ProductId; + OVR_strcpy(sinfo->SerialNumber, sizeof(sinfo->SerialNumber),HIDDesc.SerialNumber.ToCStr()); + } + return true; +} + +//------------------------------------------------------------------------------------- +// ***** LatencyTestDevice + +LatencyTestDeviceImpl::LatencyTestDeviceImpl(LatencyTestDeviceCreateDesc* createDesc) + : OVR::HIDDeviceImpl(createDesc, 0) +{ +} + +LatencyTestDeviceImpl::~LatencyTestDeviceImpl() +{ + // Check that Shutdown() was called. + OVR_ASSERT(!pCreateDesc->pDevice); +} + +// Internal creation APIs. +bool LatencyTestDeviceImpl::Initialize(DeviceBase* parent) +{ + if (HIDDeviceImpl::Initialize(parent)) + { + LogText("OVR::LatencyTestDevice initialized.\n"); + return true; + } + + return false; +} + +void LatencyTestDeviceImpl::Shutdown() +{ + HIDDeviceImpl::Shutdown(); + + LogText("OVR::LatencyTestDevice - Closed '%s'\n", getHIDDesc()->Path.ToCStr()); +} + +void LatencyTestDeviceImpl::OnInputReport(UByte* pData, UInt32 length) +{ + + bool processed = false; + if (!processed) + { + LatencyTestSamplesMessage message; + if (DecodeLatencyTestSamplesMessage(&message, pData, length)) + { + processed = true; + onLatencyTestSamplesMessage(&message); + } + } + + if (!processed) + { + LatencyTestColorDetectedMessage message; + if (DecodeLatencyTestColorDetectedMessage(&message, pData, length)) + { + processed = true; + onLatencyTestColorDetectedMessage(&message); + } + } + + if (!processed) + { + LatencyTestStartedMessage message; + if (DecodeLatencyTestStartedMessage(&message, pData, length)) + { + processed = true; + onLatencyTestStartedMessage(&message); + } + } + + if (!processed) + { + LatencyTestButtonMessage message; + if (DecodeLatencyTestButtonMessage(&message, pData, length)) + { + processed = true; + onLatencyTestButtonMessage(&message); + } + } +} + +bool LatencyTestDeviceImpl::SetConfiguration(const OVR::LatencyTestConfiguration& configuration, bool waitFlag) +{ + bool result = false; + ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue(); + + if (GetManagerImpl()->GetThreadId() != OVR::GetCurrentThreadId()) + { + if (!waitFlag) + { + return queue->PushCall(this, &LatencyTestDeviceImpl::setConfiguration, configuration); + } + + if (!queue->PushCallAndWaitResult( this, + &LatencyTestDeviceImpl::setConfiguration, + &result, + configuration)) + { + return false; + } + } + else + return setConfiguration(configuration); + + return result; +} + +bool LatencyTestDeviceImpl::setConfiguration(const OVR::LatencyTestConfiguration& configuration) +{ + LatencyTestConfigurationImpl ltc(configuration); + return GetInternalDevice()->SetFeatureReport(ltc.Buffer, LatencyTestConfigurationImpl::PacketSize); +} + +bool LatencyTestDeviceImpl::GetConfiguration(OVR::LatencyTestConfiguration* configuration) +{ + bool result = false; + + ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue(); + if (!pQueue->PushCallAndWaitResult(this, &LatencyTestDeviceImpl::getConfiguration, &result, configuration)) + return false; + + return result; +} + +bool LatencyTestDeviceImpl::getConfiguration(OVR::LatencyTestConfiguration* configuration) +{ + LatencyTestConfigurationImpl ltc(*configuration); + if (GetInternalDevice()->GetFeatureReport(ltc.Buffer, LatencyTestConfigurationImpl::PacketSize)) + { + ltc.Unpack(); + *configuration = ltc.Configuration; + return true; + } + + return false; +} + +bool LatencyTestDeviceImpl::SetCalibrate(const Color& calibrationColor, bool waitFlag) +{ + bool result = false; + ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue(); + + if (!waitFlag) + { + return queue->PushCall(this, &LatencyTestDeviceImpl::setCalibrate, calibrationColor); + } + + if (!queue->PushCallAndWaitResult( this, + &LatencyTestDeviceImpl::setCalibrate, + &result, + calibrationColor)) + { + return false; + } + + return result; +} + +bool LatencyTestDeviceImpl::setCalibrate(const Color& calibrationColor) +{ + LatencyTestCalibrateImpl ltc(calibrationColor); + return GetInternalDevice()->SetFeatureReport(ltc.Buffer, LatencyTestCalibrateImpl::PacketSize); +} + +bool LatencyTestDeviceImpl::SetStartTest(const Color& targetColor, bool waitFlag) +{ + bool result = false; + ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue(); + + if (!waitFlag) + { + return queue->PushCall(this, &LatencyTestDeviceImpl::setStartTest, targetColor); + } + + if (!queue->PushCallAndWaitResult( this, + &LatencyTestDeviceImpl::setStartTest, + &result, + targetColor)) + { + return false; + } + + return result; +} + +bool LatencyTestDeviceImpl::setStartTest(const Color& targetColor) +{ + LatencyTestStartTestImpl ltst(targetColor); + return GetInternalDevice()->SetFeatureReport(ltst.Buffer, LatencyTestStartTestImpl::PacketSize); +} + +bool LatencyTestDeviceImpl::SetDisplay(const OVR::LatencyTestDisplay& display, bool waitFlag) +{ + bool result = false; + ThreadCommandQueue * queue = GetManagerImpl()->GetThreadQueue(); + + if (!waitFlag) + { + return queue->PushCall(this, &LatencyTestDeviceImpl::setDisplay, display); + } + + if (!queue->PushCallAndWaitResult( this, + &LatencyTestDeviceImpl::setDisplay, + &result, + display)) + { + return false; + } + + return result; +} + +bool LatencyTestDeviceImpl::setDisplay(const OVR::LatencyTestDisplay& display) +{ + LatencyTestDisplayImpl ltd(display); + return GetInternalDevice()->SetFeatureReport(ltd.Buffer, LatencyTestDisplayImpl::PacketSize); +} + +void LatencyTestDeviceImpl::onLatencyTestSamplesMessage(LatencyTestSamplesMessage* message) +{ + + if (message->Type != LatencyTestMessage_Samples) + return; + + LatencyTestSamples& s = message->Samples; + + // Call OnMessage() within a lock to avoid conflicts with handlers. + Lock::Locker scopeLock(HandlerRef.GetLock()); + + if (HandlerRef.GetHandler()) + { + MessageLatencyTestSamples samples(this); + for (UByte i = 0; i < s.SampleCount; i++) + { + samples.Samples.PushBack(Color(s.Samples[i].Value[0], s.Samples[i].Value[1], s.Samples[i].Value[2])); + } + + HandlerRef.GetHandler()->OnMessage(samples); + } +} + +void LatencyTestDeviceImpl::onLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message) +{ + if (message->Type != LatencyTestMessage_ColorDetected) + return; + + LatencyTestColorDetected& s = message->ColorDetected; + + // Call OnMessage() within a lock to avoid conflicts with handlers. + Lock::Locker scopeLock(HandlerRef.GetLock()); + + if (HandlerRef.GetHandler()) + { + MessageLatencyTestColorDetected detected(this); + detected.Elapsed = s.Elapsed; + detected.DetectedValue = Color(s.TriggerValue[0], s.TriggerValue[1], s.TriggerValue[2]); + detected.TargetValue = Color(s.TargetValue[0], s.TargetValue[1], s.TargetValue[2]); + + HandlerRef.GetHandler()->OnMessage(detected); + } +} + +void LatencyTestDeviceImpl::onLatencyTestStartedMessage(LatencyTestStartedMessage* message) +{ + if (message->Type != LatencyTestMessage_TestStarted) + return; + + LatencyTestStarted& ts = message->TestStarted; + + // Call OnMessage() within a lock to avoid conflicts with handlers. + Lock::Locker scopeLock(HandlerRef.GetLock()); + + if (HandlerRef.GetHandler()) + { + MessageLatencyTestStarted started(this); + started.TargetValue = Color(ts.TargetValue[0], ts.TargetValue[1], ts.TargetValue[2]); + + HandlerRef.GetHandler()->OnMessage(started); + } +} + +void LatencyTestDeviceImpl::onLatencyTestButtonMessage(LatencyTestButtonMessage* message) +{ + if (message->Type != LatencyTestMessage_Button) + return; + +// LatencyTestButton& s = message->Button; + + // Call OnMessage() within a lock to avoid conflicts with handlers. + Lock::Locker scopeLock(HandlerRef.GetLock()); + + if (HandlerRef.GetHandler()) + { + MessageLatencyTestButton button(this); + + HandlerRef.GetHandler()->OnMessage(button); + } +} + +} // namespace OVR diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_LatencyTestImpl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_LatencyTestImpl.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,133 @@ +/************************************************************************************ + +Filename : OVR_LatencyTestImpl.h +Content : Latency Tester specific implementation. +Created : March 7, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_LatencyTestImpl_h +#define OVR_LatencyTestImpl_h + +#include "OVR_HIDDeviceImpl.h" + +namespace OVR { + +struct LatencyTestSamplesMessage; +struct LatencyTestButtonMessage; +struct LatencyTestStartedMessage; +struct LatencyTestColorDetectedMessage; + +//------------------------------------------------------------------------------------- +// LatencyTestDeviceFactory enumerates Oculus Latency Tester devices. +class LatencyTestDeviceFactory : public DeviceFactory +{ +public: + static LatencyTestDeviceFactory Instance; + + // Enumerates devices, creating and destroying relevant objects in manager. + virtual void EnumerateDevices(EnumerateVisitor& visitor); + + virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const; + virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc); + +protected: + DeviceManager* getManager() const { return (DeviceManager*) pManager; } +}; + + +// Describes a single a Oculus Latency Tester device and supports creating its instance. +class LatencyTestDeviceCreateDesc : public HIDDeviceCreateDesc +{ +public: + LatencyTestDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc) + : HIDDeviceCreateDesc(factory, Device_LatencyTester, hidDesc) { } + + virtual DeviceCreateDesc* Clone() const + { + return new LatencyTestDeviceCreateDesc(*this); + } + + virtual DeviceBase* NewDeviceInstance(); + + virtual MatchResult MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc**) const + { + if ((other.Type == Device_LatencyTester) && (pFactory == other.pFactory)) + { + const LatencyTestDeviceCreateDesc& s2 = (const LatencyTestDeviceCreateDesc&) other; + if (MatchHIDDevice(s2.HIDDesc)) + return Match_Found; + } + return Match_None; + } + + virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const + { + // should paths comparison be case insensitive? + return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) && + (HIDDesc.SerialNumber == hidDesc.SerialNumber)); + } + virtual bool GetDeviceInfo(DeviceInfo* info) const; +}; + + +//------------------------------------------------------------------------------------- +// ***** OVR::LatencyTestDeviceImpl + +// Oculus Latency Tester interface. + +class LatencyTestDeviceImpl : public HIDDeviceImpl +{ +public: + LatencyTestDeviceImpl(LatencyTestDeviceCreateDesc* createDesc); + ~LatencyTestDeviceImpl(); + + // DeviceCommon interface. + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + // DeviceManagerThread::Notifier interface. + virtual void OnInputReport(UByte* pData, UInt32 length); + + // LatencyTesterDevice interface + virtual bool SetConfiguration(const OVR::LatencyTestConfiguration& configuration, bool waitFlag = false); + virtual bool GetConfiguration(OVR::LatencyTestConfiguration* configuration); + + virtual bool SetCalibrate(const Color& calibrationColor, bool waitFlag = false); + + virtual bool SetStartTest(const Color& targetColor, bool waitFlag = false); + virtual bool SetDisplay(const LatencyTestDisplay& display, bool waitFlag = false); + +protected: + bool openDevice(const char** errorFormatString); + void closeDevice(); + void closeDeviceOnIOError(); + + bool initializeRead(); + bool processReadResult(); + + bool setConfiguration(const OVR::LatencyTestConfiguration& configuration); + bool getConfiguration(OVR::LatencyTestConfiguration* configuration); + bool setCalibrate(const Color& calibrationColor); + bool setStartTest(const Color& targetColor); + bool setDisplay(const OVR::LatencyTestDisplay& display); + + // Called for decoded messages + void onLatencyTestSamplesMessage(LatencyTestSamplesMessage* message); + void onLatencyTestButtonMessage(LatencyTestButtonMessage* message); + void onLatencyTestStartedMessage(LatencyTestStartedMessage* message); + void onLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message); + +}; + +} // namespace OVR + +#endif // OVR_LatencyTestImpl_h diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_Profile.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_Profile.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,671 @@ +/************************************************************************************ + +PublicHeader: None +Filename : OVR_Profile.cpp +Content : Structs and functions for loading and storing device profile settings +Created : February 14, 2013 +Notes : + + Profiles are used to store per-user settings that can be transferred and used + across multiple applications. For example, player IPD can be configured once + and reused for a unified experience across games. Configuration and saving of profiles + can be accomplished in game via the Profile API or by the official Oculus Configuration + Utility. + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Profile.h" +#include "OVR_JSON.h" +#include "Kernel/OVR_Types.h" +#include "Kernel/OVR_SysFile.h" +#include "Kernel/OVR_Allocator.h" +#include "Kernel/OVR_Array.h" + +#ifdef OVR_OS_WIN32 +#include +#else +#include +#include + +#ifdef OVR_OS_LINUX +#include +#include +#endif + +#endif + +#define PROFILE_VERSION 1.0 + +namespace OVR { + +//----------------------------------------------------------------------------- +// Returns the pathname of the JSON file containing the stored profiles +String GetBaseOVRPath(bool create_dir) +{ + String path; + +#if defined(OVR_OS_WIN32) + + TCHAR data_path[MAX_PATH]; + SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, NULL, 0, data_path); + path = String(data_path); + + path += "/Oculus"; + + if (create_dir) + { // Create the Oculus directory if it doesn't exist + WCHAR wpath[128]; + OVR::UTF8Util::DecodeString(wpath, path.ToCStr()); + + DWORD attrib = GetFileAttributes(wpath); + bool exists = attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY); + if (!exists) + { + CreateDirectory(wpath, NULL); + } + } + +#elif defined(OVR_OS_MAC) + + const char* home = getenv("HOME"); + path = home; + path += "/Library/Preferences/Oculus"; + + if (create_dir) + { // Create the Oculus directory if it doesn't exist + DIR* dir = opendir(path); + if (dir == NULL) + { + mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO); + } + else + { + closedir(dir); + } + } + +#else + + passwd* pwd = getpwuid(getuid()); + const char* home = pwd->pw_dir; + path = home; + path += "/.config/Oculus"; + + if (create_dir) + { // Create the Oculus directory if it doesn't exist + DIR* dir = opendir(path); + if (dir == NULL) + { + mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO); + } + else + { + closedir(dir); + } + } + +#endif + + return path; +} + +String GetProfilePath(bool create_dir) +{ + String path = GetBaseOVRPath(create_dir); + path += "/Profiles.json"; + return path; +} + +//----------------------------------------------------------------------------- +// ***** ProfileManager + +ProfileManager::ProfileManager() +{ + Changed = false; + CacheDevice = Profile_Unknown; +} + +ProfileManager::~ProfileManager() +{ + // If the profiles have been altered then write out the profile file + if (Changed) + SaveCache(); + + ClearCache(); +} + +ProfileManager* ProfileManager::Create() +{ + return new ProfileManager(); +} + +Profile* ProfileManager::CreateProfileObject(const char* user, + ProfileType device, + const char** device_name) +{ + Lock::Locker lockScope(&ProfileLock); + + Profile* profile = NULL; + switch (device) + { + case Profile_RiftDK1: + *device_name = "RiftDK1"; + profile = new RiftDK1Profile(user); + break; + case Profile_RiftDKHD: + case Profile_Unknown: + break; + } + + return profile; +} + + +// Clear the local profile cache +void ProfileManager::ClearCache() +{ + Lock::Locker lockScope(&ProfileLock); + + ProfileCache.Clear(); + CacheDevice = Profile_Unknown; +} + + +// Poplulates the local profile cache. This occurs on the first access of the profile +// data. All profile operations are performed against the local cache until the +// ProfileManager is released or goes out of scope at which time the cache is serialized +// to disk. +void ProfileManager::LoadCache(ProfileType device) +{ + Lock::Locker lockScope(&ProfileLock); + + ClearCache(); + + String path = GetProfilePath(false); + + Ptr root = *JSON::Load(path); + if (!root || root->GetItemCount() < 3) + return; + + // First read the file type and version to make sure this is a valid file + JSON* item0 = root->GetFirstItem(); + JSON* item1 = root->GetNextItem(item0); + JSON* item2 = root->GetNextItem(item1); + + if (OVR_strcmp(item0->Name, "Oculus Profile Version") == 0) + { // In the future I may need to check versioning to determine parse method + } + else + { + return; + } + + DefaultProfile = item1->Value; + + // Read the number of profiles + int profileCount = (int)item2->dValue; + JSON* profileItem = item2; + + for (int p=0; pGetNextItem(profileItem); + if (!profileItem) + break; + + // Read the required Name field + const char* profileName; + JSON* item = profileItem->GetFirstItem(); + + if (item && (OVR_strcmp(item->Name, "Name") == 0)) + { + profileName = item->Value; + } + else + { + return; // invalid field + } + + const char* deviceName = 0; + bool deviceFound = false; + Ptr profile = *CreateProfileObject(profileName, device, &deviceName); + + // Read the base profile fields. + if (profile) + { + while (item = profileItem->GetNextItem(item), item) + { + if (item->Type != JSON_Object) + { + profile->ParseProperty(item->Name, item->Value); + } + else + { // Search for the matching device to get device specific fields + if (!deviceFound && OVR_strcmp(item->Name, deviceName) == 0) + { + deviceFound = true; + + for (JSON* deviceItem = item->GetFirstItem(); deviceItem; + deviceItem = item->GetNextItem(deviceItem)) + { + profile->ParseProperty(deviceItem->Name, deviceItem->Value); + } + } + } + } + } + + // Add the new profile + if (deviceFound) + ProfileCache.PushBack(profile); + } + + CacheDevice = device; +} + + +// Serializes the profiles to disk. +void ProfileManager::SaveCache() +{ + String path = GetProfilePath(true); + + Lock::Locker lockScope(&ProfileLock); + + // TODO: Since there is only a single device type now, a full tree overwrite + // is sufficient but in the future a selective device branch replacement will + // be necessary + + Ptr root = *JSON::CreateObject(); + root->AddNumberItem("Oculus Profile Version", PROFILE_VERSION); + root->AddStringItem("CurrentProfile", DefaultProfile); + root->AddNumberItem("ProfileCount", (double) ProfileCache.GetSize()); + + // Generate a JSON subtree for each profile + for (unsigned int i=0; iName = "Profile"; + json_profile->AddStringItem("Name", profile->Name); + const char* gender; + switch (profile->GetGender()) + { + case Profile::Gender_Male: gender = "Male"; break; + case Profile::Gender_Female: gender = "Female"; break; + default: gender = "Unspecified"; + } + json_profile->AddStringItem("Gender", gender); + json_profile->AddNumberItem("PlayerHeight", profile->PlayerHeight); + json_profile->AddNumberItem("IPD", profile->IPD); + + if (profile->Type == Profile_RiftDK1) + { + RiftDK1Profile* riftdk1 = (RiftDK1Profile*)profile; + JSON* json_riftdk1 = JSON::CreateObject(); + json_profile->AddItem("RiftDK1", json_riftdk1); + + const char* eyecup = "A"; + switch (riftdk1->EyeCups) + { + case RiftDK1Profile::EyeCup_A: eyecup = "A"; break; + case RiftDK1Profile::EyeCup_B: eyecup = "B"; break; + case RiftDK1Profile::EyeCup_C: eyecup = "C"; break; + } + json_riftdk1->AddStringItem("EyeCup", eyecup); + json_riftdk1->AddNumberItem("LL", riftdk1->LL); + json_riftdk1->AddNumberItem("LR", riftdk1->LR); + json_riftdk1->AddNumberItem("RL", riftdk1->RL); + json_riftdk1->AddNumberItem("RR", riftdk1->RR); + } + + root->AddItem("Profile", json_profile); + } + + root->Save(path); +} + +// Returns the number of stored profiles for this device type +int ProfileManager::GetProfileCount(ProfileType device) +{ + Lock::Locker lockScope(&ProfileLock); + + if (CacheDevice == Profile_Unknown) + LoadCache(device); + + return (int)ProfileCache.GetSize(); +} + +// Returns the profile name of a specific profile in the list. The returned +// memory is locally allocated and should not be stored or deleted. Returns NULL +// if the index is invalid +const char* ProfileManager::GetProfileName(ProfileType device, unsigned int index) +{ + Lock::Locker lockScope(&ProfileLock); + + if (CacheDevice == Profile_Unknown) + LoadCache(device); + + if (index < ProfileCache.GetSize()) + { + Profile* profile = ProfileCache[index]; + OVR_strcpy(NameBuff, Profile::MaxNameLen, profile->Name); + return NameBuff; + } + else + { + return NULL; + } +} + +bool ProfileManager::HasProfile(ProfileType device, const char* name) +{ + Lock::Locker lockScope(&ProfileLock); + + if (CacheDevice == Profile_Unknown) + LoadCache(device); + + for (unsigned i = 0; i< ProfileCache.GetSize(); i++) + { + if (ProfileCache[i] && OVR_strcmp(ProfileCache[i]->Name, name) == 0) + return true; + } + return false; +} + + +// Returns a specific profile object in the list. The returned memory should be +// encapsulated in a Ptr<> object or released after use. Returns NULL if the index +// is invalid +Profile* ProfileManager::LoadProfile(ProfileType device, unsigned int index) +{ + Lock::Locker lockScope(&ProfileLock); + + if (CacheDevice == Profile_Unknown) + LoadCache(device); + + if (index < ProfileCache.GetSize()) + { + Profile* profile = ProfileCache[index]; + return profile->Clone(); + } + else + { + return NULL; + } +} + +// Returns a profile object for a particular device and user name. The returned +// memory should be encapsulated in a Ptr<> object or released after use. Returns +// NULL if the profile is not found +Profile* ProfileManager::LoadProfile(ProfileType device, const char* user) +{ + if (user == NULL) + return NULL; + + Lock::Locker lockScope(&ProfileLock); + + if (CacheDevice == Profile_Unknown) + LoadCache(device); + + for (unsigned int i=0; iName) == 0) + { // Found the requested user profile + Profile* profile = ProfileCache[i]; + return profile->Clone(); + } + } + + return NULL; +} + +// Returns a profile with all system default values +Profile* ProfileManager::GetDeviceDefaultProfile(ProfileType device) +{ + const char* device_name = NULL; + return CreateProfileObject("default", device, &device_name); +} + +// Returns the name of the profile that is marked as the current default user. +const char* ProfileManager::GetDefaultProfileName(ProfileType device) +{ + Lock::Locker lockScope(&ProfileLock); + + if (CacheDevice == Profile_Unknown) + LoadCache(device); + + if (ProfileCache.GetSize() > 0) + { + OVR_strcpy(NameBuff, Profile::MaxNameLen, DefaultProfile); + return NameBuff; + } + else + { + return NULL; + } +} + +// Marks a particular user as the current default user. +bool ProfileManager::SetDefaultProfileName(ProfileType device, const char* name) +{ + Lock::Locker lockScope(&ProfileLock); + + if (CacheDevice == Profile_Unknown) + LoadCache(device); +// TODO: I should verify that the user is valid + if (ProfileCache.GetSize() > 0) + { + DefaultProfile = name; + Changed = true; + return true; + } + else + { + return false; + } +} + + +// Saves a new or existing profile. Returns true on success or false on an +// invalid or failed save. +bool ProfileManager::Save(const Profile* profile) +{ + Lock::Locker lockScope(&ProfileLock); + + if (OVR_strcmp(profile->Name, "default") == 0) + return false; // don't save a default profile + + // TODO: I should also verify that this profile type matches the current cache + if (CacheDevice == Profile_Unknown) + LoadCache(profile->Type); + + // Look for the pre-existence of this profile + bool added = false; + for (unsigned int i=0; iName, ProfileCache[i]->Name); + + if (compare == 0) + { + // TODO: I should do a proper field comparison to avoid unnecessary + // overwrites and file saves + + // Replace the previous instance with the new profile + ProfileCache[i] = *profile->Clone(); + added = true; + Changed = true; + break; + } + } + + if (!added) + { + ProfileCache.PushBack(*profile->Clone()); + if (ProfileCache.GetSize() == 1) + CacheDevice = profile->Type; + + Changed = true; + } + + return true; +} + +// Removes an existing profile. Returns true if the profile was found and deleted +// and returns false otherwise. +bool ProfileManager::Delete(const Profile* profile) +{ + Lock::Locker lockScope(&ProfileLock); + + if (OVR_strcmp(profile->Name, "default") == 0) + return false; // don't delete a default profile + + if (CacheDevice == Profile_Unknown) + LoadCache(profile->Type); + + // Look for the existence of this profile + for (unsigned int i=0; iName, ProfileCache[i]->Name) == 0) + { + if (OVR_strcmp(profile->Name, DefaultProfile) == 0) + DefaultProfile.Clear(); + + ProfileCache.RemoveAt(i); + Changed = true; + return true; + } + } + + return false; +} + + + +//----------------------------------------------------------------------------- +// ***** Profile + +Profile::Profile(ProfileType device, const char* name) +{ + Type = device; + Gender = Gender_Unspecified; + PlayerHeight = 1.778f; // 5'10" inch man + IPD = 0.064f; + OVR_strcpy(Name, MaxNameLen, name); +} + + +bool Profile::ParseProperty(const char* prop, const char* sval) +{ + if (OVR_strcmp(prop, "Name") == 0) + { + OVR_strcpy(Name, MaxNameLen, sval); + return true; + } + else if (OVR_strcmp(prop, "Gender") == 0) + { + if (OVR_strcmp(sval, "Male") == 0) + Gender = Gender_Male; + else if (OVR_strcmp(sval, "Female") == 0) + Gender = Gender_Female; + else + Gender = Gender_Unspecified; + + return true; + } + else if (OVR_strcmp(prop, "PlayerHeight") == 0) + { + PlayerHeight = (float)atof(sval); + return true; + } + else if (OVR_strcmp(prop, "IPD") == 0) + { + IPD = (float)atof(sval); + return true; + } + + return false; +} + + +// Computes the eye height from the metric head height +float Profile::GetEyeHeight() +{ + const float EYE_TO_HEADTOP_RATIO = 0.44538f; + const float MALE_AVG_HEAD_HEIGHT = 0.232f; + const float FEMALE_AVG_HEAD_HEIGHT = 0.218f; + + // compute distance from top of skull to the eye + float head_height; + if (Gender == Gender_Female) + head_height = FEMALE_AVG_HEAD_HEIGHT; + else + head_height = MALE_AVG_HEAD_HEIGHT; + + float skull = EYE_TO_HEADTOP_RATIO * head_height; + + float eye_height = PlayerHeight - skull; + return eye_height; +} + + +//----------------------------------------------------------------------------- +// ***** RiftDK1Profile + +RiftDK1Profile::RiftDK1Profile(const char* name) : Profile(Profile_RiftDK1, name) +{ + EyeCups = EyeCup_A; + LL = 0; + LR = 0; + RL = 0; + RR = 0; +} + +bool RiftDK1Profile::ParseProperty(const char* prop, const char* sval) +{ + if (OVR_strcmp(prop, "EyeCup") == 0) + { + switch (sval[0]) + { + case 'C': EyeCups = EyeCup_C; break; + case 'B': EyeCups = EyeCup_B; break; + default: EyeCups = EyeCup_A; break; + } + return true; + } + else if (OVR_strcmp(prop, "LL") == 0) + { + LL = atoi(sval); + return true; + } + else if (OVR_strcmp(prop, "LR") == 0) + { + LR = atoi(sval); + return true; + } + else if (OVR_strcmp(prop, "RL") == 0) + { + RL = atoi(sval); + return true; + } + else if (OVR_strcmp(prop, "RR") == 0) + { + RR = atoi(sval); + return true; + } + + return Profile::ParseProperty(prop, sval); +} + +Profile* RiftDK1Profile::Clone() const +{ + RiftDK1Profile* profile = new RiftDK1Profile(*this); + return profile; +} + +} // OVR diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_Profile.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_Profile.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,189 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Profile.h +Content : Structs and functions for loading and storing device profile settings +Created : February 14, 2013 +Notes : + Profiles are used to store per-user settings that can be transferred and used + across multiple applications. For example, player IPD can be configured once + and reused for a unified experience across games. Configuration and saving of profiles + can be accomplished in game via the Profile API or by the official Oculus Configuration + Utility. + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Profile_h +#define OVR_Profile_h + +#include "Kernel/OVR_String.h" +#include "Kernel/OVR_RefCount.h" +#include "Kernel/OVR_Array.h" + +namespace OVR { + +// Defines the profile object for each device type +enum ProfileType +{ + Profile_Unknown = 0, + Profile_RiftDK1 = 1, + Profile_RiftDKHD = 2, +}; + +class Profile; + +// ----------------------------------------------------------------------------- +// ***** ProfileManager + +// Profiles are interfaced through a ProfileManager object. Applications should +// create a ProfileManager each time they intend to read or write user profile data. +// The scope of the ProfileManager object defines when disk I/O is performed. Disk +// reads are performed on the first profile access and disk writes are performed when +// the ProfileManager goes out of scope. All profile interactions between these times +// are performed in local memory and are fast. A typical profile interaction might +// look like this: +// +// { +// Ptr pm = *ProfileManager::Create(); +// Ptr profile = pm->LoadProfile(Profile_RiftDK1, +// pm->GetDefaultProfileName(Profile_RiftDK1)); +// if (profile) +// { // Retrieve the current profile settings +// } +// } // Profile will be destroyed and any disk I/O completed when going out of scope + +class ProfileManager : public RefCountBase +{ +protected: + // Synchronize ProfileManager access since it may be accessed from multiple threads, + // as it's shared through DeviceManager. + Lock ProfileLock; + Array > ProfileCache; + ProfileType CacheDevice; + String DefaultProfile; + bool Changed; + char NameBuff[32]; + +public: + static ProfileManager* Create(); + + // Static interface functions + int GetProfileCount(ProfileType device); + const char* GetProfileName(ProfileType device, unsigned int index); + bool HasProfile(ProfileType device, const char* name); + Profile* LoadProfile(ProfileType device, unsigned int index); + Profile* LoadProfile(ProfileType device, const char* name); + Profile* GetDeviceDefaultProfile(ProfileType device); + const char* GetDefaultProfileName(ProfileType device); + bool SetDefaultProfileName(ProfileType device, const char* name); + bool Save(const Profile* profile); + bool Delete(const Profile* profile); + +protected: + ProfileManager(); + ~ProfileManager(); + void LoadCache(ProfileType device); + void SaveCache(); + void ClearCache(); + Profile* CreateProfileObject(const char* user, + ProfileType device, + const char** device_name); +}; + +//------------------------------------------------------------------- +// ***** Profile + +// The base profile for all HMD devices. This object is never created directly. +// Instead derived objects provide specific data implementations. Some settings +// such as IPD will be tied to a specific user and be consistent between , +// implementations but other settings like optical distortion may vary between devices. + +class Profile : public RefCountBase +{ +public: + enum { MaxNameLen = 32 }; + + enum GenderType + { + Gender_Unspecified = 0, + Gender_Male = 1, + Gender_Female = 2 + }; + + ProfileType Type; // The type of device profile + char Name[MaxNameLen]; // The name given to this profile + +protected: + GenderType Gender; // The gender of the user + float PlayerHeight; // The height of the user in meters + float IPD; // Distance between eyes in meters + +public: + // These are properties which are intrinsic to the user and affect scene setup + GenderType GetGender() { return Gender; }; + float GetPlayerHeight() { return PlayerHeight; }; + float GetIPD() { return IPD; }; + float GetEyeHeight(); + + void SetGender(GenderType gender) { Gender = gender; }; + void SetPlayerHeight(float height) { PlayerHeight = height; }; + void SetIPD(float ipd) { IPD = ipd; }; + + +protected: + Profile(ProfileType type, const char* name); + + virtual Profile* Clone() const = 0; + virtual bool ParseProperty(const char* prop, const char* sval); + + friend class ProfileManager; +}; + + +//----------------------------------------------------------------------------- +// ***** RiftDK1Profile + +// This profile is specific to the Rift Dev Kit 1 and contains overrides specific +// to that device and lens cup settings. +class RiftDK1Profile : public Profile +{ +public: + enum EyeCupType + { + EyeCup_A = 0, + EyeCup_B = 1, + EyeCup_C = 2 + }; + +protected: + EyeCupType EyeCups; // Which eye cup does the player use + int LL; // Configuration Utility IPD setting + int LR; // Configuration Utility IPD setting + int RL; // Configuration Utility IPD setting + int RR; // Configuration Utility IPD setting + +public: + EyeCupType GetEyeCup() { return EyeCups; }; + void SetEyeCup(EyeCupType cup) { EyeCups = cup; }; + +protected: + RiftDK1Profile(const char* name); + + virtual Profile* Clone() const; + virtual bool ParseProperty(const char* prop, const char* sval); + + friend class ProfileManager; +}; + + +String GetBaseOVRPath(bool create_dir); + +} + +#endif // OVR_Profile_h \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_SensorFilter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_SensorFilter.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,201 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_SensorFilter.cpp +Content : Basic filtering of sensor data +Created : March 7, 2013 +Authors : Steve LaValle, Anna Yershova + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_SensorFilter.h" + +namespace OVR { + +Vector3f SensorFilter::Total() const +{ + Vector3f total = Vector3f(0.0f, 0.0f, 0.0f); + for (int i = 0; i < Size; i++) + total += Elements[i]; + return total; +} + +Vector3f SensorFilter::Mean() const +{ + Vector3f total = Vector3f(0.0f, 0.0f, 0.0f); + for (int i = 0; i < Size; i++) + total += Elements[i]; + return total / (float) Size; +} + +Vector3f SensorFilter::Median() const +{ + int half_window = (int) Size / 2; + float sortx[MaxFilterSize]; + float resultx = 0.0f; + + float sorty[MaxFilterSize]; + float resulty = 0.0f; + + float sortz[MaxFilterSize]; + float resultz = 0.0f; + + for (int i = 0; i < Size; i++) + { + sortx[i] = Elements[i].x; + sorty[i] = Elements[i].y; + sortz[i] = Elements[i].z; + } + for (int j = 0; j <= half_window; j++) + { + int minx = j; + int miny = j; + int minz = j; + for (int k = j + 1; k < Size; k++) + { + if (sortx[k] < sortx[minx]) minx = k; + if (sorty[k] < sorty[miny]) miny = k; + if (sortz[k] < sortz[minz]) minz = k; + } + const float tempx = sortx[j]; + const float tempy = sorty[j]; + const float tempz = sortz[j]; + sortx[j] = sortx[minx]; + sortx[minx] = tempx; + + sorty[j] = sorty[miny]; + sorty[miny] = tempy; + + sortz[j] = sortz[minz]; + sortz[minz] = tempz; + } + resultx = sortx[half_window]; + resulty = sorty[half_window]; + resultz = sortz[half_window]; + + return Vector3f(resultx, resulty, resultz); +} + +// Only the diagonal of the covariance matrix. +Vector3f SensorFilter::Variance() const +{ + Vector3f mean = Mean(); + Vector3f total = Vector3f(0.0f, 0.0f, 0.0f); + for (int i = 0; i < Size; i++) + { + total.x += (Elements[i].x - mean.x) * (Elements[i].x - mean.x); + total.y += (Elements[i].y - mean.y) * (Elements[i].y - mean.y); + total.z += (Elements[i].z - mean.z) * (Elements[i].z - mean.z); + } + return total / (float) Size; +} + +// Should be a 3x3 matrix returned, but OVR_math.h doesn't have one +Matrix4f SensorFilter::Covariance() const +{ + Vector3f mean = Mean(); + Matrix4f total = Matrix4f(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0); + for (int i = 0; i < Size; i++) + { + total.M[0][0] += (Elements[i].x - mean.x) * (Elements[i].x - mean.x); + total.M[1][0] += (Elements[i].y - mean.y) * (Elements[i].x - mean.x); + total.M[2][0] += (Elements[i].z - mean.z) * (Elements[i].x - mean.x); + total.M[1][1] += (Elements[i].y - mean.y) * (Elements[i].y - mean.y); + total.M[2][1] += (Elements[i].z - mean.z) * (Elements[i].y - mean.y); + total.M[2][2] += (Elements[i].z - mean.z) * (Elements[i].z - mean.z); + } + total.M[0][1] = total.M[1][0]; + total.M[0][2] = total.M[2][0]; + total.M[1][2] = total.M[2][1]; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + total.M[i][j] *= 1.0f / Size; + return total; +} + +Vector3f SensorFilter::PearsonCoefficient() const +{ + Matrix4f cov = Covariance(); + Vector3f pearson = Vector3f(); + pearson.x = cov.M[0][1]/(sqrt(cov.M[0][0])*sqrt(cov.M[1][1])); + pearson.y = cov.M[1][2]/(sqrt(cov.M[1][1])*sqrt(cov.M[2][2])); + pearson.z = cov.M[2][0]/(sqrt(cov.M[2][2])*sqrt(cov.M[0][0])); + + return pearson; +} + + +Vector3f SensorFilter::SavitzkyGolaySmooth8() const +{ + OVR_ASSERT(Size >= 8); + return GetPrev(0)*0.41667f + + GetPrev(1)*0.33333f + + GetPrev(2)*0.25f + + GetPrev(3)*0.16667f + + GetPrev(4)*0.08333f - + GetPrev(6)*0.08333f - + GetPrev(7)*0.16667f; +} + + +Vector3f SensorFilter::SavitzkyGolayDerivative4() const +{ + OVR_ASSERT(Size >= 4); + return GetPrev(0)*0.3f + + GetPrev(1)*0.1f - + GetPrev(2)*0.1f - + GetPrev(3)*0.3f; +} + +Vector3f SensorFilter::SavitzkyGolayDerivative5() const +{ + OVR_ASSERT(Size >= 5); + return GetPrev(0)*0.2f + + GetPrev(1)*0.1f - + GetPrev(3)*0.1f - + GetPrev(4)*0.2f; +} + +Vector3f SensorFilter::SavitzkyGolayDerivative12() const +{ + OVR_ASSERT(Size >= 12); + return GetPrev(0)*0.03846f + + GetPrev(1)*0.03147f + + GetPrev(2)*0.02448f + + GetPrev(3)*0.01748f + + GetPrev(4)*0.01049f + + GetPrev(5)*0.0035f - + GetPrev(6)*0.0035f - + GetPrev(7)*0.01049f - + GetPrev(8)*0.01748f - + GetPrev(9)*0.02448f - + GetPrev(10)*0.03147f - + GetPrev(11)*0.03846f; +} + +Vector3f SensorFilter::SavitzkyGolayDerivativeN(int n) const +{ + OVR_ASSERT(Size >= n); + int m = (n-1)/2; + Vector3f result = Vector3f(); + for (int k = 1; k <= m; k++) + { + int ind1 = m - k; + int ind2 = n - m + k - 1; + result += (GetPrev(ind1) - GetPrev(ind2)) * (float) k; + } + float coef = 3.0f/(m*(m+1.0f)*(2.0f*m+1.0f)); + result = result*coef; + return result; +} + + + + +} //namespace OVR \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_SensorFilter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_SensorFilter.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,99 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_SensorFilter.h +Content : Basic filtering of sensor data +Created : March 7, 2013 +Authors : Steve LaValle, Anna Yershova + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_SensorFilter_h +#define OVR_SensorFilter_h + +#include "Kernel/OVR_Math.h" + + +namespace OVR { + +// This class maintains a sliding window of sensor data taken over time and implements +// various simple filters, most of which are linear functions of the data history. +class SensorFilter +{ + enum + { + MaxFilterSize = 100, + DefaultFilterSize = 20 + }; + +private: + int LastIdx; // The index of the last element that was added to the array + int Size; // The window size (number of elements) + Vector3f Elements[MaxFilterSize]; + +public: + // Create a new filter with default size + SensorFilter() + { + LastIdx = -1; + Size = DefaultFilterSize; + }; + + // Create a new filter with size i + SensorFilter(int i) + { + OVR_ASSERT(i <= MaxFilterSize); + LastIdx = -1; + Size = i; + }; + + + // Create a new element to the filter + void AddElement (const Vector3f &e) + { + if (LastIdx == Size - 1) + LastIdx = 0; + else + LastIdx++; + + Elements[LastIdx] = e; + }; + + // Get element i. 0 is the most recent, 1 is one step ago, 2 is two steps ago, ... + Vector3f GetPrev(int i) const + { + OVR_ASSERT(i >= 0); // + int idx = (LastIdx - i); + if (idx < 0) // Fix the wraparound case + idx += Size; + OVR_ASSERT(idx >= 0); // Multiple wraparounds not allowed + return Elements[idx]; + }; + + // Simple statistics + Vector3f Total() const; + Vector3f Mean() const; + Vector3f Median() const; + Vector3f Variance() const; // The diagonal of covariance matrix + Matrix4f Covariance() const; + Vector3f PearsonCoefficient() const; + + // A popular family of smoothing filters and smoothed derivatives + Vector3f SavitzkyGolaySmooth8() const; + Vector3f SavitzkyGolayDerivative4() const; + Vector3f SavitzkyGolayDerivative5() const; + Vector3f SavitzkyGolayDerivative12() const; + Vector3f SavitzkyGolayDerivativeN(int n) const; + + ~SensorFilter() {}; +}; + +} //namespace OVR + +#endif // OVR_SensorFilter_h diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_SensorFusion.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_SensorFusion.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,696 @@ +/************************************************************************************ + +Filename : OVR_SensorFusion.cpp +Content : Methods that determine head orientation from sensor data over time +Created : October 9, 2012 +Authors : Michael Antonov, Steve LaValle + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_SensorFusion.h" +#include "Kernel/OVR_Log.h" +#include "Kernel/OVR_System.h" +#include "OVR_JSON.h" +#include "OVR_Profile.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +// ***** Sensor Fusion + +SensorFusion::SensorFusion(SensorDevice* sensor) + : Handler(getThis()), pDelegate(0), + Gain(0.05f), YawMult(1), EnableGravity(true), Stage(0), RunningTime(0), DeltaT(0.001f), + EnablePrediction(true), PredictionDT(0.03f), PredictionTimeIncrement(0.001f), + FRawMag(10), FAccW(20), FAngV(20), + TiltCondCount(0), TiltErrorAngle(0), + TiltErrorAxis(0,1,0), + MagCondCount(0), MagCalibrated(false), MagRefQ(0, 0, 0, 1), + MagRefM(0), MagRefYaw(0), YawErrorAngle(0), MagRefDistance(0.5f), + YawErrorCount(0), YawCorrectionActivated(false), YawCorrectionInProgress(false), + EnableYawCorrection(false), MagNumReferences(0), MagHasNearbyReference(false), + MotionTrackingEnabled(true) +{ + if (sensor) + AttachToSensor(sensor); + MagCalibrationMatrix.SetIdentity(); +} + +SensorFusion::~SensorFusion() +{ +} + + +bool SensorFusion::AttachToSensor(SensorDevice* sensor) +{ + // clear the cached device information + CachedSensorInfo.SerialNumber[0] = 0; + CachedSensorInfo.VendorId = 0; + CachedSensorInfo.ProductId = 0; + + if (sensor != NULL) + { + // Cache the sensor device so we can access this information during + // mag saving and loading (avoid holding a reference to sensor to prevent + // deadlock on shutdown) + sensor->GetDeviceInfo(&CachedSensorInfo); // save the device information + MessageHandler* pCurrentHandler = sensor->GetMessageHandler(); + + if (pCurrentHandler == &Handler) + { + Reset(); + return true; + } + + if (pCurrentHandler != NULL) + { + OVR_DEBUG_LOG( + ("SensorFusion::AttachToSensor failed - sensor %p already has handler", sensor)); + return false; + } + + // Automatically load the default mag calibration for this sensor + LoadMagCalibration(); + } + + if (Handler.IsHandlerInstalled()) + { + Handler.RemoveHandlerFromDevices(); + } + + if (sensor != NULL) + { + sensor->SetMessageHandler(&Handler); + } + + Reset(); + return true; +} + + + // Resets the current orientation +void SensorFusion::Reset() +{ + Lock::Locker lockScope(Handler.GetHandlerLock()); + Q = Quatf(); + QUncorrected = Quatf(); + Stage = 0; + RunningTime = 0; + MagNumReferences = 0; + MagHasNearbyReference = false; +} + + +void SensorFusion::handleMessage(const MessageBodyFrame& msg) +{ + if (msg.Type != Message_BodyFrame || !IsMotionTrackingEnabled()) + return; + + // Put the sensor readings into convenient local variables + Vector3f angVel = msg.RotationRate; + Vector3f rawAccel = msg.Acceleration; + Vector3f mag = msg.MagneticField; + + // Set variables accessible through the class API + DeltaT = msg.TimeDelta; + AngV = msg.RotationRate; + AngV.y *= YawMult; // Warning: If YawMult != 1, then AngV is not true angular velocity + A = rawAccel; + + // Allow external access to uncalibrated magnetometer values + RawMag = mag; + + // Apply the calibration parameters to raw mag + if (HasMagCalibration()) + { + mag.x += MagCalibrationMatrix.M[0][3]; + mag.y += MagCalibrationMatrix.M[1][3]; + mag.z += MagCalibrationMatrix.M[2][3]; + } + + // Provide external access to calibrated mag values + // (if the mag is not calibrated, then the raw value is returned) + CalMag = mag; + + float angVelLength = angVel.Length(); + float accLength = rawAccel.Length(); + + + // Acceleration in the world frame (Q is current HMD orientation) + Vector3f accWorld = Q.Rotate(rawAccel); + + // Keep track of time + Stage++; + RunningTime += DeltaT; + + // Insert current sensor data into filter history + FRawMag.AddElement(RawMag); + FAccW.AddElement(accWorld); + FAngV.AddElement(angVel); + + // Update orientation Q based on gyro outputs. This technique is + // based on direct properties of the angular velocity vector: + // Its direction is the current rotation axis, and its magnitude + // is the rotation rate (rad/sec) about that axis. Our sensor + // sampling rate is so fast that we need not worry about integral + // approximation error (not yet, anyway). + if (angVelLength > 0.0f) + { + Vector3f rotAxis = angVel / angVelLength; + float halfRotAngle = angVelLength * DeltaT * 0.5f; + float sinHRA = sin(halfRotAngle); + Quatf deltaQ(rotAxis.x*sinHRA, rotAxis.y*sinHRA, rotAxis.z*sinHRA, cos(halfRotAngle)); + + Q = Q * deltaQ; + } + + // The quaternion magnitude may slowly drift due to numerical error, + // so it is periodically normalized. + if (Stage % 5000 == 0) + Q.Normalize(); + + // Maintain the uncorrected orientation for later use by predictive filtering + QUncorrected = Q; + + // Perform tilt correction using the accelerometer data. This enables + // drift errors in pitch and roll to be corrected. Note that yaw cannot be corrected + // because the rotation axis is parallel to the gravity vector. + if (EnableGravity) + { + // Correcting for tilt error by using accelerometer data + const float gravityEpsilon = 0.4f; + const float angVelEpsilon = 0.1f; // Relatively slow rotation + const int tiltPeriod = 50; // Required time steps of stability + const float maxTiltError = 0.05f; + const float minTiltError = 0.01f; + + // This condition estimates whether the only measured acceleration is due to gravity + // (the Rift is not linearly accelerating). It is often wrong, but tends to average + // out well over time. + if ((fabs(accLength - 9.81f) < gravityEpsilon) && + (angVelLength < angVelEpsilon)) + TiltCondCount++; + else + TiltCondCount = 0; + + // After stable measurements have been taken over a sufficiently long period, + // estimate the amount of tilt error and calculate the tilt axis for later correction. + if (TiltCondCount >= tiltPeriod) + { // Update TiltErrorEstimate + TiltCondCount = 0; + // Use an average value to reduce noise (could alternatively use an LPF) + Vector3f accWMean = FAccW.Mean(); + // Project the acceleration vector into the XZ plane + Vector3f xzAcc = Vector3f(accWMean.x, 0.0f, accWMean.z); + // The unit normal of xzAcc will be the rotation axis for tilt correction + Vector3f tiltAxis = Vector3f(xzAcc.z, 0.0f, -xzAcc.x).Normalized(); + Vector3f yUp = Vector3f(0.0f, 1.0f, 0.0f); + // This is the amount of rotation + float tiltAngle = yUp.Angle(accWMean); + // Record values if the tilt error is intolerable + if (tiltAngle > maxTiltError) + { + TiltErrorAngle = tiltAngle; + TiltErrorAxis = tiltAxis; + } + } + + // This part performs the actual tilt correction as needed + if (TiltErrorAngle > minTiltError) + { + if ((TiltErrorAngle > 0.4f)&&(RunningTime < 8.0f)) + { // Tilt completely to correct orientation + Q = Quatf(TiltErrorAxis, -TiltErrorAngle) * Q; + TiltErrorAngle = 0.0f; + } + else + { + //LogText("Performing tilt correction - Angle: %f Axis: %f %f %f\n", + // TiltErrorAngle,TiltErrorAxis.x,TiltErrorAxis.y,TiltErrorAxis.z); + //float deltaTiltAngle = -Gain*TiltErrorAngle*0.005f; + // This uses aggressive correction steps while your head is moving fast + float deltaTiltAngle = -Gain*TiltErrorAngle*0.005f*(5.0f*angVelLength+1.0f); + // Incrementally "un-tilt" by a small step size + Q = Quatf(TiltErrorAxis, deltaTiltAngle) * Q; + TiltErrorAngle += deltaTiltAngle; + } + } + } + + // Yaw drift correction based on magnetometer data. This corrects the part of the drift + // that the accelerometer cannot handle. + // This will only work if the magnetometer has been enabled, calibrated, and a reference + // point has been set. + const float maxAngVelLength = 3.0f; + const int magWindow = 5; + const float yawErrorMax = 0.1f; + const float yawErrorMin = 0.01f; + const int yawErrorCountLimit = 50; + const float yawRotationStep = 0.00002f; + + if (angVelLength < maxAngVelLength) + MagCondCount++; + else + MagCondCount = 0; + + // Find, create, and utilize reference points for the magnetometer + // Need to be careful not to set reference points while there is significant tilt error + if ((EnableYawCorrection && MagCalibrated)&&(RunningTime > 10.0f)&&(TiltErrorAngle < 0.2f)) + { + if (MagNumReferences == 0) + { + setMagReference(); // Use the current direction + } + else if (Q.Distance(MagRefQ) > MagRefDistance) + { + MagHasNearbyReference = false; + float bestDist = 100000.0f; + int bestNdx = 0; + float dist; + for (int i = 0; i < MagNumReferences; i++) + { + dist = Q.Distance(MagRefTableQ[i]); + if (dist < bestDist) + { + bestNdx = i; + bestDist = dist; + } + } + + if (bestDist < MagRefDistance) + { + MagHasNearbyReference = true; + MagRefQ = MagRefTableQ[bestNdx]; + MagRefM = MagRefTableM[bestNdx]; + MagRefYaw = MagRefTableYaw[bestNdx]; + //LogText("Using reference %d\n",bestNdx); + } + else if (MagNumReferences < MagMaxReferences) + setMagReference(); + } + } + + YawCorrectionInProgress = false; + if (EnableYawCorrection && MagCalibrated && (RunningTime > 2.0f) && (MagCondCount >= magWindow) && + MagHasNearbyReference) + { + // Use rotational invariance to bring reference mag value into global frame + Vector3f grefmag = MagRefQ.Rotate(GetCalibratedMagValue(MagRefM)); + // Bring current (averaged) mag reading into global frame + Vector3f gmag = Q.Rotate(GetCalibratedMagValue(FRawMag.Mean())); + // Calculate the reference yaw in the global frame + Anglef gryaw = Anglef(atan2(grefmag.x,grefmag.z)); + // Calculate the current yaw in the global frame + Anglef gyaw = Anglef(atan2(gmag.x,gmag.z)); + // The difference between reference and current yaws is the perceived error + Anglef YawErrorAngle = gyaw - gryaw; + + //LogText("Yaw error estimate: %f\n",YawErrorAngle.Get()); + // If the perceived error is large, keep count + if ((YawErrorAngle.Abs() > yawErrorMax) && (!YawCorrectionActivated)) + YawErrorCount++; + // After enough iterations of high perceived error, start the correction process + if (YawErrorCount > yawErrorCountLimit) + YawCorrectionActivated = true; + // If the perceived error becomes small, turn off the yaw correction + if ((YawErrorAngle.Abs() < yawErrorMin) && YawCorrectionActivated) + { + YawCorrectionActivated = false; + YawErrorCount = 0; + } + + // Perform the actual yaw correction, due to previously detected, large yaw error + if (YawCorrectionActivated) + { + YawCorrectionInProgress = true; + // Incrementally "unyaw" by a small step size + Q = Quatf(Vector3f(0.0f,1.0f,0.0f), -yawRotationStep * YawErrorAngle.Sign()) * Q; + } + } +} + + +// A predictive filter based on extrapolating the smoothed, current angular velocity +Quatf SensorFusion::GetPredictedOrientation(float pdt) +{ + Lock::Locker lockScope(Handler.GetHandlerLock()); + Quatf qP = QUncorrected; + + if (EnablePrediction) + { + // This method assumes a constant angular velocity + Vector3f angVelF = FAngV.SavitzkyGolaySmooth8(); + float angVelFL = angVelF.Length(); + + // Force back to raw measurement + angVelF = AngV; + angVelFL = AngV.Length(); + + // Dynamic prediction interval: Based on angular velocity to reduce vibration + const float minPdt = 0.001f; + const float slopePdt = 0.1f; + float newpdt = pdt; + float tpdt = minPdt + slopePdt * angVelFL; + if (tpdt < pdt) + newpdt = tpdt; + //LogText("PredictonDTs: %d\n",(int)(newpdt / PredictionTimeIncrement + 0.5f)); + + if (angVelFL > 0.001f) + { + Vector3f rotAxisP = angVelF / angVelFL; + float halfRotAngleP = angVelFL * newpdt * 0.5f; + float sinaHRAP = sin(halfRotAngleP); + Quatf deltaQP(rotAxisP.x*sinaHRAP, rotAxisP.y*sinaHRAP, + rotAxisP.z*sinaHRAP, cos(halfRotAngleP)); + qP = QUncorrected * deltaQP; + } + } + return qP; +} + + +Vector3f SensorFusion::GetCalibratedMagValue(const Vector3f& rawMag) const +{ + Vector3f mag = rawMag; + OVR_ASSERT(HasMagCalibration()); + mag.x += MagCalibrationMatrix.M[0][3]; + mag.y += MagCalibrationMatrix.M[1][3]; + mag.z += MagCalibrationMatrix.M[2][3]; + return mag; +} + + +void SensorFusion::setMagReference(const Quatf& q, const Vector3f& rawMag) +{ + if (MagNumReferences < MagMaxReferences) + { + MagRefTableQ[MagNumReferences] = q; + MagRefTableM[MagNumReferences] = rawMag; //FRawMag.Mean(); + + //LogText("Inserting reference %d\n",MagNumReferences); + + MagRefQ = q; + MagRefM = rawMag; + + float pitch, roll, yaw; + Quatf q2 = q; + q2.GetEulerAngles(&pitch, &roll, &yaw); + MagRefTableYaw[MagNumReferences] = yaw; + MagRefYaw = yaw; + + MagNumReferences++; + + MagHasNearbyReference = true; + } +} + + +SensorFusion::BodyFrameHandler::~BodyFrameHandler() +{ + RemoveHandlerFromDevices(); +} + +void SensorFusion::BodyFrameHandler::OnMessage(const Message& msg) +{ + if (msg.Type == Message_BodyFrame) + pFusion->handleMessage(static_cast(msg)); + if (pFusion->pDelegate) + pFusion->pDelegate->OnMessage(msg); +} + +bool SensorFusion::BodyFrameHandler::SupportsMessageType(MessageType type) const +{ + return (type == Message_BodyFrame); +} + +// Writes the current calibration for a particular device to a device profile file +// sensor - the sensor that was calibrated +// cal_name - an optional name for the calibration or default if cal_name == NULL +bool SensorFusion::SaveMagCalibration(const char* calibrationName) const +{ + if (CachedSensorInfo.SerialNumber[0] == NULL || !HasMagCalibration()) + return false; + + // A named calibration may be specified for calibration in different + // environments, otherwise the default calibration is used + if (calibrationName == NULL) + calibrationName = "default"; + + // Generate a mag calibration event + JSON* calibration = JSON::CreateObject(); + // (hardcoded for now) the measurement and representation method + calibration->AddStringItem("Version", "1.0"); + calibration->AddStringItem("Name", "default"); + + // time stamp the calibration + char time_str[64]; + +#ifdef OVR_OS_WIN32 + struct tm caltime; + localtime_s(&caltime, &MagCalibrationTime); + strftime(time_str, 64, "%Y-%m-%d %H:%M:%S", &caltime); +#else + struct tm* caltime; + caltime = localtime(&MagCalibrationTime); + strftime(time_str, 64, "%Y-%m-%d %H:%M:%S", caltime); +#endif + + calibration->AddStringItem("Time", time_str); + + // write the full calibration matrix + Matrix4f calmat = GetMagCalibration(); + char matrix[128]; + int pos = 0; + for (int r=0; r<4; r++) + { + for (int c=0; c<4; c++) + { + pos += (int)OVR_sprintf(matrix+pos, 128, "%g ", calmat.M[r][c]); + } + } + calibration->AddStringItem("Calibration", matrix); + + + String path = GetBaseOVRPath(true); + path += "/Devices.json"; + + // Look for a prexisting device file to edit + Ptr root = *JSON::Load(path); + if (root) + { // Quick sanity check of the file type and format before we parse it + JSON* version = root->GetFirstItem(); + if (version && version->Name == "Oculus Device Profile Version") + { // In the future I may need to check versioning to determine parse method + } + else + { + root->Release(); + root = NULL; + } + } + + JSON* device = NULL; + if (root) + { + device = root->GetFirstItem(); // skip the header + device = root->GetNextItem(device); + while (device) + { // Search for a previous calibration with the same name for this device + // and remove it before adding the new one + if (device->Name == "Device") + { + JSON* item = device->GetItemByName("Serial"); + if (item && item->Value == CachedSensorInfo.SerialNumber) + { // found an entry for this device + item = device->GetNextItem(item); + while (item) + { + if (item->Name == "MagCalibration") + { + JSON* name = item->GetItemByName("Name"); + if (name && name->Value == calibrationName) + { // found a calibration of the same name + item->RemoveNode(); + item->Release(); + break; + } + } + item = device->GetNextItem(item); + } + + // update the auto-mag flag + item = device->GetItemByName("EnableYawCorrection"); + if (item) + item->dValue = (double)EnableYawCorrection; + else + device->AddBoolItem("EnableYawCorrection", EnableYawCorrection); + + break; + } + } + + device = root->GetNextItem(device); + } + } + else + { // Create a new device root + root = *JSON::CreateObject(); + root->AddStringItem("Oculus Device Profile Version", "1.0"); + } + + if (device == NULL) + { + device = JSON::CreateObject(); + device->AddStringItem("Product", CachedSensorInfo.ProductName); + device->AddNumberItem("ProductID", CachedSensorInfo.ProductId); + device->AddStringItem("Serial", CachedSensorInfo.SerialNumber); + device->AddBoolItem("EnableYawCorrection", EnableYawCorrection); + + root->AddItem("Device", device); + } + + // Create and the add the new calibration event to the device + device->AddItem("MagCalibration", calibration); + + return root->Save(path); +} + +// Loads a saved calibration for the specified device from the device profile file +// sensor - the sensor that the calibration was saved for +// cal_name - an optional name for the calibration or the default if cal_name == NULL +bool SensorFusion::LoadMagCalibration(const char* calibrationName) +{ + if (CachedSensorInfo.SerialNumber[0] == NULL) + return false; + + // A named calibration may be specified for calibration in different + // environments, otherwise the default calibration is used + if (calibrationName == NULL) + calibrationName = "default"; + + String path = GetBaseOVRPath(true); + path += "/Devices.json"; + + // Load the device profiles + Ptr root = *JSON::Load(path); + if (root == NULL) + return false; + + // Quick sanity check of the file type and format before we parse it + JSON* version = root->GetFirstItem(); + if (version && version->Name == "Oculus Device Profile Version") + { // In the future I may need to check versioning to determine parse method + } + else + { + return false; + } + + bool autoEnableCorrection = false; + + JSON* device = root->GetNextItem(version); + while (device) + { // Search for a previous calibration with the same name for this device + // and remove it before adding the new one + if (device->Name == "Device") + { + JSON* item = device->GetItemByName("Serial"); + if (item && item->Value == CachedSensorInfo.SerialNumber) + { // found an entry for this device + + JSON* autoyaw = device->GetItemByName("EnableYawCorrection"); + if (autoyaw) + autoEnableCorrection = (autoyaw->dValue != 0); + + item = device->GetNextItem(item); + while (item) + { + if (item->Name == "MagCalibration") + { + JSON* calibration = item; + JSON* name = calibration->GetItemByName("Name"); + if (name && name->Value == calibrationName) + { // found a calibration with this name + + time_t now; + time(&now); + + // parse the calibration time + time_t calibration_time = now; + JSON* caltime = calibration->GetItemByName("Time"); + if (caltime) + { + const char* caltime_str = caltime->Value.ToCStr(); + + tm ct; + memset(&ct, 0, sizeof(tm)); + +#ifdef OVR_OS_WIN32 + struct tm nowtime; + localtime_s(&nowtime, &now); + ct.tm_isdst = nowtime.tm_isdst; + sscanf_s(caltime_str, "%d-%d-%d %d:%d:%d", + &ct.tm_year, &ct.tm_mon, &ct.tm_mday, + &ct.tm_hour, &ct.tm_min, &ct.tm_sec); +#else + struct tm* nowtime = localtime(&now); + ct.tm_isdst = nowtime->tm_isdst; + sscanf(caltime_str, "%d-%d-%d %d:%d:%d", + &ct.tm_year, &ct.tm_mon, &ct.tm_mday, + &ct.tm_hour, &ct.tm_min, &ct.tm_sec); +#endif + ct.tm_year -= 1900; + ct.tm_mon--; + calibration_time = mktime(&ct); + } + + // parse the calibration matrix + JSON* cal = calibration->GetItemByName("Calibration"); + if (cal) + { + const char* data_str = cal->Value.ToCStr(); + Matrix4f calmat; + for (int r=0; r<4; r++) + { + for (int c=0; c<4; c++) + { + calmat.M[r][c] = (float)atof(data_str); + while (data_str && *data_str != ' ') + data_str++; + + if (data_str) + data_str++; + } + } + + SetMagCalibration(calmat); + MagCalibrationTime = calibration_time; + EnableYawCorrection = autoEnableCorrection; + + return true; + } + } + } + item = device->GetNextItem(item); + } + + break; + } + } + + device = root->GetNextItem(device); + } + + return false; +} + + + +} // namespace OVR + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_SensorFusion.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_SensorFusion.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,277 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_SensorFusion.h +Content : Methods that determine head orientation from sensor data over time +Created : October 9, 2012 +Authors : Michael Antonov, Steve LaValle + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_SensorFusion_h +#define OVR_SensorFusion_h + +#include "OVR_Device.h" +#include "OVR_SensorFilter.h" +#include + +namespace OVR { + +//------------------------------------------------------------------------------------- +// ***** SensorFusion + +// SensorFusion class accumulates Sensor notification messages to keep track of +// orientation, which involves integrating the gyro and doing correction with gravity. +// Magnetometer based yaw drift correction is also supported; it is usually enabled +// automatically based on loaded magnetometer configuration. +// Orientation is reported as a quaternion, from which users can obtain either the +// rotation matrix or Euler angles. +// +// The class can operate in two ways: +// - By user manually passing MessageBodyFrame messages to the OnMessage() function. +// - By attaching SensorFusion to a SensorDevice, in which case it will +// automatically handle notifications from that device. + + +class SensorFusion : public NewOverrideBase +{ + enum + { + MagMaxReferences = 80 + }; + +public: + SensorFusion(SensorDevice* sensor = 0); + ~SensorFusion(); + + + // *** Setup + + // Attaches this SensorFusion to a sensor device, from which it will receive + // notification messages. If a sensor is attached, manual message notification + // is not necessary. Calling this function also resets SensorFusion state. + bool AttachToSensor(SensorDevice* sensor); + + // Returns true if this Sensor fusion object is attached to a sensor. + bool IsAttachedToSensor() const { return Handler.IsHandlerInstalled(); } + + + + // *** State Query + + // Obtain the current accumulated orientation. Many apps will want to use GetPredictedOrientation + // instead to reduce latency. + Quatf GetOrientation() const { return lockedGet(&Q); } + + // Get predicted orientaion in the near future; predictDt is lookahead amount in seconds. + Quatf GetPredictedOrientation(float predictDt); + Quatf GetPredictedOrientation() { return GetPredictedOrientation(PredictionDT); } + + // Obtain the last absolute acceleration reading, in m/s^2. + Vector3f GetAcceleration() const { return lockedGet(&A); } + // Obtain the last angular velocity reading, in rad/s. + Vector3f GetAngularVelocity() const { return lockedGet(&AngV); } + + // Obtain the last raw magnetometer reading, in Gauss + Vector3f GetMagnetometer() const { return lockedGet(&RawMag); } + // Obtain the calibrated magnetometer reading (direction and field strength) + Vector3f GetCalibratedMagnetometer() const { OVR_ASSERT(MagCalibrated); return lockedGet(&CalMag); } + + + // Resets the current orientation. + void Reset(); + + + + // *** Configuration + + void EnableMotionTracking(bool enable = true) { MotionTrackingEnabled = enable; } + bool IsMotionTrackingEnabled() const { return MotionTrackingEnabled; } + + // Multiplier for yaw rotation (turning); setting this higher than 1 (the default) can allow the game + // to be played without auxillary rotation controls, possibly making it more immersive. + // Whether this is more or less likely to cause motion sickness is unknown. + float GetYawMultiplier() const { return YawMult; } + void SetYawMultiplier(float y) { YawMult = y; } + + + // *** Prediction Control + + // Prediction functions. + // Prediction delta specifes how much prediction should be applied in seconds; it should in + // general be under the average rendering latency. Call GetPredictedOrientation() to get + // predicted orientation. + float GetPredictionDelta() const { return PredictionDT; } + void SetPrediction(float dt, bool enable = true) { PredictionDT = dt; EnablePrediction = enable; } + void SetPredictionEnabled(bool enable = true) { EnablePrediction = enable; } + bool IsPredictionEnabled() { return EnablePrediction; } + + + // *** Accelerometer/Gravity Correction Control + + // Enables/disables gravity correction (on by default). + void SetGravityEnabled(bool enableGravity) { EnableGravity = enableGravity; } + bool IsGravityEnabled() const { return EnableGravity;} + + // Gain used to correct gyro with accel. Default value is appropriate for typical use. + float GetAccelGain() const { return Gain; } + void SetAccelGain(float ag) { Gain = ag; } + + + + // *** Magnetometer and Yaw Drift Correction Control + + // Methods to load and save a mag calibration. Calibrations can optionally + // be specified by name to differentiate multiple calibrations under different conditions + // If LoadMagCalibration succeeds, it will override YawCorrectionEnabled based on + // saved calibration setting. + bool SaveMagCalibration(const char* calibrationName = NULL) const; + bool LoadMagCalibration(const char* calibrationName = NULL); + + // Enables/disables magnetometer based yaw drift correction. Must also have mag calibration + // data for this correction to work. + void SetYawCorrectionEnabled(bool enable) { EnableYawCorrection = enable; } + // Determines if yaw correction is enabled. + bool IsYawCorrectionEnabled() const { return EnableYawCorrection;} + + // Yaw correction is currently working (forcing a corrective yaw rotation) + bool IsYawCorrectionInProgress() const { return YawCorrectionInProgress;} + + // Store the calibration matrix for the magnetometer + void SetMagCalibration(const Matrix4f& m) + { + MagCalibrationMatrix = m; + time(&MagCalibrationTime); // time stamp the calibration + MagCalibrated = true; + } + + // Retrieves the magnetometer calibration matrix + Matrix4f GetMagCalibration() const { return MagCalibrationMatrix; } + // Retrieve the time of the calibration + time_t GetMagCalibrationTime() const { return MagCalibrationTime; } + + // True only if the mag has calibration values stored + bool HasMagCalibration() const { return MagCalibrated;} + // Force the mag into the uncalibrated state + void ClearMagCalibration() { MagCalibrated = false; } + + // These refer to reference points that associate mag readings with orientations + void ClearMagReferences() { MagNumReferences = 0; } + void SetMagRefDistance(const float d) { MagRefDistance = d; } + + + Vector3f GetCalibratedMagValue(const Vector3f& rawMag) const; + + float GetMagRefYaw() const { return MagRefYaw; } + float GetYawErrorAngle() const { return YawErrorAngle; } + + + + // *** Message Handler Logic + + // Notifies SensorFusion object about a new BodyFrame message from a sensor. + // Should be called by user if not attaching to a sensor. + void OnMessage(const MessageBodyFrame& msg) + { + OVR_ASSERT(!IsAttachedToSensor()); + handleMessage(msg); + } + + void SetDelegateMessageHandler(MessageHandler* handler) + { pDelegate = handler; } + + + +private: + + SensorFusion* getThis() { return this; } + + // Helper used to read and return value within a Lock. + template + C lockedGet(const C* p) const + { + Lock::Locker lockScope(Handler.GetHandlerLock()); + return *p; + } + + // Internal handler for messages; bypasses error checking. + void handleMessage(const MessageBodyFrame& msg); + + // Set the magnetometer's reference orientation for use in yaw correction + // The supplied mag is an uncalibrated value + void setMagReference(const Quatf& q, const Vector3f& rawMag); + // Default to current HMD orientation + void setMagReference() { setMagReference(Q, RawMag); } + + class BodyFrameHandler : public MessageHandler + { + SensorFusion* pFusion; + public: + BodyFrameHandler(SensorFusion* fusion) : pFusion(fusion) { } + ~BodyFrameHandler(); + + virtual void OnMessage(const Message& msg); + virtual bool SupportsMessageType(MessageType type) const; + }; + + SensorInfo CachedSensorInfo; + + Quatf Q; + Quatf QUncorrected; + Vector3f A; + Vector3f AngV; + Vector3f CalMag; + Vector3f RawMag; + unsigned int Stage; + float RunningTime; + float DeltaT; + BodyFrameHandler Handler; + MessageHandler* pDelegate; + float Gain; + float YawMult; + volatile bool EnableGravity; + + bool EnablePrediction; + float PredictionDT; + float PredictionTimeIncrement; + + SensorFilter FRawMag; + SensorFilter FAccW; + SensorFilter FAngV; + + int TiltCondCount; + float TiltErrorAngle; + Vector3f TiltErrorAxis; + + bool EnableYawCorrection; + Matrix4f MagCalibrationMatrix; + time_t MagCalibrationTime; + bool MagCalibrated; + int MagCondCount; + float MagRefDistance; + Quatf MagRefQ; + Vector3f MagRefM; + float MagRefYaw; + bool MagHasNearbyReference; + Quatf MagRefTableQ[MagMaxReferences]; + Vector3f MagRefTableM[MagMaxReferences]; + float MagRefTableYaw[MagMaxReferences]; + int MagNumReferences; + float YawErrorAngle; + int YawErrorCount; + bool YawCorrectionInProgress; + bool YawCorrectionActivated; + + bool MotionTrackingEnabled; +}; + + +} // namespace OVR + +#endif diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_SensorImpl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_SensorImpl.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,882 @@ +/************************************************************************************ + +Filename : OVR_SensorImpl.cpp +Content : Oculus Sensor device implementation. +Created : March 7, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_SensorImpl.h" + +// HMDDeviceDesc can be created/updated through Sensor carrying DisplayInfo. + +#include "Kernel/OVR_Timer.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +// ***** Oculus Sensor-specific packet data structures + +enum { + Sensor_VendorId = Oculus_VendorId, + Sensor_ProductId = 0x0001, + + // ST's VID used originally; should be removed in the future + Sensor_OldVendorId = 0x0483, + Sensor_OldProductId = 0x5750, + + Sensor_DefaultReportRate = 500, // Hz + Sensor_MaxReportRate = 1000 // Hz +}; + +// Reported data is little-endian now +static UInt16 DecodeUInt16(const UByte* buffer) +{ + return (UInt16(buffer[1]) << 8) | UInt16(buffer[0]); +} + +static SInt16 DecodeSInt16(const UByte* buffer) +{ + return (SInt16(buffer[1]) << 8) | SInt16(buffer[0]); +} + +static UInt32 DecodeUInt32(const UByte* buffer) +{ + return (buffer[0]) | UInt32(buffer[1] << 8) | UInt32(buffer[2] << 16) | UInt32(buffer[3] << 24); +} + +static float DecodeFloat(const UByte* buffer) +{ + union { + UInt32 U; + float F; + }; + + U = DecodeUInt32(buffer); + return F; +} + + +static void UnpackSensor(const UByte* buffer, SInt32* x, SInt32* y, SInt32* z) +{ + // Sign extending trick + // from http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend + struct {SInt32 x:21;} s; + + *x = s.x = (buffer[0] << 13) | (buffer[1] << 5) | ((buffer[2] & 0xF8) >> 3); + *y = s.x = ((buffer[2] & 0x07) << 18) | (buffer[3] << 10) | (buffer[4] << 2) | + ((buffer[5] & 0xC0) >> 6); + *z = s.x = ((buffer[5] & 0x3F) << 15) | (buffer[6] << 7) | (buffer[7] >> 1); +} + +// Messages we care for +enum TrackerMessageType +{ + TrackerMessage_None = 0, + TrackerMessage_Sensors = 1, + TrackerMessage_Unknown = 0x100, + TrackerMessage_SizeError = 0x101, +}; + +struct TrackerSample +{ + SInt32 AccelX, AccelY, AccelZ; + SInt32 GyroX, GyroY, GyroZ; +}; + + +struct TrackerSensors +{ + UByte SampleCount; + UInt16 Timestamp; + UInt16 LastCommandID; + SInt16 Temperature; + + TrackerSample Samples[3]; + + SInt16 MagX, MagY, MagZ; + + TrackerMessageType Decode(const UByte* buffer, int size) + { + if (size < 62) + return TrackerMessage_SizeError; + + SampleCount = buffer[1]; + Timestamp = DecodeUInt16(buffer + 2); + LastCommandID = DecodeUInt16(buffer + 4); + Temperature = DecodeSInt16(buffer + 6); + + //if (SampleCount > 2) + // OVR_DEBUG_LOG_TEXT(("TackerSensor::Decode SampleCount=%d\n", SampleCount)); + + // Only unpack as many samples as there actually are + UByte iterationCount = (SampleCount > 2) ? 3 : SampleCount; + + for (UByte i = 0; i < iterationCount; i++) + { + UnpackSensor(buffer + 8 + 16 * i, &Samples[i].AccelX, &Samples[i].AccelY, &Samples[i].AccelZ); + UnpackSensor(buffer + 16 + 16 * i, &Samples[i].GyroX, &Samples[i].GyroY, &Samples[i].GyroZ); + } + + MagX = DecodeSInt16(buffer + 56); + MagY = DecodeSInt16(buffer + 58); + MagZ = DecodeSInt16(buffer + 60); + + return TrackerMessage_Sensors; + } +}; + +struct TrackerMessage +{ + TrackerMessageType Type; + TrackerSensors Sensors; +}; + +bool DecodeTrackerMessage(TrackerMessage* message, UByte* buffer, int size) +{ + memset(message, 0, sizeof(TrackerMessage)); + + if (size < 4) + { + message->Type = TrackerMessage_SizeError; + return false; + } + + switch (buffer[0]) + { + case TrackerMessage_Sensors: + message->Type = message->Sensors.Decode(buffer, size); + break; + + default: + message->Type = TrackerMessage_Unknown; + break; + } + + return (message->Type < TrackerMessage_Unknown) && (message->Type != TrackerMessage_None); +} + + +// ***** SensorRangeImpl Implementation + +// Sensor HW only accepts specific maximum range values, used to maximize +// the 16-bit sensor outputs. Use these ramps to specify and report appropriate values. +static const UInt16 AccelRangeRamp[] = { 2, 4, 8, 16 }; +static const UInt16 GyroRangeRamp[] = { 250, 500, 1000, 2000 }; +static const UInt16 MagRangeRamp[] = { 880, 1300, 1900, 2500 }; + +static UInt16 SelectSensorRampValue(const UInt16* ramp, unsigned count, + float val, float factor, const char* label) +{ + UInt16 threshold = (UInt16)(val * factor); + + for (unsigned i = 0; i= threshold) + return ramp[i]; + } + OVR_DEBUG_LOG(("SensorDevice::SetRange - %s clamped to %0.4f", + label, float(ramp[count-1]) / factor)); + OVR_UNUSED2(factor, label); + return ramp[count-1]; +} + +// SensorScaleImpl provides buffer packing logic for the Sensor Range +// record that can be applied to DK1 sensor through Get/SetFeature. We expose this +// through SensorRange class, which has different units. +struct SensorRangeImpl +{ + enum { PacketSize = 8 }; + UByte Buffer[PacketSize]; + + UInt16 CommandId; + UInt16 AccelScale; + UInt16 GyroScale; + UInt16 MagScale; + + SensorRangeImpl(const SensorRange& r, UInt16 commandId = 0) + { + SetSensorRange(r, commandId); + } + + void SetSensorRange(const SensorRange& r, UInt16 commandId = 0) + { + CommandId = commandId; + AccelScale = SelectSensorRampValue(AccelRangeRamp, sizeof(AccelRangeRamp)/sizeof(AccelRangeRamp[0]), + r.MaxAcceleration, (1.0f / 9.81f), "MaxAcceleration"); + GyroScale = SelectSensorRampValue(GyroRangeRamp, sizeof(GyroRangeRamp)/sizeof(GyroRangeRamp[0]), + r.MaxRotationRate, Math::RadToDegreeFactor, "MaxRotationRate"); + MagScale = SelectSensorRampValue(MagRangeRamp, sizeof(MagRangeRamp)/sizeof(MagRangeRamp[0]), + r.MaxMagneticField, 1000.0f, "MaxMagneticField"); + Pack(); + } + + void GetSensorRange(SensorRange* r) + { + r->MaxAcceleration = AccelScale * 9.81f; + r->MaxRotationRate = DegreeToRad((float)GyroScale); + r->MaxMagneticField= MagScale * 0.001f; + } + + static SensorRange GetMaxSensorRange() + { + return SensorRange(AccelRangeRamp[sizeof(AccelRangeRamp)/sizeof(AccelRangeRamp[0]) - 1] * 9.81f, + GyroRangeRamp[sizeof(GyroRangeRamp)/sizeof(GyroRangeRamp[0]) - 1] * + Math::DegreeToRadFactor, + MagRangeRamp[sizeof(MagRangeRamp)/sizeof(MagRangeRamp[0]) - 1] * 0.001f); + } + + void Pack() + { + Buffer[0] = 4; + Buffer[1] = UByte(CommandId & 0xFF); + Buffer[2] = UByte(CommandId >> 8); + Buffer[3] = UByte(AccelScale); + Buffer[4] = UByte(GyroScale & 0xFF); + Buffer[5] = UByte(GyroScale >> 8); + Buffer[6] = UByte(MagScale & 0xFF); + Buffer[7] = UByte(MagScale >> 8); + } + + void Unpack() + { + CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8); + AccelScale= Buffer[3]; + GyroScale = Buffer[4] | (UInt16(Buffer[5]) << 8); + MagScale = Buffer[6] | (UInt16(Buffer[7]) << 8); + } +}; + + +// Sensor configuration command, ReportId == 2. + +struct SensorConfigImpl +{ + enum { PacketSize = 7 }; + UByte Buffer[PacketSize]; + + // Flag values for Flags. + enum { + Flag_RawMode = 0x01, + Flag_CallibrationTest = 0x02, // Internal test mode + Flag_UseCallibration = 0x04, + Flag_AutoCallibration = 0x08, + Flag_MotionKeepAlive = 0x10, + Flag_CommandKeepAlive = 0x20, + Flag_SensorCoordinates = 0x40 + }; + + UInt16 CommandId; + UByte Flags; + UInt16 PacketInterval; + UInt16 KeepAliveIntervalMs; + + SensorConfigImpl() : CommandId(0), Flags(0), PacketInterval(0), KeepAliveIntervalMs(0) + { + memset(Buffer, 0, PacketSize); + Buffer[0] = 2; + } + + void SetSensorCoordinates(bool sensorCoordinates) + { Flags = (Flags & ~Flag_SensorCoordinates) | (sensorCoordinates ? Flag_SensorCoordinates : 0); } + bool IsUsingSensorCoordinates() const + { return (Flags & Flag_SensorCoordinates) != 0; } + + void Pack() + { + Buffer[0] = 2; + Buffer[1] = UByte(CommandId & 0xFF); + Buffer[2] = UByte(CommandId >> 8); + Buffer[3] = Flags; + Buffer[4] = UByte(PacketInterval); + Buffer[5] = UByte(KeepAliveIntervalMs & 0xFF); + Buffer[6] = UByte(KeepAliveIntervalMs >> 8); + } + + void Unpack() + { + CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8); + Flags = Buffer[3]; + PacketInterval = Buffer[4]; + KeepAliveIntervalMs= Buffer[5] | (UInt16(Buffer[6]) << 8); + } + +}; + + +// SensorKeepAlive - feature report that needs to be sent at regular intervals for sensor +// to receive commands. +struct SensorKeepAliveImpl +{ + enum { PacketSize = 5 }; + UByte Buffer[PacketSize]; + + UInt16 CommandId; + UInt16 KeepAliveIntervalMs; + + SensorKeepAliveImpl(UInt16 interval = 0, UInt16 commandId = 0) + : CommandId(commandId), KeepAliveIntervalMs(interval) + { + Pack(); + } + + void Pack() + { + Buffer[0] = 8; + Buffer[1] = UByte(CommandId & 0xFF); + Buffer[2] = UByte(CommandId >> 8); + Buffer[3] = UByte(KeepAliveIntervalMs & 0xFF); + Buffer[4] = UByte(KeepAliveIntervalMs >> 8); + } + + void Unpack() + { + CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8); + KeepAliveIntervalMs= Buffer[3] | (UInt16(Buffer[4]) << 8); + } +}; + + +//------------------------------------------------------------------------------------- +// ***** SensorDisplayInfoImpl +SensorDisplayInfoImpl::SensorDisplayInfoImpl() + : CommandId(0), DistortionType(Base_None) +{ + memset(Buffer, 0, PacketSize); + Buffer[0] = 9; +} + +void SensorDisplayInfoImpl::Unpack() +{ + CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8); + DistortionType = Buffer[3]; + HResolution = DecodeUInt16(Buffer+4); + VResolution = DecodeUInt16(Buffer+6); + HScreenSize = DecodeUInt32(Buffer+8) * (1/1000000.f); + VScreenSize = DecodeUInt32(Buffer+12) * (1/1000000.f); + VCenter = DecodeUInt32(Buffer+16) * (1/1000000.f); + LensSeparation = DecodeUInt32(Buffer+20) * (1/1000000.f); + EyeToScreenDistance[0] = DecodeUInt32(Buffer+24) * (1/1000000.f); + EyeToScreenDistance[1] = DecodeUInt32(Buffer+28) * (1/1000000.f); + DistortionK[0] = DecodeFloat(Buffer+32); + DistortionK[1] = DecodeFloat(Buffer+36); + DistortionK[2] = DecodeFloat(Buffer+40); + DistortionK[3] = DecodeFloat(Buffer+44); + DistortionK[4] = DecodeFloat(Buffer+48); + DistortionK[5] = DecodeFloat(Buffer+52); +} + + +//------------------------------------------------------------------------------------- +// ***** SensorDeviceFactory + +SensorDeviceFactory SensorDeviceFactory::Instance; + +void SensorDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) +{ + + class SensorEnumerator : public HIDEnumerateVisitor + { + // Assign not supported; suppress MSVC warning. + void operator = (const SensorEnumerator&) { } + + DeviceFactory* pFactory; + EnumerateVisitor& ExternalVisitor; + public: + SensorEnumerator(DeviceFactory* factory, EnumerateVisitor& externalVisitor) + : pFactory(factory), ExternalVisitor(externalVisitor) { } + + virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) + { + return pFactory->MatchVendorProduct(vendorId, productId); + } + + virtual void Visit(HIDDevice& device, const HIDDeviceDesc& desc) + { + SensorDeviceCreateDesc createDesc(pFactory, desc); + ExternalVisitor.Visit(createDesc); + + // Check if the sensor returns DisplayInfo. If so, try to use it to override potentially + // mismatching monitor information (in case wrong EDID is reported by splitter), + // or to create a new "virtualized" HMD Device. + + SensorDisplayInfoImpl displayInfo; + + if (device.GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize)) + { + displayInfo.Unpack(); + + // If we got display info, try to match / create HMDDevice as well + // so that sensor settings give preference. + if (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) + { + SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(displayInfo, ExternalVisitor); + } + } + } + }; + + //double start = Timer::GetProfileSeconds(); + + SensorEnumerator sensorEnumerator(this, visitor); + GetManagerImpl()->GetHIDDeviceManager()->Enumerate(&sensorEnumerator); + + //double totalSeconds = Timer::GetProfileSeconds() - start; +} + +bool SensorDeviceFactory::MatchVendorProduct(UInt16 vendorId, UInt16 productId) const +{ + return ((vendorId == Sensor_VendorId) && (productId == Sensor_ProductId)) || + ((vendorId == Sensor_OldVendorId) && (productId == Sensor_OldProductId)); +} + +bool SensorDeviceFactory::DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc) +{ + if (MatchVendorProduct(desc.VendorId, desc.ProductId)) + { + SensorDeviceCreateDesc createDesc(this, desc); + return pdevMgr->AddDevice_NeedsLock(createDesc).GetPtr() != NULL; + } + return false; +} + +//------------------------------------------------------------------------------------- +// ***** SensorDeviceCreateDesc + +DeviceBase* SensorDeviceCreateDesc::NewDeviceInstance() +{ + return new SensorDeviceImpl(this); +} + +bool SensorDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_Sensor) && + (info->InfoClassType != Device_None)) + return false; + + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, HIDDesc.Product.ToCStr()); + OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, HIDDesc.Manufacturer.ToCStr()); + info->Type = Device_Sensor; + info->Version = 0; + + if (info->InfoClassType == Device_Sensor) + { + SensorInfo* sinfo = (SensorInfo*)info; + sinfo->VendorId = HIDDesc.VendorId; + sinfo->ProductId = HIDDesc.ProductId; + sinfo->MaxRanges = SensorRangeImpl::GetMaxSensorRange(); + OVR_strcpy(sinfo->SerialNumber, sizeof(sinfo->SerialNumber),HIDDesc.SerialNumber.ToCStr()); + } + return true; +} + + +//------------------------------------------------------------------------------------- +// ***** SensorDevice + +SensorDeviceImpl::SensorDeviceImpl(SensorDeviceCreateDesc* createDesc) + : OVR::HIDDeviceImpl(createDesc, 0), + Coordinates(SensorDevice::Coord_Sensor), + HWCoordinates(SensorDevice::Coord_HMD), // HW reports HMD coordinates by default. + NextKeepAliveTicks(0), + MaxValidRange(SensorRangeImpl::GetMaxSensorRange()) +{ + SequenceValid = false; + LastSampleCount= 0; + LastTimestamp = 0; + + OldCommandId = 0; +} + +SensorDeviceImpl::~SensorDeviceImpl() +{ + // Check that Shutdown() was called. + OVR_ASSERT(!pCreateDesc->pDevice); +} + +// Internal creation APIs. +bool SensorDeviceImpl::Initialize(DeviceBase* parent) +{ + if (HIDDeviceImpl::Initialize(parent)) + { + openDevice(); + + LogText("OVR::SensorDevice initialized.\n"); + + return true; + } + + return false; +} + +void SensorDeviceImpl::openDevice() +{ + + // Read the currently configured range from sensor. + SensorRangeImpl sr(SensorRange(), 0); + + if (GetInternalDevice()->GetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize)) + { + sr.Unpack(); + sr.GetSensorRange(&CurrentRange); + } + + + // If the sensor has "DisplayInfo" data, use HMD coordinate frame by default. + SensorDisplayInfoImpl displayInfo; + if (GetInternalDevice()->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize)) + { + displayInfo.Unpack(); + Coordinates = (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) ? + Coord_HMD : Coord_Sensor; + } + + // Read/Apply sensor config. + setCoordinateFrame(Coordinates); + setReportRate(Sensor_DefaultReportRate); + + // Set Keep-alive at 10 seconds. + SensorKeepAliveImpl skeepAlive(10 * 1000); + GetInternalDevice()->SetFeatureReport(skeepAlive.Buffer, SensorKeepAliveImpl::PacketSize); +} + +void SensorDeviceImpl::closeDeviceOnError() +{ + LogText("OVR::SensorDevice - Lost connection to '%s'\n", getHIDDesc()->Path.ToCStr()); + NextKeepAliveTicks = 0; +} + +void SensorDeviceImpl::Shutdown() +{ + HIDDeviceImpl::Shutdown(); + + LogText("OVR::SensorDevice - Closed '%s'\n", getHIDDesc()->Path.ToCStr()); +} + + +void SensorDeviceImpl::OnInputReport(UByte* pData, UInt32 length) +{ + + bool processed = false; + if (!processed) + { + + TrackerMessage message; + if (DecodeTrackerMessage(&message, pData, length)) + { + processed = true; + onTrackerMessage(&message); + } + } +} + +UInt64 SensorDeviceImpl::OnTicks(UInt64 ticksMks) +{ + + if (ticksMks >= NextKeepAliveTicks) + { + // Use 3-seconds keep alive by default. + UInt64 keepAliveDelta = Timer::MksPerSecond * 3; + + // Set Keep-alive at 10 seconds. + SensorKeepAliveImpl skeepAlive(10 * 1000); + // OnTicks is called from background thread so we don't need to add this to the command queue. + GetInternalDevice()->SetFeatureReport(skeepAlive.Buffer, SensorKeepAliveImpl::PacketSize); + + // Emit keep-alive every few seconds. + NextKeepAliveTicks = ticksMks + keepAliveDelta; + } + return NextKeepAliveTicks - ticksMks; +} + +bool SensorDeviceImpl::SetRange(const SensorRange& range, bool waitFlag) +{ + bool result = 0; + ThreadCommandQueue * threadQueue = GetManagerImpl()->GetThreadQueue(); + + if (!waitFlag) + { + return threadQueue->PushCall(this, &SensorDeviceImpl::setRange, range); + } + + if (!threadQueue->PushCallAndWaitResult(this, + &SensorDeviceImpl::setRange, + &result, + range)) + { + return false; + } + + return result; +} + +void SensorDeviceImpl::GetRange(SensorRange* range) const +{ + Lock::Locker lockScope(GetLock()); + *range = CurrentRange; +} + +bool SensorDeviceImpl::setRange(const SensorRange& range) +{ + SensorRangeImpl sr(range); + + if (GetInternalDevice()->SetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize)) + { + Lock::Locker lockScope(GetLock()); + sr.GetSensorRange(&CurrentRange); + return true; + } + + return false; +} + +void SensorDeviceImpl::SetCoordinateFrame(CoordinateFrame coordframe) +{ + // Push call with wait. + GetManagerImpl()->GetThreadQueue()-> + PushCall(this, &SensorDeviceImpl::setCoordinateFrame, coordframe, true); +} + +SensorDevice::CoordinateFrame SensorDeviceImpl::GetCoordinateFrame() const +{ + return Coordinates; +} + +Void SensorDeviceImpl::setCoordinateFrame(CoordinateFrame coordframe) +{ + + Coordinates = coordframe; + + // Read the original coordinate frame, then try to change it. + SensorConfigImpl scfg; + if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize)) + { + scfg.Unpack(); + } + + scfg.SetSensorCoordinates(coordframe == Coord_Sensor); + scfg.Pack(); + + GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize); + + // Re-read the state, in case of older firmware that doesn't support Sensor coordinates. + if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize)) + { + scfg.Unpack(); + HWCoordinates = scfg.IsUsingSensorCoordinates() ? Coord_Sensor : Coord_HMD; + } + else + { + HWCoordinates = Coord_HMD; + } + return 0; +} + +void SensorDeviceImpl::SetReportRate(unsigned rateHz) +{ + // Push call with wait. + GetManagerImpl()->GetThreadQueue()-> + PushCall(this, &SensorDeviceImpl::setReportRate, rateHz, true); +} + +unsigned SensorDeviceImpl::GetReportRate() const +{ + // Read the original configuration + SensorConfigImpl scfg; + if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize)) + { + scfg.Unpack(); + return Sensor_MaxReportRate / (scfg.PacketInterval + 1); + } + return 0; // error +} + +Void SensorDeviceImpl::setReportRate(unsigned rateHz) +{ + // Read the original configuration + SensorConfigImpl scfg; + if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize)) + { + scfg.Unpack(); + } + + if (rateHz > Sensor_MaxReportRate) + rateHz = Sensor_MaxReportRate; + else if (rateHz == 0) + rateHz = Sensor_DefaultReportRate; + + scfg.PacketInterval = UInt16((Sensor_MaxReportRate / rateHz) - 1); + + scfg.Pack(); + + GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize); + return 0; +} + +void SensorDeviceImpl::SetMessageHandler(MessageHandler* handler) +{ + if (handler) + { + SequenceValid = false; + DeviceBase::SetMessageHandler(handler); + } + else + { + DeviceBase::SetMessageHandler(handler); + } +} + +// Sensor reports data in the following coordinate system: +// Accelerometer: 10^-4 m/s^2; X forward, Y right, Z Down. +// Gyro: 10^-4 rad/s; X positive roll right, Y positive pitch up; Z positive yaw right. + + +// We need to convert it to the following RHS coordinate system: +// X right, Y Up, Z Back (out of screen) +// +Vector3f AccelFromBodyFrameUpdate(const TrackerSensors& update, UByte sampleNumber, + bool convertHMDToSensor = false) +{ + const TrackerSample& sample = update.Samples[sampleNumber]; + float ax = (float)sample.AccelX; + float ay = (float)sample.AccelY; + float az = (float)sample.AccelZ; + + Vector3f val = convertHMDToSensor ? Vector3f(ax, az, -ay) : Vector3f(ax, ay, az); + return val * 0.0001f; +} + + +Vector3f MagFromBodyFrameUpdate(const TrackerSensors& update, + bool convertHMDToSensor = false) +{ + // Note: Y and Z are swapped in comparison to the Accel. + // This accounts for DK1 sensor firmware axis swap, which should be undone in future releases. + if (!convertHMDToSensor) + { + return Vector3f( (float)update.MagX, + (float)update.MagZ, + (float)update.MagY) * 0.0001f; + } + + return Vector3f( (float)update.MagX, + (float)update.MagY, + -(float)update.MagZ) * 0.0001f; +} + +Vector3f EulerFromBodyFrameUpdate(const TrackerSensors& update, UByte sampleNumber, + bool convertHMDToSensor = false) +{ + const TrackerSample& sample = update.Samples[sampleNumber]; + float gx = (float)sample.GyroX; + float gy = (float)sample.GyroY; + float gz = (float)sample.GyroZ; + + Vector3f val = convertHMDToSensor ? Vector3f(gx, gz, -gy) : Vector3f(gx, gy, gz); + return val * 0.0001f; +} + + +void SensorDeviceImpl::onTrackerMessage(TrackerMessage* message) +{ + if (message->Type != TrackerMessage_Sensors) + return; + + const float timeUnit = (1.0f / 1000.f); + TrackerSensors& s = message->Sensors; + + + // Call OnMessage() within a lock to avoid conflicts with handlers. + Lock::Locker scopeLock(HandlerRef.GetLock()); + + + if (SequenceValid) + { + unsigned timestampDelta; + + if (s.Timestamp < LastTimestamp) + timestampDelta = ((((int)s.Timestamp) + 0x10000) - (int)LastTimestamp); + else + timestampDelta = (s.Timestamp - LastTimestamp); + + // If we missed a small number of samples, replicate the last sample. + if ((timestampDelta > LastSampleCount) && (timestampDelta <= 254)) + { + if (HandlerRef.GetHandler()) + { + MessageBodyFrame sensors(this); + sensors.TimeDelta = (timestampDelta - LastSampleCount) * timeUnit; + sensors.Acceleration = LastAcceleration; + sensors.RotationRate = LastRotationRate; + sensors.MagneticField = LastMagneticField; + sensors.Temperature = LastTemperature; + + HandlerRef.GetHandler()->OnMessage(sensors); + } + } + } + else + { + LastAcceleration = Vector3f(0); + LastRotationRate = Vector3f(0); + LastMagneticField= Vector3f(0); + LastTemperature = 0; + SequenceValid = true; + } + + LastSampleCount = s.SampleCount; + LastTimestamp = s.Timestamp; + + bool convertHMDToSensor = (Coordinates == Coord_Sensor) && (HWCoordinates == Coord_HMD); + + if (HandlerRef.GetHandler()) + { + MessageBodyFrame sensors(this); + UByte iterations = s.SampleCount; + + if (s.SampleCount > 3) + { + iterations = 3; + sensors.TimeDelta = (s.SampleCount - 2) * timeUnit; + } + else + { + sensors.TimeDelta = timeUnit; + } + + for (UByte i = 0; i < iterations; i++) + { + sensors.Acceleration = AccelFromBodyFrameUpdate(s, i, convertHMDToSensor); + sensors.RotationRate = EulerFromBodyFrameUpdate(s, i, convertHMDToSensor); + sensors.MagneticField= MagFromBodyFrameUpdate(s, convertHMDToSensor); + sensors.Temperature = s.Temperature * 0.01f; + HandlerRef.GetHandler()->OnMessage(sensors); + // TimeDelta for the last two sample is always fixed. + sensors.TimeDelta = timeUnit; + } + + LastAcceleration = sensors.Acceleration; + LastRotationRate = sensors.RotationRate; + LastMagneticField= sensors.MagneticField; + LastTemperature = sensors.Temperature; + } + else + { + UByte i = (s.SampleCount > 3) ? 2 : (s.SampleCount - 1); + LastAcceleration = AccelFromBodyFrameUpdate(s, i, convertHMDToSensor); + LastRotationRate = EulerFromBodyFrameUpdate(s, i, convertHMDToSensor); + LastMagneticField = MagFromBodyFrameUpdate(s, convertHMDToSensor); + LastTemperature = s.Temperature * 0.01f; + } +} + +} // namespace OVR + + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_SensorImpl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_SensorImpl.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,208 @@ +/************************************************************************************ + +Filename : OVR_SensorImpl.h +Content : Sensor device specific implementation. +Created : March 7, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_SensorImpl_h +#define OVR_SensorImpl_h + +#include "OVR_HIDDeviceImpl.h" + +namespace OVR { + +struct TrackerMessage; +class ExternalVisitor; + +//------------------------------------------------------------------------------------- +// SensorDeviceFactory enumerates Oculus Sensor devices. +class SensorDeviceFactory : public DeviceFactory +{ +public: + static SensorDeviceFactory Instance; + + // Enumerates devices, creating and destroying relevant objects in manager. + virtual void EnumerateDevices(EnumerateVisitor& visitor); + + virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const; + virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc); +protected: + DeviceManager* getManager() const { return (DeviceManager*) pManager; } +}; + + +// Describes a single a Oculus Sensor device and supports creating its instance. +class SensorDeviceCreateDesc : public HIDDeviceCreateDesc +{ +public: + SensorDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc) + : HIDDeviceCreateDesc(factory, Device_Sensor, hidDesc) { } + + virtual DeviceCreateDesc* Clone() const + { + return new SensorDeviceCreateDesc(*this); + } + + virtual DeviceBase* NewDeviceInstance(); + + virtual MatchResult MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc**) const + { + if ((other.Type == Device_Sensor) && (pFactory == other.pFactory)) + { + const SensorDeviceCreateDesc& s2 = (const SensorDeviceCreateDesc&) other; + if (MatchHIDDevice(s2.HIDDesc)) + return Match_Found; + } + return Match_None; + } + + virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const + { + // should paths comparison be case insensitive? + return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) && + (HIDDesc.SerialNumber == hidDesc.SerialNumber)); + } + + virtual bool GetDeviceInfo(DeviceInfo* info) const; +}; + + +//------------------------------------------------------------------------------------- +// ***** OVR::SensorDisplayInfoImpl + +// DisplayInfo obtained from sensor; these values are used to report distortion +// settings and other coefficients. +// Older SensorDisplayInfo will have all zeros, causing the library to apply hard-coded defaults. +// Currently, only resolutions and sizes are used. +struct SensorDisplayInfoImpl +{ + enum { PacketSize = 56 }; + UByte Buffer[PacketSize]; + + enum + { + Mask_BaseFmt = 0x0f, + Mask_OptionFmts = 0xf0, + Base_None = 0, + Base_ScreenOnly = 1, + Base_Distortion = 2, + }; + + UInt16 CommandId; + UByte DistortionType; + UInt16 HResolution, VResolution; + float HScreenSize, VScreenSize; + float VCenter; + float LensSeparation; + float EyeToScreenDistance[2]; + float DistortionK[6]; + + SensorDisplayInfoImpl(); + + void Unpack(); +}; + + +//------------------------------------------------------------------------------------- +// ***** OVR::SensorDeviceImpl + +// Oculus Sensor interface. + +class SensorDeviceImpl : public HIDDeviceImpl +{ +public: + SensorDeviceImpl(SensorDeviceCreateDesc* createDesc); + ~SensorDeviceImpl(); + + + // DeviceCommaon interface + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + virtual void SetMessageHandler(MessageHandler* handler); + + // HIDDevice::Notifier interface. + virtual void OnInputReport(UByte* pData, UInt32 length); + virtual UInt64 OnTicks(UInt64 ticksMks); + + // HMD-Mounted sensor has a different coordinate frame. + virtual void SetCoordinateFrame(CoordinateFrame coordframe); + virtual CoordinateFrame GetCoordinateFrame() const; + + // SensorDevice interface + virtual bool SetRange(const SensorRange& range, bool waitFlag); + virtual void GetRange(SensorRange* range) const; + + // Sets report rate (in Hz) of MessageBodyFrame messages (delivered through MessageHandler::OnMessage call). + // Currently supported maximum rate is 1000Hz. If the rate is set to 500 or 333 Hz then OnMessage will be + // called twice or thrice at the same 'tick'. + // If the rate is < 333 then the OnMessage / MessageBodyFrame will be called three + // times for each 'tick': the first call will contain averaged values, the second + // and third calls will provide with most recent two recorded samples. + virtual void SetReportRate(unsigned rateHz); + // Returns currently set report rate, in Hz. If 0 - error occurred. + // Note, this value may be different from the one provided for SetReportRate. The return + // value will contain the actual rate. + virtual unsigned GetReportRate() const; + + // Hack to create HMD device from sensor display info. + static void EnumerateHMDFromSensorDisplayInfo(const SensorDisplayInfoImpl& displayInfo, + DeviceFactory::EnumerateVisitor& visitor); +protected: + + void openDevice(); + void closeDeviceOnError(); + + Void setCoordinateFrame(CoordinateFrame coordframe); + bool setRange(const SensorRange& range); + + Void setReportRate(unsigned rateHz); + + // Called for decoded messages + void onTrackerMessage(TrackerMessage* message); + + // Helpers to reduce casting. +/* + SensorDeviceCreateDesc* getCreateDesc() const + { return (SensorDeviceCreateDesc*)pCreateDesc.GetPtr(); } + + HIDDeviceDesc* getHIDDesc() const + { return &getCreateDesc()->HIDDesc; } +*/ + + // Set if the sensor is located on the HMD. + // Older prototype firmware doesn't support changing HW coordinates, + // so we track its state. + CoordinateFrame Coordinates; + CoordinateFrame HWCoordinates; + UInt64 NextKeepAliveTicks; + + bool SequenceValid; + SInt16 LastTimestamp; + UByte LastSampleCount; + float LastTemperature; + Vector3f LastAcceleration; + Vector3f LastRotationRate; + Vector3f LastMagneticField; + + // Current sensor range obtained from device. + SensorRange MaxValidRange; + SensorRange CurrentRange; + + UInt16 OldCommandId; +}; + + +} // namespace OVR + +#endif // OVR_SensorImpl_h diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_ThreadCommandQueue.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_ThreadCommandQueue.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,370 @@ +/************************************************************************************ + +PublicHeader: None +Filename : OVR_ThreadCommandQueue.cpp +Content : Command queue for operations executed on a thread +Created : October 29, 2012 + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_ThreadCommandQueue.h" + +namespace OVR { + + +//------------------------------------------------------------------------ +// ***** CircularBuffer + +// CircularBuffer is a FIFO buffer implemented in a single block of memory, +// which allows writing and reading variable-size data chucks. Write fails +// if buffer is full. + +class CircularBuffer +{ + enum { + AlignSize = 16, + AlignMask = AlignSize - 1 + }; + + UByte* pBuffer; + UPInt Size; + UPInt Tail; // Byte offset of next item to be popped. + UPInt Head; // Byte offset of where next push will take place. + UPInt End; // When Head < Tail, this is used instead of Size. + + inline UPInt roundUpSize(UPInt size) + { return (size + AlignMask) & ~(UPInt)AlignMask; } + +public: + + CircularBuffer(UPInt size) + : Size(size), Tail(0), Head(0), End(0) + { + pBuffer = (UByte*)OVR_ALLOC_ALIGNED(roundUpSize(size), AlignSize); + } + ~CircularBuffer() + { + // For ThreadCommands, we must consume everything before shutdown. + OVR_ASSERT(IsEmpty()); + OVR_FREE_ALIGNED(pBuffer); + } + + bool IsEmpty() const { return (Head == Tail); } + + // Allocates a state block of specified size and advances pointers, + // returning 0 if buffer is full. + UByte* Write(UPInt size); + + // Returns a pointer to next available data block; 0 if none available. + UByte* ReadBegin() + { return (Head != Tail) ? (pBuffer + Tail) : 0; } + // Consumes data of specified size; this must match size passed to Write. + void ReadEnd(UPInt size); +}; + + +// Allocates a state block of specified size and advances pointers, +// returning 0 if buffer is full. +UByte* CircularBuffer::Write(UPInt size) +{ + UByte* p = 0; + + size = roundUpSize(size); + // Since this is circular buffer, always allow at least one item. + OVR_ASSERT(size < Size/2); + + if (Head >= Tail) + { + OVR_ASSERT(End == 0); + + if (size <= (Size - Head)) + { + p = pBuffer + Head; + Head += size; + } + else if (size < Tail) + { + p = pBuffer; + End = Head; + Head = size; + OVR_ASSERT(Head != Tail); + } + } + else + { + OVR_ASSERT(End != 0); + + if ((Tail - Head) > size) + { + p = pBuffer + Head; + Head += size; + OVR_ASSERT(Head != Tail); + } + } + + return p; +} + +void CircularBuffer::ReadEnd(UPInt size) +{ + OVR_ASSERT(Head != Tail); + size = roundUpSize(size); + + Tail += size; + if (Tail == End) + { + Tail = End = 0; + } + else if (Tail == Head) + { + OVR_ASSERT(End == 0); + Tail = Head = 0; + } +} + + +//------------------------------------------------------------------------------------- +// ***** ThreadCommand + +ThreadCommand::PopBuffer::~PopBuffer() +{ + if (Size) + Destruct(toCommand()); +} + +void ThreadCommand::PopBuffer::InitFromBuffer(void* data) +{ + ThreadCommand* cmd = (ThreadCommand*)data; + OVR_ASSERT(cmd->Size <= MaxSize); + + if (Size) + Destruct(toCommand()); + Size = cmd->Size; + memcpy(Buffer, (void*)cmd, Size); +} + +void ThreadCommand::PopBuffer::Execute() +{ + ThreadCommand* command = toCommand(); + OVR_ASSERT(command); + command->Execute(); + if (NeedsWait()) + GetEvent()->PulseEvent(); +} + +//------------------------------------------------------------------------------------- + +class ThreadCommandQueueImpl : public NewOverrideBase +{ + typedef ThreadCommand::NotifyEvent NotifyEvent; + friend class ThreadCommandQueue; + +public: + + ThreadCommandQueueImpl(ThreadCommandQueue* queue) + : pQueue(queue), CommandBuffer(2048), + ExitEnqueued(false), ExitProcessed(false) + { + } + ~ThreadCommandQueueImpl(); + + + bool PushCommand(const ThreadCommand& command); + bool PopCommand(ThreadCommand::PopBuffer* popBuffer); + + + // ExitCommand is used by notify us that Thread is shutting down. + struct ExitCommand : public ThreadCommand + { + ThreadCommandQueueImpl* pImpl; + + ExitCommand(ThreadCommandQueueImpl* impl, bool wait) + : ThreadCommand(sizeof(ExitCommand), wait, true), pImpl(impl) { } + + virtual void Execute() const + { + Lock::Locker lock(&pImpl->QueueLock); + pImpl->ExitProcessed = true; + } + virtual ThreadCommand* CopyConstruct(void* p) const + { return Construct(p, *this); } + }; + + + NotifyEvent* AllocNotifyEvent_NTS() + { + NotifyEvent* p = AvailableEvents.GetFirst(); + + if (!AvailableEvents.IsNull(p)) + p->RemoveNode(); + else + p = new NotifyEvent; + return p; + } + + void FreeNotifyEvent_NTS(NotifyEvent* p) + { + AvailableEvents.PushBack(p); + } + + void FreeNotifyEvents_NTS() + { + while(!AvailableEvents.IsEmpty()) + { + NotifyEvent* p = AvailableEvents.GetFirst(); + p->RemoveNode(); + delete p; + } + } + + ThreadCommandQueue* pQueue; + Lock QueueLock; + volatile bool ExitEnqueued; + volatile bool ExitProcessed; + List AvailableEvents; + List BlockedProducers; + CircularBuffer CommandBuffer; +}; + + + +ThreadCommandQueueImpl::~ThreadCommandQueueImpl() +{ + Lock::Locker lock(&QueueLock); + OVR_ASSERT(BlockedProducers.IsEmpty()); + FreeNotifyEvents_NTS(); +} + +bool ThreadCommandQueueImpl::PushCommand(const ThreadCommand& command) +{ + ThreadCommand::NotifyEvent* completeEvent = 0; + ThreadCommand::NotifyEvent* queueAvailableEvent = 0; + + // Repeat writing command into buffer until it is available. + do { + + { // Lock Scope + Lock::Locker lock(&QueueLock); + + if (queueAvailableEvent) + { + FreeNotifyEvent_NTS(queueAvailableEvent); + queueAvailableEvent = 0; + } + + // Don't allow any commands after PushExitCommand() is called. + if (ExitEnqueued && !command.ExitFlag) + return false; + + + bool bufferWasEmpty = CommandBuffer.IsEmpty(); + UByte* buffer = CommandBuffer.Write(command.GetSize()); + if (buffer) + { + ThreadCommand* c = command.CopyConstruct(buffer); + if (c->NeedsWait()) + completeEvent = c->pEvent = AllocNotifyEvent_NTS(); + // Signal-waker consumer when we add data to buffer. + if (bufferWasEmpty) + pQueue->OnPushNonEmpty_Locked(); + break; + } + + queueAvailableEvent = AllocNotifyEvent_NTS(); + BlockedProducers.PushBack(queueAvailableEvent); + } // Lock Scope + + queueAvailableEvent->Wait(); + + } while(1); + + // Command was enqueued, wait if necessary. + if (completeEvent) + { + completeEvent->Wait(); + Lock::Locker lock(&QueueLock); + FreeNotifyEvent_NTS(completeEvent); + } + + return true; +} + + +// Pops the next command from the thread queue, if any is available. +bool ThreadCommandQueueImpl::PopCommand(ThreadCommand::PopBuffer* popBuffer) +{ + Lock::Locker lock(&QueueLock); + + UByte* buffer = CommandBuffer.ReadBegin(); + if (!buffer) + { + // Notify thread while in lock scope, enabling initialization of wait. + pQueue->OnPopEmpty_Locked(); + return false; + } + + popBuffer->InitFromBuffer(buffer); + CommandBuffer.ReadEnd(popBuffer->GetSize()); + + if (!BlockedProducers.IsEmpty()) + { + ThreadCommand::NotifyEvent* queueAvailableEvent = BlockedProducers.GetFirst(); + queueAvailableEvent->RemoveNode(); + queueAvailableEvent->PulseEvent(); + // Event is freed later by waiter. + } + return true; +} + + +//------------------------------------------------------------------------------------- + +ThreadCommandQueue::ThreadCommandQueue() +{ + pImpl = new ThreadCommandQueueImpl(this); +} +ThreadCommandQueue::~ThreadCommandQueue() +{ + delete pImpl; +} + +bool ThreadCommandQueue::PushCommand(const ThreadCommand& command) +{ + return pImpl->PushCommand(command); +} + +bool ThreadCommandQueue::PopCommand(ThreadCommand::PopBuffer* popBuffer) +{ + return pImpl->PopCommand(popBuffer); +} + +void ThreadCommandQueue::PushExitCommand(bool wait) +{ + // Exit is processed in two stages: + // - First, ExitEnqueued flag is set to block further commands from queuing up. + // - Second, the actual exit call is processed on the consumer thread, flushing + // any prior commands. + // IsExiting() only returns true after exit has flushed. + { + Lock::Locker lock(&pImpl->QueueLock); + if (pImpl->ExitEnqueued) + return; + pImpl->ExitEnqueued = true; + } + + PushCommand(ThreadCommandQueueImpl::ExitCommand(pImpl, wait)); +} + +bool ThreadCommandQueue::IsExiting() const +{ + return pImpl->ExitProcessed; +} + + +} // namespace OVR diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/OVR_ThreadCommandQueue.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/OVR_ThreadCommandQueue.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,308 @@ +/************************************************************************************ + +PublicHeader: None +Filename : OVR_ThreadCommandQueue.h +Content : Command queue for operations executed on a thread +Created : October 29, 2012 +Author : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_ThreadCommandQueue_h +#define OVR_ThreadCommandQueue_h + +#include "Kernel/OVR_Types.h" +#include "Kernel/OVR_List.h" +#include "Kernel/OVR_Atomic.h" +#include "Kernel/OVR_Threads.h" + +namespace OVR { + +class ThreadCommand; +class ThreadCommandQueue; + + +//------------------------------------------------------------------------------------- +// ***** ThreadCommand + +// ThreadCommand is a base class implementation for commands stored in ThreadCommandQueue. +class ThreadCommand +{ +public: + + // NotifyEvent is used by ThreadCommandQueue::PushCallAndWait to notify the + // calling (producer) thread when command is completed or queue slot is available. + class NotifyEvent : public ListNode, public NewOverrideBase + { + Event E; + public: + NotifyEvent() { } + + void Wait() { E.Wait(); } + void PulseEvent() { E.PulseEvent(); } + }; + + // ThreadCommand::PopBuffer is temporary storage for a command popped off + // by ThreadCommandQueue::PopCommand. + class PopBuffer + { + enum { MaxSize = 256 }; + + UPInt Size; + union { + UByte Buffer[MaxSize]; + UPInt Align; + }; + + ThreadCommand* toCommand() const { return (ThreadCommand*)Buffer; } + + public: + PopBuffer() : Size(0) { } + ~PopBuffer(); + + void InitFromBuffer(void* data); + + bool HasCommand() const { return Size != 0; } + UPInt GetSize() const { return Size; } + bool NeedsWait() const { return toCommand()->NeedsWait(); } + NotifyEvent* GetEvent() const { return toCommand()->pEvent; } + + // Execute the command and also notifies caller to finish waiting, + // if necessary. + void Execute(); + }; + + UInt16 Size; + bool WaitFlag; + bool ExitFlag; // Marks the last exit command. + NotifyEvent* pEvent; + + ThreadCommand(UPInt size, bool waitFlag, bool exitFlag = false) + : Size((UInt16)size), WaitFlag(waitFlag), ExitFlag(exitFlag), pEvent(0) { } + virtual ~ThreadCommand() { } + + bool NeedsWait() const { return WaitFlag; } + UPInt GetSize() const { return Size; } + + virtual void Execute() const = 0; + // Copy constructor used for serializing this to memory buffer. + virtual ThreadCommand* CopyConstruct(void* p) const = 0; +}; + + +//------------------------------------------------------------------------------------- + +// CleanType is a template that strips 'const' and '&' modifiers from the argument type; +// for example, typename CleanType::Type is equivalent to A. +template struct CleanType { typedef T Type; }; +template struct CleanType { typedef T Type; }; +template struct CleanType { typedef T Type; }; +template struct CleanType { typedef T Type; }; + +// SelfType is a template that yields the argument type. This helps avoid conflicts with +// automatic template argument deduction for function calls when identical argument +// is already defined. +template struct SelfType { typedef T Type; }; + + + +//------------------------------------------------------------------------------------- +// ThreadCommand specializations for member functions with different number of +// arguments and argument types. + +// Used to return nothing from a ThreadCommand, to avoid problems with 'void'. +struct Void +{ + Void() {} + Void(int) {} +}; + +// ThreadCommand for member function with 0 arguments. +template +class ThreadCommandMF0 : public ThreadCommand +{ + typedef R (C::*FnPtr)(); + C* pClass; + FnPtr pFn; + R* pRet; + + void executeImpl() const + { + pRet ? (void)(*pRet = (pClass->*pFn)()) : + (void)(pClass->*pFn)(); + } + +public: + ThreadCommandMF0(C* pclass, FnPtr fn, R* ret, bool needsWait) + : ThreadCommand(sizeof(ThreadCommandMF0), needsWait), + pClass(pclass), pFn(fn), pRet(ret) { } + + virtual void Execute() const { executeImpl(); } + virtual ThreadCommand* CopyConstruct(void* p) const + { return Construct(p, *this); } +}; + + +// ThreadCommand for member function with 1 argument. +template +class ThreadCommandMF1 : public ThreadCommand +{ + typedef R (C::*FnPtr)(A0); + C* pClass; + FnPtr pFn; + R* pRet; + typename CleanType::Type AVal0; + + void executeImpl() const + { + pRet ? (void)(*pRet = (pClass->*pFn)(AVal0)) : + (void)(pClass->*pFn)(AVal0); + } + +public: + ThreadCommandMF1(C* pclass, FnPtr fn, R* ret, A0 a0, bool needsWait) + : ThreadCommand(sizeof(ThreadCommandMF1), needsWait), + pClass(pclass), pFn(fn), pRet(ret), AVal0(a0) { } + + virtual void Execute() const { executeImpl(); } + virtual ThreadCommand* CopyConstruct(void* p) const + { return Construct(p, *this); } +}; + +// ThreadCommand for member function with 2 arguments. +template +class ThreadCommandMF2 : public ThreadCommand +{ + typedef R (C::*FnPtr)(A0, A1); + C* pClass; + FnPtr pFn; + R* pRet; + typename CleanType::Type AVal0; + typename CleanType::Type AVal1; + + void executeImpl() const + { + pRet ? (void)(*pRet = (pClass->*pFn)(AVal0, AVal1)) : + (void)(pClass->*pFn)(AVal0, AVal1); + } + +public: + ThreadCommandMF2(C* pclass, FnPtr fn, R* ret, A0 a0, A1 a1, bool needsWait) + : ThreadCommand(sizeof(ThreadCommandMF2), needsWait), + pClass(pclass), pFn(fn), pRet(ret), AVal0(a0), AVal1(a1) { } + + virtual void Execute() const { executeImpl(); } + virtual ThreadCommand* CopyConstruct(void* p) const + { return Construct(p, *this); } +}; + + +//------------------------------------------------------------------------------------- +// ***** ThreadCommandQueue + +// ThreadCommandQueue is a queue of executable function-call commands intended to be +// serviced by a single consumer thread. Commands are added to the queue with PushCall +// and removed with PopCall; they are processed in FIFO order. Multiple producer threads +// are supported and will be blocked if internal data buffer is full. + +class ThreadCommandQueue +{ +public: + + ThreadCommandQueue(); + virtual ~ThreadCommandQueue(); + + + // Pops the next command from the thread queue, if any is available. + // The command should be executed by calling popBuffer->Execute(). + // Returns 'false' if no command is available at the time of the call. + bool PopCommand(ThreadCommand::PopBuffer* popBuffer); + + // Generic implementaion of PushCommand; enqueues a command for execution. + // Returns 'false' if push failed, usually indicating thread shutdown. + bool PushCommand(const ThreadCommand& command); + + // + void PushExitCommand(bool wait); + + // Returns 'true' once ExitCommand has been processed, so the thread can shut down. + bool IsExiting() const; + + + // These two virtual functions serve as notifications for derived + // thread waiting. + virtual void OnPushNonEmpty_Locked() { } + virtual void OnPopEmpty_Locked() { } + + + // *** PushCall with no result + + // Enqueue a member function of 'this' class to be called on consumer thread. + // By default the function returns immediately; set 'wait' argument to 'true' to + // wait for completion. + template + bool PushCall(R (C::*fn)(), bool wait = false) + { return PushCommand(ThreadCommandMF0(static_cast(this), fn, 0, wait)); } + template + bool PushCall(R (C::*fn)(A0), typename SelfType::Type a0, bool wait = false) + { return PushCommand(ThreadCommandMF1(static_cast(this), fn, 0, a0, wait)); } + template + bool PushCall(R (C::*fn)(A0, A1), + typename SelfType::Type a0, typename SelfType::Type a1, bool wait = false) + { return PushCommand(ThreadCommandMF2(static_cast(this), fn, 0, a0, a1, wait)); } + // Enqueue a specified member function call of class C. + // By default the function returns immediately; set 'wait' argument to 'true' to + // wait for completion. + template + bool PushCall(C* p, R (C::*fn)(), bool wait = false) + { return PushCommand(ThreadCommandMF0(p, fn, 0, wait)); } + template + bool PushCall(C* p, R (C::*fn)(A0), typename SelfType::Type a0, bool wait = false) + { return PushCommand(ThreadCommandMF1(p, fn, 0, a0, wait)); } + template + bool PushCall(C* p, R (C::*fn)(A0, A1), + typename SelfType::Type a0, typename SelfType::Type a1, bool wait = false) + { return PushCommand(ThreadCommandMF2(p, fn, 0, a0, a1, wait)); } + + + // *** PushCall with Result + + // Enqueue a member function of 'this' class call and wait for call to complete + // on consumer thread before returning. + template + bool PushCallAndWaitResult(R (C::*fn)(), R* ret) + { return PushCommand(ThreadCommandMF0(static_cast(this), fn, ret, true)); } + template + bool PushCallAndWaitResult(R (C::*fn)(A0), R* ret, typename SelfType::Type a0) + { return PushCommand(ThreadCommandMF1(static_cast(this), fn, ret, a0, true)); } + template + bool PushCallAndWaitResult(R (C::*fn)(A0, A1), R* ret, + typename SelfType::Type a0, typename SelfType::Type a1) + { return PushCommand(ThreadCommandMF2(static_cast(this), fn, ret, a0, a1, true)); } + // Enqueue a member function call for class C and wait for the call to complete + // on consumer thread before returning. + template + bool PushCallAndWaitResult(C* p, R (C::*fn)(), R* ret) + { return PushCommand(ThreadCommandMF0(p, fn, ret, true)); } + template + bool PushCallAndWaitResult(C* p, R (C::*fn)(A0), R* ret, typename SelfType::Type a0) + { return PushCommand(ThreadCommandMF1(p, fn, ret, a0, true)); } + template + bool PushCallAndWaitResult(C* p, R (C::*fn)(A0, A1), R* ret, + typename SelfType::Type a0, typename SelfType::Type a1) + { return PushCommand(ThreadCommandMF2(p, fn, ret, a0, a1, true)); } + +private: + class ThreadCommandQueueImpl* pImpl; +}; + + +} + +#endif // OVR_ThreadCommandQueue_h diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Util/Util_LatencyTest.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Util/Util_LatencyTest.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : Util_LatencyTest.cpp Content : Wraps the lower level LatencyTester interface and adds functionality. Created : February 14, 2013 Authors : Lee Cooper Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #include "Util_LatencyTest.h" #include "../Kernel/OVR_Log.h" #include "../Kernel/OVR_Timer.h" namespace OVR { namespace Util { static const UInt32 TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION = 16*10; static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION = 16*10; static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT = 16*5; static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS = 16*5; static const UInt32 DEFAULT_NUMBER_OF_SAMPLES = 10; // For both color 1->2 and color 2->1 transitions. static const UInt32 INITIAL_SAMPLES_TO_IGNORE = 4; static const UInt32 TIMEOUT_WAITING_FOR_TEST_STARTED = 1000; static const UInt32 TIMEOUT_WAITING_FOR_COLOR_DETECTED = 4000; static const Color CALIBRATE_BLACK(0, 0, 0); static const Color CALIBRATE_WHITE(255, 255, 255); static const Color COLOR1(0, 0, 0); static const Color COLOR2(255, 255, 255); static const Color SENSOR_DETECT_THRESHOLD(128, 255, 255); static const float BIG_FLOAT = 1000000.0f; static const float SMALL_FLOAT = -1000000.0f; //------------------------------------------------------------------------------------- // ***** LatencyTest LatencyTest::LatencyTest(LatencyTestDevice* device) : Handler(getThis()) { if (device != NULL) { SetDevice(device); } reset(); srand(Timer::GetTicksMs()); } LatencyTest::~LatencyTest() { clearMeasurementResults(); } bool LatencyTest::SetDevice(LatencyTestDevice* device) { if (device != Device) { if (device != NULL) { if (device->GetMessageHandler() != NULL) { OVR_DEBUG_LOG( ("LatencyTest::AttachToDevice failed - device %p already has handler", device)); return false; } } if (Device != NULL) { Device->SetMessageHandler(0); } Device = device; if (Device != NULL) { Device->SetMessageHandler(&Handler); // Set trigger threshold. LatencyTestConfiguration configuration(SENSOR_DETECT_THRESHOLD, false); // No samples streaming. Device->SetConfiguration(configuration, true); // Set display to intial (3 dashes). LatencyTestDisplay ltd(2, 0x40400040); Device->SetDisplay(ltd); } } return true; } UInt32 LatencyTest::getRandomComponent(UInt32 range) { UInt32 val = rand() % range; return val; } void LatencyTest::BeginTest() { if (State == State_WaitingForButton) { // Set color to black and wait a while. RenderColor = CALIBRATE_BLACK; State = State_WaitingForSettlePreCalibrationColorBlack; OVR_DEBUG_LOG(("State_WaitingForButton -> State_WaitingForSettlePreCalibrationColorBlack.")); setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); } } void LatencyTest::handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage) { // For debugging. /* if (msg.Type == Message_LatencyTestSamples) { MessageLatencyTestSamples* pSamples = (MessageLatencyTestSamples*) &msg; if (pSamples->Samples.GetSize() > 0) { // Just show the first one for now. Color c = pSamples->Samples[0]; OVR_DEBUG_LOG(("%d %d %d", c.R, c.G, c.B)); } return; } */ if (latencyTestMessage == LatencyTest_Timer) { if (!Device) { reset(); return; } if (State == State_WaitingForSettlePreCalibrationColorBlack) { // Send calibrate message to device and wait a while. Device->SetCalibrate(CALIBRATE_BLACK); State = State_WaitingForSettlePostCalibrationColorBlack; OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorBlack -> State_WaitingForSettlePostCalibrationColorBlack.")); setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); } else if (State == State_WaitingForSettlePostCalibrationColorBlack) { // Change color to white and wait a while. RenderColor = CALIBRATE_WHITE; State = State_WaitingForSettlePreCalibrationColorWhite; OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorBlack -> State_WaitingForSettlePreCalibrationColorWhite.")); setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); } else if (State == State_WaitingForSettlePreCalibrationColorWhite) { // Send calibrate message to device and wait a while. Device->SetCalibrate(CALIBRATE_WHITE); State = State_WaitingForSettlePostCalibrationColorWhite; OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorWhite -> State_WaitingForSettlePostCalibrationColorWhite.")); setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); } else if (State == State_WaitingForSettlePostCalibrationColorWhite) { // Calibration is done. Switch to color 1 and wait for it to settle. RenderColor = COLOR1; State = State_WaitingForSettlePostMeasurement; OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorWhite -> State_WaitingForSettlePostMeasurement.")); UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); setTimer(waitTime); } else if (State == State_WaitingForSettlePostMeasurement) { // Prepare for next measurement. // Create a new result object. MeasurementResult* pResult = new MeasurementResult(); Results.PushBack(pResult); State = State_WaitingToTakeMeasurement; OVR_DEBUG_LOG(("State_WaitingForSettlePostMeasurement -> State_WaitingToTakeMeasurement.")); } else if (State == State_WaitingForTestStarted) { // We timed out waiting for 'TestStarted'. Abandon this measurement and setup for the next. getActiveResult()->TimedOutWaitingForTestStarted = true; State = State_WaitingForSettlePostMeasurement; OVR_DEBUG_LOG(("** Timed out waiting for 'TestStarted'.")); OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForSettlePostMeasurement.")); UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); setTimer(waitTime); } else if (State == State_WaitingForColorDetected) { // We timed out waiting for 'ColorDetected'. Abandon this measurement and setup for the next. getActiveResult()->TimedOutWaitingForColorDetected = true; State = State_WaitingForSettlePostMeasurement; OVR_DEBUG_LOG(("** Timed out waiting for 'ColorDetected'.")); OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); setTimer(waitTime); } } else if (latencyTestMessage == LatencyTest_ProcessInputs) { if (State == State_WaitingToTakeMeasurement) { if (!Device) { reset(); return; } // Send 'StartTest' feature report with opposite target color. if (RenderColor == COLOR1) { RenderColor = COLOR2; } else { RenderColor = COLOR1; } getActiveResult()->TargetColor = RenderColor; // Record time so we can determine usb roundtrip time. getActiveResult()->StartTestTicksMicroS = Timer::GetTicks(); Device->SetStartTest(RenderColor); State = State_WaitingForTestStarted; OVR_DEBUG_LOG(("State_WaitingToTakeMeasurement -> State_WaitingForTestStarted.")); setTimer(TIMEOUT_WAITING_FOR_TEST_STARTED); LatencyTestDisplay ltd(2, 0x40090040); Device->SetDisplay(ltd); } } else if (msg.Type == Message_LatencyTestButton) { BeginTest(); } else if (msg.Type == Message_LatencyTestStarted) { if (State == State_WaitingForTestStarted) { clearTimer(); // Record time so we can determine usb roundtrip time. getActiveResult()->TestStartedTicksMicroS = Timer::GetTicks(); State = State_WaitingForColorDetected; OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForColorDetected.")); setTimer(TIMEOUT_WAITING_FOR_COLOR_DETECTED); } } else if (msg.Type == Message_LatencyTestColorDetected) { if (State == State_WaitingForColorDetected) { // Record time to detect color. MessageLatencyTestColorDetected* pDetected = (MessageLatencyTestColorDetected*) &msg; UInt16 elapsedTime = pDetected->Elapsed; OVR_DEBUG_LOG(("Time to 'ColorDetected' = %d", elapsedTime)); getActiveResult()->DeviceMeasuredElapsedMilliS = elapsedTime; if (areResultsComplete()) { // We're done. processResults(); reset(); } else { // Run another measurement. State = State_WaitingForSettlePostMeasurement; OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); setTimer(waitTime); LatencyTestDisplay ltd(2, 0x40400040); Device->SetDisplay(ltd); } } } else if (msg.Type == Message_DeviceRemoved) { reset(); } } LatencyTest::MeasurementResult* LatencyTest::getActiveResult() { OVR_ASSERT(!Results.IsEmpty()); return Results.GetLast(); } void LatencyTest::setTimer(UInt32 timeMilliS) { ActiveTimerMilliS = timeMilliS; } void LatencyTest::clearTimer() { ActiveTimerMilliS = 0; } void LatencyTest::reset() { clearMeasurementResults(); State = State_WaitingForButton; HaveOldTime = false; ActiveTimerMilliS = 0; } void LatencyTest::clearMeasurementResults() { while(!Results.IsEmpty()) { MeasurementResult* pElem = Results.GetFirst(); pElem->RemoveNode(); delete pElem; } } LatencyTest::LatencyTestHandler::~LatencyTestHandler() { RemoveHandlerFromDevices(); } void LatencyTest::LatencyTestHandler::OnMessage(const Message& msg) { pLatencyTestUtil->handleMessage(msg); } void LatencyTest::ProcessInputs() { updateForTimeouts(); handleMessage(Message(), LatencyTest_ProcessInputs); } bool LatencyTest::DisplayScreenColor(Color& colorToDisplay) { updateForTimeouts(); if (State == State_WaitingForButton) { return false; } colorToDisplay = RenderColor; return true; } const char* LatencyTest::GetResultsString() { if (!ResultsString.IsEmpty() && ReturnedResultString != ResultsString.ToCStr()) { ReturnedResultString = ResultsString; return ReturnedResultString.ToCStr(); } return NULL; } bool LatencyTest::areResultsComplete() { UInt32 initialMeasurements = 0; UInt32 measurements1to2 = 0; UInt32 measurements2to1 = 0; MeasurementResult* pCurr = Results.GetFirst(); while(true) { // Process. if (!pCurr->TimedOutWaitingForTestStarted && !pCurr->TimedOutWaitingForColorDetected) { initialMeasurements++; if (initialMeasurements > INITIAL_SAMPLES_TO_IGNORE) { if (pCurr->TargetColor == COLOR2) { measurements1to2++; } else { measurements2to1++; } } } if (Results.IsLast(pCurr)) { break; } pCurr = Results.GetNext(pCurr); } if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) { return true; } return false; } void LatencyTest::processResults() { UInt32 minTime1To2 = UINT_MAX; UInt32 maxTime1To2 = 0; float averageTime1To2 = 0.0f; UInt32 minTime2To1 = UINT_MAX; UInt32 maxTime2To1 = 0; float averageTime2To1 = 0.0f; float minUSBTripMilliS = BIG_FLOAT; float maxUSBTripMilliS = SMALL_FLOAT; float averageUSBTripMilliS = 0.0f; UInt32 countUSBTripTime = 0; UInt32 measurementsCount = 0; UInt32 measurements1to2 = 0; UInt32 measurements2to1 = 0; MeasurementResult* pCurr = Results.GetFirst(); UInt32 count = 0; while(true) { count++; if (!pCurr->TimedOutWaitingForTestStarted && !pCurr->TimedOutWaitingForColorDetected) { measurementsCount++; if (measurementsCount > INITIAL_SAMPLES_TO_IGNORE) { if (pCurr->TargetColor == COLOR2) { measurements1to2++; if (measurements1to2 <= DEFAULT_NUMBER_OF_SAMPLES) { UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; minTime1To2 = Alg::Min(elapsed, minTime1To2); maxTime1To2 = Alg::Max(elapsed, maxTime1To2); averageTime1To2 += (float) elapsed; } } else { measurements2to1++; if (measurements2to1 <= DEFAULT_NUMBER_OF_SAMPLES) { UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; minTime2To1 = Alg::Min(elapsed, minTime2To1); maxTime2To1 = Alg::Max(elapsed, maxTime2To1); averageTime2To1 += (float) elapsed; } } float usbRountripElapsedMilliS = 0.001f * (float) (pCurr->TestStartedTicksMicroS - pCurr->StartTestTicksMicroS); minUSBTripMilliS = Alg::Min(usbRountripElapsedMilliS, minUSBTripMilliS); maxUSBTripMilliS = Alg::Max(usbRountripElapsedMilliS, maxUSBTripMilliS); averageUSBTripMilliS += usbRountripElapsedMilliS; countUSBTripTime++; } } if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) { break; } if (Results.IsLast(pCurr)) { break; } pCurr = Results.GetNext(pCurr); } averageTime1To2 /= (float) DEFAULT_NUMBER_OF_SAMPLES; averageTime2To1 /= (float) DEFAULT_NUMBER_OF_SAMPLES; averageUSBTripMilliS /= countUSBTripTime; float finalResult = 0.5f * (averageTime1To2 + averageTime2To1); finalResult += averageUSBTripMilliS; ResultsString.Clear(); ResultsString.AppendFormat("RESULT=%.1f (add half Tracker period) [b->w %d|%.1f|%d] [w->b %d|%.1f|%d] [usb rndtrp %.1f|%.1f|%.1f] [cnt %d] [tmouts %d]", finalResult, minTime1To2, averageTime1To2, maxTime1To2, minTime2To1, averageTime2To1, maxTime2To1, minUSBTripMilliS, averageUSBTripMilliS, maxUSBTripMilliS, DEFAULT_NUMBER_OF_SAMPLES*2, count - measurementsCount); // Display result on latency tester display. LatencyTestDisplay ltd(1, (int)finalResult); Device->SetDisplay(ltd); } void LatencyTest::updateForTimeouts() { if (!HaveOldTime) { HaveOldTime = true; OldTime = Timer::GetTicksMs(); return; } UInt32 newTime = Timer::GetTicksMs(); UInt32 elapsedMilliS = newTime - OldTime; if (newTime < OldTime) { elapsedMilliS = OldTime - newTime; elapsedMilliS = UINT_MAX - elapsedMilliS; } OldTime = newTime; elapsedMilliS = Alg::Min(elapsedMilliS, (UInt32) 100); // Clamp at 100mS in case we're not being called very often. if (ActiveTimerMilliS == 0) { return; } if (elapsedMilliS >= ActiveTimerMilliS) { ActiveTimerMilliS = 0; handleMessage(Message(), LatencyTest_Timer); return; } ActiveTimerMilliS -= elapsedMilliS; } }} // namespace OVR::Util \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Util/Util_LatencyTest.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Util/Util_LatencyTest.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : Util_LatencyTest.h Content : Wraps the lower level LatencyTesterDevice and adds functionality. Created : February 14, 2013 Authors : Lee Cooper Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #ifndef OVR_Util_LatencyTest_h #define OVR_Util_LatencyTest_h #include "../OVR_Device.h" #include "../Kernel/OVR_String.h" #include "../Kernel/OVR_List.h" namespace OVR { namespace Util { //------------------------------------------------------------------------------------- // ***** LatencyTest // // LatencyTest utility class wraps the low level LatencyTestDevice and manages the scheduling // of a latency test. A single test is composed of a series of individual latency measurements // which are used to derive min, max, and an average latency value. // // Developers are required to call the following methods: // SetDevice - Sets the LatencyTestDevice to be used for the tests. // ProcessInputs - This should be called at the same place in the code where the game engine // reads the headset orientation from LibOVR (typically done by calling // 'GetOrientation' on the SensorFusion object). Calling this at the right time // enables us to measure the same latency that occurs for headset orientation // changes. // DisplayScreenColor - The latency tester works by sensing the color of the pixels directly // beneath it. The color of these pixels can be set by drawing a small // quad at the end of the rendering stage. The quad should be small // such that it doesn't significantly impact the rendering of the scene, // but large enough to be 'seen' by the sensor. See the SDK // documentation for more information. // GetResultsString - Call this to get a string containing the most recent results. // If the string has already been gotten then NULL will be returned. // The string pointer will remain valid until the next time this // method is called. // class LatencyTest : public NewOverrideBase { public: LatencyTest(LatencyTestDevice* device = NULL); ~LatencyTest(); // Set the Latency Tester device that we'll use to send commands to and receive // notification messages from. bool SetDevice(LatencyTestDevice* device); // Returns true if this LatencyTestUtil has a Latency Tester device. bool HasDevice() const { return Handler.IsHandlerInstalled(); } void ProcessInputs(); bool DisplayScreenColor(Color& colorToDisplay); const char* GetResultsString(); // Begin test. Equivalent to pressing the button on the latency tester. void BeginTest(); private: LatencyTest* getThis() { return this; } enum LatencyTestMessageType { LatencyTest_None, LatencyTest_Timer, LatencyTest_ProcessInputs, }; UInt32 getRandomComponent(UInt32 range); void handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage = LatencyTest_None); void reset(); void setTimer(UInt32 timeMilliS); void clearTimer(); class LatencyTestHandler : public MessageHandler { LatencyTest* pLatencyTestUtil; public: LatencyTestHandler(LatencyTest* latencyTester) : pLatencyTestUtil(latencyTester) { } ~LatencyTestHandler(); virtual void OnMessage(const Message& msg); }; bool areResultsComplete(); void processResults(); void updateForTimeouts(); Ptr Device; LatencyTestHandler Handler; enum TesterState { State_WaitingForButton, State_WaitingForSettlePreCalibrationColorBlack, State_WaitingForSettlePostCalibrationColorBlack, State_WaitingForSettlePreCalibrationColorWhite, State_WaitingForSettlePostCalibrationColorWhite, State_WaitingToTakeMeasurement, State_WaitingForTestStarted, State_WaitingForColorDetected, State_WaitingForSettlePostMeasurement }; TesterState State; bool HaveOldTime; UInt32 OldTime; UInt32 ActiveTimerMilliS; Color RenderColor; struct MeasurementResult : public ListNode, public NewOverrideBase { MeasurementResult() : DeviceMeasuredElapsedMilliS(0), TimedOutWaitingForTestStarted(false), TimedOutWaitingForColorDetected(false), StartTestTicksMicroS(0), TestStartedTicksMicroS(0) {} Color TargetColor; UInt32 DeviceMeasuredElapsedMilliS; bool TimedOutWaitingForTestStarted; bool TimedOutWaitingForColorDetected; UInt64 StartTestTicksMicroS; UInt64 TestStartedTicksMicroS; }; List Results; void clearMeasurementResults(); MeasurementResult* getActiveResult(); StringBuffer ResultsString; String ReturnedResultString; }; }} // namespace OVR::Util #endif // OVR_Util_LatencyTest_h \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Util/Util_MagCalibration.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Util/Util_MagCalibration.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : Util_MagCalibration.cpp Content : Procedures for calibrating the magnetometer Created : April 16, 2013 Authors : Steve LaValle, Andrew Reisse Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #include "Util_MagCalibration.h" namespace OVR { namespace Util { void MagCalibration::BeginAutoCalibration(SensorFusion& sf) { Stat = Mag_AutoCalibrating; // This is a "hard" reset of the mag, so need to clear stored values sf.ClearMagCalibration(); SampleCount = 0; // reset the statistics MinMagValues = Vector3f(10000.0f,10000.0f,10000.0f); MaxMagValues = Vector3f(-10000.0f,-10000.0f,-10000.0f); MinQuatValues = Quatf(1.0f,1.0f,1.0f,1.0f); MaxQuatValues = Quatf(0.0f,0.0f,0.0f,0.0f); } unsigned MagCalibration::UpdateAutoCalibration(SensorFusion& sf) { if (Stat != Mag_AutoCalibrating) return Stat; Quatf q = sf.GetOrientation(); Vector3f m = sf.GetMagnetometer(); InsertIfAcceptable(q, m); if ((SampleCount == 4) && (Stat == Mag_AutoCalibrating)) { //LogText("Magnetometer Output Spread: %f %f %f\n",MagSpread.x,MagSpread.y,MagSpread.z); //LogText("Quaternion Spread: %f %f %f %f\n",QuatSpread.x,QuatSpread.y,QuatSpread.z,QuatSpread.w); SetCalibration(sf); } return Stat; } void MagCalibration::BeginManualCalibration(SensorFusion& sf) { Stat = Mag_ManuallyCalibrating; sf.ClearMagCalibration(); SampleCount = 0; } bool MagCalibration::IsAcceptableSample(const Quatf& q, const Vector3f& m) { switch (SampleCount) { // Initial sample is always acceptable case 0: return true; break; case 1: return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& ((m - MagSamples[0]).LengthSq() > MinMagDistanceSq); break; case 2: return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& (q.DistanceSq(QuatSamples[1]) > MinQuatDistanceSq)&& ((m - MagSamples[0]).LengthSq() > MinMagDistanceSq)&& ((m - MagSamples[1]).LengthSq() > MinMagDistanceSq); break; case 3: return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& (q.DistanceSq(QuatSamples[1]) > MinQuatDistanceSq)&& (q.DistanceSq(QuatSamples[2]) > MinQuatDistanceSq)&& ((PointToPlaneDistance(MagSamples[0],MagSamples[1],MagSamples[2],m) > MinMagDistance)|| (PointToPlaneDistance(MagSamples[1],MagSamples[2],m,MagSamples[0]) > MinMagDistance)|| (PointToPlaneDistance(MagSamples[2],m,MagSamples[0],MagSamples[1]) > MinMagDistance)|| (PointToPlaneDistance(m,MagSamples[0],MagSamples[1],MagSamples[2]) > MinMagDistance)); } return false; } bool MagCalibration::InsertIfAcceptable(const Quatf& q, const Vector3f& m) { // Update some statistics if (m.x < MinMagValues.x) MinMagValues.x = m.x; if (m.y < MinMagValues.y) MinMagValues.y = m.y; if (m.z < MinMagValues.z) MinMagValues.z = m.z; if (m.x > MaxMagValues.x) MaxMagValues.x = m.x; if (m.y > MaxMagValues.y) MaxMagValues.y = m.y; if (m.z > MaxMagValues.z) MaxMagValues.z = m.z; if (q.x < MinQuatValues.x) MinQuatValues.x = q.x; if (q.y < MinQuatValues.y) MinQuatValues.y = q.y; if (q.z < MinQuatValues.z) MinQuatValues.z = q.z; if (q.w < MinQuatValues.w) MinQuatValues.w = q.w; if (q.x > MaxQuatValues.x) MaxQuatValues.x = q.x; if (q.y > MaxQuatValues.y) MaxQuatValues.y = q.y; if (q.z > MaxQuatValues.z) MaxQuatValues.z = q.z; if (q.w > MaxQuatValues.w) MaxQuatValues.w = q.w; MagSpread = MaxMagValues - MinMagValues; QuatSpread = MaxQuatValues - MinQuatValues; if (IsAcceptableSample(q, m)) { MagSamples[SampleCount] = m; QuatSamples[SampleCount] = q; SampleCount++; return true; } return false; } Matrix4f MagCalibration::GetMagCalibration() const { Matrix4f calMat = Matrix4f(); calMat.M[0][3] = -MagCenter.x; calMat.M[1][3] = -MagCenter.y; calMat.M[2][3] = -MagCenter.z; return calMat; } bool MagCalibration::SetCalibration(SensorFusion& sf) { if (SampleCount < 4) return false; MagCenter = CalculateSphereCenter(MagSamples[0],MagSamples[1],MagSamples[2],MagSamples[3]); Matrix4f calMat = GetMagCalibration(); sf.SetMagCalibration(calMat); Stat = Mag_Calibrated; //LogText("MagCenter: %f %f %f\n",MagCenter.x,MagCenter.y,MagCenter.z); return true; } // Calculate the center of a sphere that passes through p1, p2, p3, p4 Vector3f MagCalibration::CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2, const Vector3f& p3, const Vector3f& p4) { Matrix4f A; int i; Vector3f p[4]; p[0] = p1; p[1] = p2; p[2] = p3; p[3] = p4; for (i = 0; i < 4; i++) { A.M[i][0] = p[i].x; A.M[i][1] = p[i].y; A.M[i][2] = p[i].z; A.M[i][3] = 1.0f; } float m11 = A.Determinant(); OVR_ASSERT(m11 != 0.0f); for (i = 0; i < 4; i++) { A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; A.M[i][1] = p[i].y; A.M[i][2] = p[i].z; A.M[i][3] = 1.0f; } float m12 = A.Determinant(); for (i = 0; i < 4; i++) { A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; A.M[i][1] = p[i].x; A.M[i][2] = p[i].z; A.M[i][3] = 1.0f; } float m13 = A.Determinant(); for (i = 0; i < 4; i++) { A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; A.M[i][1] = p[i].x; A.M[i][2] = p[i].y; A.M[i][3] = 1.0f; } float m14 = A.Determinant(); float c = 0.5f / m11; return Vector3f(c*m12, -c*m13, c*m14); } // Distance from p4 to the nearest point on a plane through p1, p2, p3 float MagCalibration::PointToPlaneDistance(const Vector3f& p1, const Vector3f& p2, const Vector3f& p3, const Vector3f& p4) { Vector3f v1 = p1 - p2; Vector3f v2 = p1 - p3; Vector3f planeNormal = v1.Cross(v2); planeNormal.Normalize(); return (fabs((planeNormal * p4) - planeNormal * p1)); } }} \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Util/Util_MagCalibration.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Util/Util_MagCalibration.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : Util_MagCalibration.h Content : Procedures for calibrating the magnetometer Created : April 16, 2013 Authors : Steve LaValle, Andrew Reisse Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #ifndef OVR_Util_MagCalibration_h #define OVR_Util_MagCalibration_h #include "../OVR_SensorFusion.h" #include "../Kernel/OVR_String.h" #include "../Kernel/OVR_Log.h" namespace OVR { namespace Util { class MagCalibration { public: enum MagStatus { Mag_Uninitialized = 0, Mag_AutoCalibrating = 1, Mag_ManuallyCalibrating = 2, Mag_Calibrated = 3 }; MagCalibration() : Stat(Mag_Uninitialized), MinMagDistance(0.2f), MinQuatDistance(0.5f), SampleCount(0) { MinMagDistanceSq = MinMagDistance * MinMagDistance; MinQuatDistanceSq = MinQuatDistance * MinQuatDistance; MinMagValues = Vector3f(10000.0f,10000.0f,10000.0f); MaxMagValues = Vector3f(-10000.0f,-10000.0f,-10000.0f); MinQuatValues = Quatf(1.0f,1.0f,1.0f,1.0f); MaxQuatValues = Quatf(0.0f,0.0f,0.0f,0.0f); } // Methods that are useful for either auto or manual calibration bool IsUnitialized() const { return Stat == Mag_Uninitialized; } bool IsCalibrated() const { return Stat == Mag_Calibrated; } int NumberOfSamples() const { return SampleCount; } int RequiredSampleCount() const { return 4; } void AbortCalibration() { Stat = Mag_Uninitialized; SampleCount = 0; } void ClearCalibration(SensorFusion& sf) { Stat = Mag_Uninitialized; SampleCount = 0; sf.ClearMagCalibration(); }; // Methods for automatic magnetometer calibration void BeginAutoCalibration(SensorFusion& sf); unsigned UpdateAutoCalibration(SensorFusion& sf); bool IsAutoCalibrating() const { return Stat == Mag_AutoCalibrating; } // Methods for building a manual (user-guided) calibraton procedure void BeginManualCalibration(SensorFusion& sf); bool IsAcceptableSample(const Quatf& q, const Vector3f& m); bool InsertIfAcceptable(const Quatf& q, const Vector3f& m); // Returns true if successful, requiring that SampleCount = 4 bool SetCalibration(SensorFusion& sf); bool IsManuallyCalibrating() const { return Stat == Mag_ManuallyCalibrating; } // This is the minimum acceptable distance (Euclidean) between raw // magnetometer values to be acceptable for usage in calibration. void SetMinMagDistance(float dist) { MinMagDistance = dist; MinMagDistanceSq = MinMagDistance * MinMagDistance; } // The minimum acceptable distance (4D Euclidean) between orientations // to be acceptable for calibration usage. void SetMinQuatDistance(float dist) { MinQuatDistance = dist; MinQuatDistanceSq = MinQuatDistance * MinQuatDistance; } // A result of the calibration, which is the center of a sphere that // roughly approximates the magnetometer data. Vector3f GetMagCenter() const { return MagCenter; } // Retrieves the full magnetometer calibration matrix Matrix4f GetMagCalibration() const; // Retrieves the range of each quaternion term during calibration Quatf GetCalibrationQuatSpread() const { return QuatSpread; } // Retrieves the range of each magnetometer term during calibration Vector3f GetCalibrationMagSpread() const { return MagSpread; } private: // Determine the unique sphere through 4 non-coplanar points Vector3f CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2, const Vector3f& p3, const Vector3f& p4); // Distance from p4 to the nearest point on a plane through p1, p2, p3 float PointToPlaneDistance(const Vector3f& p1, const Vector3f& p2, const Vector3f& p3, const Vector3f& p4); Vector3f MagCenter; unsigned Stat; float MinMagDistance; float MinQuatDistance; float MinMagDistanceSq; float MinQuatDistanceSq; // For gathering statistics during calibration Vector3f MinMagValues; Vector3f MaxMagValues; Vector3f MagSpread; Quatf MinQuatValues; Quatf MaxQuatValues; Quatf QuatSpread; unsigned SampleCount; Vector3f MagSamples[4]; Quatf QuatSamples[4]; }; }} #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Util/Util_Render_Stereo.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Util/Util_Render_Stereo.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : Util_Render_Stereo.cpp Content : Stereo rendering configuration implementation Created : October 22, 2012 Authors : Michael Antonov, Andrew Reisse Copyright : Copyright 2012 Oculus, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus Inc license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #include "Util_Render_Stereo.h" namespace OVR { namespace Util { namespace Render { //----------------------------------------------------------------------------------- // DistortionFnInverse computes the inverse of the distortion function on an argument. float DistortionConfig::DistortionFnInverse(float r) { OVR_ASSERT((r <= 10.0f)); float s, d; float delta = r * 0.25f; s = r * 0.5f; d = fabs(r - DistortionFn(s)); for (int i = 0; i < 20; i++) { float sUp = s + delta; float sDown = s - delta; float dUp = fabs(r - DistortionFn(sUp)); float dDown = fabs(r - DistortionFn(sDown)); if (dUp < d) { s = sUp; d = dUp; } else if (dDown < d) { s = sDown; d = dDown; } else { delta *= 0.5f; } } return s; } //----------------------------------------------------------------------------------- // **** StereoConfig Implementation StereoConfig::StereoConfig(StereoMode mode, const Viewport& vp) : Mode(mode), InterpupillaryDistance(0.064f), AspectMultiplier(1.0f), FullView(vp), DirtyFlag(true), IPDOverride(false), YFov(0), Aspect(vp.w / float(vp.h)), ProjectionCenterOffset(0), OrthoPixelOffset(0) { // And default distortion for it. Distortion.SetCoefficients(1.0f, 0.22f, 0.24f); Distortion.Scale = 1.0f; // Will be computed later. // Fit left of the image. DistortionFitX = -1.0f; DistortionFitY = 0.0f; // Initialize "fake" default HMD values for testing without HMD plugged in. // These default values match those returned by the HMD. HMD.HResolution = 1280; HMD.VResolution = 800; HMD.HScreenSize = 0.14976f; HMD.VScreenSize = HMD.HScreenSize / (1280.0f / 800.0f); HMD.InterpupillaryDistance = InterpupillaryDistance; HMD.LensSeparationDistance = 0.0635f; HMD.EyeToScreenDistance = 0.041f; HMD.DistortionK[0] = Distortion.K[0]; HMD.DistortionK[1] = Distortion.K[1]; HMD.DistortionK[2] = Distortion.K[2]; HMD.DistortionK[3] = 0; Set2DAreaFov(DegreeToRad(85.0f)); } void StereoConfig::SetFullViewport(const Viewport& vp) { if (vp != FullView) { FullView = vp; DirtyFlag = true; } } void StereoConfig::SetHMDInfo(const HMDInfo& hmd) { HMD = hmd; Distortion.K[0] = hmd.DistortionK[0]; Distortion.K[1] = hmd.DistortionK[1]; Distortion.K[2] = hmd.DistortionK[2]; Distortion.K[3] = hmd.DistortionK[3]; Distortion.SetChromaticAberration(hmd.ChromaAbCorrection[0], hmd.ChromaAbCorrection[1], hmd.ChromaAbCorrection[2], hmd.ChromaAbCorrection[3]); if (!IPDOverride) InterpupillaryDistance = HMD.InterpupillaryDistance; DirtyFlag = true; } void StereoConfig::SetDistortionFitPointVP(float x, float y) { DistortionFitX = x; DistortionFitY = y; DirtyFlag = true; } void StereoConfig::SetDistortionFitPointPixels(float x, float y) { DistortionFitX = (4 * x / float(FullView.w)) - 1.0f; DistortionFitY = (2 * y / float(FullView.h)) - 1.0f; DirtyFlag = true; } void StereoConfig::Set2DAreaFov(float fovRadians) { Area2DFov = fovRadians; DirtyFlag = true; } const StereoEyeParams& StereoConfig::GetEyeRenderParams(StereoEye eye) { static const UByte eyeParamIndices[3] = { 0, 0, 1 }; updateIfDirty(); OVR_ASSERT(eye < sizeof(eyeParamIndices)); return EyeRenderParams[eyeParamIndices[eye]]; } void StereoConfig::updateComputedState() { // Need to compute all of the following: // - Aspect Ratio // - FOV // - Projection offsets for 3D // - Distortion XCenterOffset // - Update 2D // - Initialize EyeRenderParams // Compute aspect ratio. Stereo mode cuts width in half. Aspect = float(FullView.w) / float(FullView.h); Aspect *= (Mode == Stereo_None) ? 1.0f : 0.5f; Aspect *= AspectMultiplier; updateDistortionOffsetAndScale(); // Compute Vertical FOV based on distance, distortion, etc. // Distance from vertical center to render vertical edge perceived through the lens. // This will be larger then normal screen size due to magnification & distortion. // // This percievedHalfRTDistance equation should hold as long as the render target // and display have the same aspect ratios. What we'd like to know is where the edge // of the render target will on the perceived screen surface. With NO LENS, // the answer would be: // // halfRTDistance = (VScreenSize / 2) * aspect * // DistortionFn_Inverse( DistortionScale / aspect ) // // To model the optical lens we eliminates DistortionFn_Inverse. Aspect ratios // cancel out, so we get: // // halfRTDistance = (VScreenSize / 2) * DistortionScale // if (Mode == Stereo_None) { YFov = DegreeToRad(80.0f); } else { float percievedHalfRTDistance = (HMD.VScreenSize / 2) * Distortion.Scale; YFov = 2.0f * atan(percievedHalfRTDistance/HMD.EyeToScreenDistance); } updateProjectionOffset(); update2D(); updateEyeParams(); DirtyFlag = false; } void StereoConfig::updateDistortionOffsetAndScale() { // Distortion center shift is stored separately, since it isn't affected // by the eye distance. float lensOffset = HMD.LensSeparationDistance * 0.5f; float lensShift = HMD.HScreenSize * 0.25f - lensOffset; float lensViewportShift = 4.0f * lensShift / HMD.HScreenSize; Distortion.XCenterOffset= lensViewportShift; // Compute distortion scale from DistortionFitX & DistortionFitY. // Fit value of 0.0 means "no fit". if ((fabs(DistortionFitX) < 0.0001f) && (fabs(DistortionFitY) < 0.0001f)) { Distortion.Scale = 1.0f; } else { // Convert fit value to distortion-centered coordinates before fit radius // calculation. float stereoAspect = 0.5f * float(FullView.w) / float(FullView.h); float dx = DistortionFitX - Distortion.XCenterOffset; float dy = DistortionFitY / stereoAspect; float fitRadius = sqrt(dx * dx + dy * dy); Distortion.Scale = Distortion.DistortionFn(fitRadius)/fitRadius; } } void StereoConfig::updateProjectionOffset() { // Post-projection viewport coordinates range from (-1.0, 1.0), with the // center of the left viewport falling at (1/4) of horizontal screen size. // We need to shift this projection center to match with the lens center; // note that we don't use the IPD here due to collimated light property of the lens. // We compute this shift in physical units (meters) to // correct for different screen sizes and then rescale to viewport coordinates. float viewCenter = HMD.HScreenSize * 0.25f; float eyeProjectionShift = viewCenter - HMD.LensSeparationDistance*0.5f; ProjectionCenterOffset = 4.0f * eyeProjectionShift / HMD.HScreenSize; } void StereoConfig::update2D() { // Orthographic projection fakes a screen at a distance of 0.8m from the // eye, where hmd screen projection surface is at 0.05m distance. // This introduces an extra off-center pixel projection shift based on eye distance. // This offCenterShift is the pixel offset of the other camera's center // in your reference camera based on surface distance. float metersToPixels = (HMD.HResolution / HMD.HScreenSize); float lensDistanceScreenPixels= metersToPixels * HMD.LensSeparationDistance; float eyeDistanceScreenPixels = metersToPixels * InterpupillaryDistance; float offCenterShiftPixels = (HMD.EyeToScreenDistance / 0.8f) * eyeDistanceScreenPixels; float leftPixelCenter = (HMD.HResolution / 2) - lensDistanceScreenPixels * 0.5f; float rightPixelCenter = lensDistanceScreenPixels * 0.5f; float pixelDifference = leftPixelCenter - rightPixelCenter; // This computes the number of pixels that fit within specified 2D FOV (assuming // distortion scaling will be done). float percievedHalfScreenDistance = tan(Area2DFov * 0.5f) * HMD.EyeToScreenDistance; float vfovSize = 2.0f * percievedHalfScreenDistance / Distortion.Scale; FovPixels = HMD.VResolution * vfovSize / HMD.VScreenSize; // Create orthographic matrix. Matrix4f& m = OrthoCenter; m.SetIdentity(); m.M[0][0] = FovPixels / (FullView.w * 0.5f); m.M[1][1] = -FovPixels / FullView.h; m.M[0][3] = 0; m.M[1][3] = 0; m.M[2][2] = 0; float orthoPixelOffset = (pixelDifference + offCenterShiftPixels/Distortion.Scale) * 0.5f; OrthoPixelOffset = orthoPixelOffset * 2.0f / FovPixels; } void StereoConfig::updateEyeParams() { // Projection matrix for the center eye, which the left/right matrices are based on. Matrix4f projCenter = Matrix4f::PerspectiveRH(YFov, Aspect, 0.01f, 2000.0f); switch(Mode) { case Stereo_None: { EyeRenderParams[0].Init(StereoEye_Center, FullView, 0, projCenter, OrthoCenter); } break; case Stereo_LeftRight_Multipass: { Matrix4f projLeft = Matrix4f::Translation(ProjectionCenterOffset, 0, 0) * projCenter, projRight = Matrix4f::Translation(-ProjectionCenterOffset, 0, 0) * projCenter; EyeRenderParams[0].Init(StereoEye_Left, Viewport(FullView.x, FullView.y, FullView.w/2, FullView.h), +InterpupillaryDistance * 0.5f, // World view shift. projLeft, OrthoCenter * Matrix4f::Translation(OrthoPixelOffset, 0, 0), &Distortion); EyeRenderParams[1].Init(StereoEye_Right, Viewport(FullView.x + FullView.w/2, FullView.y, FullView.w/2, FullView.h), -InterpupillaryDistance * 0.5f, projRight, OrthoCenter * Matrix4f::Translation(-OrthoPixelOffset, 0, 0), &Distortion); } break; } } }}} // OVR::Util::Render \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/Util/Util_Render_Stereo.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/Util/Util_Render_Stereo.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : Util_Render_Stereo.h Content : Sample stereo rendering configuration classes. Created : October 22, 2012 Authors : Michael Antonov Copyright : Copyright 2012 Oculus, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus Inc license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #ifndef OVR_Util_Render_Stereo_h #define OVR_Util_Render_Stereo_h #include "../OVR_Device.h" namespace OVR { namespace Util { namespace Render { //----------------------------------------------------------------------------------- // ***** Stereo Enumerations // StereoMode describes rendering modes that can be used by StereoConfig. // These modes control whether stereo rendering is used or not (Stereo_None), // and how it is implemented. enum StereoMode { Stereo_None = 0, Stereo_LeftRight_Multipass = 1 }; // StereoEye specifies which eye we are rendering for; it is used to // retrieve StereoEyeParams. enum StereoEye { StereoEye_Center, StereoEye_Left, StereoEye_Right }; //----------------------------------------------------------------------------------- // ***** Viewport // Viewport describes a rectangular area used for rendering, in pixels. struct Viewport { int x, y; int w, h; Viewport() {} Viewport(int x1, int y1, int w1, int h1) : x(x1), y(y1), w(w1), h(h1) { } bool operator == (const Viewport& vp) const { return (x == vp.x) && (y == vp.y) && (w == vp.w) && (h == vp.h); } bool operator != (const Viewport& vp) const { return !operator == (vp); } }; //----------------------------------------------------------------------------------- // ***** DistortionConfig // DistortionConfig Provides controls for the distortion shader. // - K[0] - K[3] are coefficients for the distortion function. // - XCenterOffset is the offset of lens distortion center from the // center of one-eye screen half. [-1, 1] Range. // - Scale is a factor of how much larger will the input image be, // with a factor of 1.0f being no scaling. An inverse of this // value is applied to sampled UV coordinates (1/Scale). // - ChromaticAberration is an array of parameters for controlling // additional Red and Blue scaling in order to reduce chromatic aberration // caused by the Rift lenses. class DistortionConfig { public: DistortionConfig(float k0 = 1.0f, float k1 = 0.0f, float k2 = 0.0f, float k3 = 0.0f) : XCenterOffset(0), YCenterOffset(0), Scale(1.0f) { SetCoefficients(k0, k1, k2, k3); SetChromaticAberration(); } void SetCoefficients(float k0, float k1 = 0.0f, float k2 = 0.0f, float k3 = 0.0f) { K[0] = k0; K[1] = k1; K[2] = k2; K[3] = k3; } void SetChromaticAberration(float red1 = 1.0f, float red2 = 0.0f, float blue1 = 1.0f, float blue2 = 0.0f) { ChromaticAberration[0] = red1; ChromaticAberration[1] = red2; ChromaticAberration[2] = blue1; ChromaticAberration[3] = blue2; } // DistortionFn applies distortion equation to the argument. The returned // value should match distortion equation used in shader. float DistortionFn(float r) const { float rsq = r * r; float scale = r * (K[0] + K[1] * rsq + K[2] * rsq * rsq + K[3] * rsq * rsq * rsq); return scale; } // DistortionFnInverse computes the inverse of the distortion function on an argument. float DistortionFnInverse(float r); float K[4]; float XCenterOffset, YCenterOffset; float Scale; float ChromaticAberration[4]; // Additional per-channel scaling is applied after distortion: // Index [0] - Red channel constant coefficient. // Index [1] - Red channel r^2 coefficient. // Index [2] - Blue channel constant coefficient. // Index [3] - Blue channel r^2 coefficient. }; //----------------------------------------------------------------------------------- // ***** StereoEyeParams // StereoEyeParams describes RenderDevice configuration needed to render // the scene for one eye. class StereoEyeParams { public: StereoEye Eye; Viewport VP; // Viewport that we are rendering to const DistortionConfig* pDistortion; Matrix4f ViewAdjust; // Translation to be applied to view matrix. Matrix4f Projection; // Projection matrix used with this eye. Matrix4f OrthoProjection; // Orthographic projection used with this eye. void Init(StereoEye eye, const Viewport &vp, float vofs, const Matrix4f& proj, const Matrix4f& orthoProj, const DistortionConfig* distortion = 0) { Eye = eye; VP = vp; ViewAdjust = Matrix4f::Translation(Vector3f(vofs,0,0)); Projection = proj; OrthoProjection = orthoProj; pDistortion = distortion; } }; //----------------------------------------------------------------------------------- // ***** StereoConfig // StereoConfig maintains a scene stereo state and allow switching between different // stereo rendering modes. To support rendering, StereoConfig keeps track of HMD // variables such as screen size, eye-to-screen distance and distortion, and computes // extra data such as FOV and distortion center offsets based on it. Rendering // parameters are returned though StereoEyeParams for each eye. // // Beyond regular 3D projection, this class supports rendering a 2D orthographic // surface for UI and text. The 2D surface will be defined as fitting within a 2D // field of view (85 degrees by default) and used [-1,1] coordinate system with // square pixels. The (0,0) coordinate corresponds to eye center location // that is properly adjusted during rendering through SterepRenderParams::Adjust2D. // Genreally speaking, text outside [-1,1] coordinate range will not be readable. class StereoConfig { public: StereoConfig(StereoMode mode = Stereo_LeftRight_Multipass, const Viewport& fullViewport = Viewport(0,0, 1280,800)); // *** Modifiable State Access // Sets a stereo rendering mode and updates internal cached // state (matrices, per-eye view) based on it. void SetStereoMode(StereoMode mode) { Mode = mode; DirtyFlag = true; } StereoMode GetStereoMode() const { return Mode; } // Sets HMD parameters; also initializes distortion coefficients. void SetHMDInfo(const HMDInfo& hmd); const HMDInfo& GetHMDInfo() const { return HMD; } // Query physical eye-to-screen distance in meters, which combines screen-to-lens and // and lens-to-eye pupil distances. Modifying this value adjusts FOV. float GetEyeToScreenDistance() const { return HMD.EyeToScreenDistance; } void SetEyeToScreenDistance(float esd) { HMD.EyeToScreenDistance = esd; DirtyFlag = true; } // Interpupillary distance used for stereo, in meters. Default is 0.064m (64 mm). void SetIPD(float ipd) { InterpupillaryDistance = ipd; IPDOverride = DirtyFlag = true; } float GetIPD() const { return InterpupillaryDistance; } // Set full render target viewport; for HMD this includes both eyes. void SetFullViewport(const Viewport& vp); const Viewport& GetFullViewport() const { return FullView; } // Aspect ratio defaults to ((w/h)*multiplier) computed per eye. // Aspect multiplier allows adjusting aspect ratio consistently for Stereo/NoStereo. void SetAspectMultiplier(float m) { AspectMultiplier = m; DirtyFlag = true; } float GetAspectMultiplier() const { return AspectMultiplier; } // For the distorted image to fill rendered viewport, input texture render target needs to be // scaled by DistortionScale before sampling. The scale factor is computed by fitting a point // on of specified radius from a distortion center, more easily specified as a coordinate. // SetDistortionFitPointVP sets the (x,y) coordinate of the point that scale will be "fit" to, // assuming [-1,1] coordinate range for full left-eye viewport. A fit point is a location // where source (pre-distortion) and target (post-distortion) image match each other. // For the right eye, the interpretation of 'u' will be inverted. void SetDistortionFitPointVP(float x, float y); // SetDistortionFitPointPixels sets the (x,y) coordinate of the point that scale will be "fit" to, // specified in pixeld for full left-eye texture. void SetDistortionFitPointPixels(float x, float y); // Changes all distortion settings. // Note that setting HMDInfo also changes Distortion coefficients. void SetDistortionConfig(const DistortionConfig& d) { Distortion = d; DirtyFlag = true; } // Modify distortion coefficients; useful for adjustment tweaking. void SetDistortionK(int i, float k) { Distortion.K[i] = k; DirtyFlag = true; } float GetDistortionK(int i) const { return Distortion.K[i]; } // Sets the fieldOfView that the 2D coordinate area stretches to. void Set2DAreaFov(float fovRadians); // *** Computed State // Return current aspect ratio. float GetAspect() { updateIfDirty(); return Aspect; } // Return computed vertical FOV in radians/degrees. float GetYFOVRadians() { updateIfDirty(); return YFov; } float GetYFOVDegrees() { return RadToDegree(GetYFOVRadians()); } // Query horizontal projection center offset as a distance away from the // one-eye [-1,1] unit viewport. // Positive return value should be used for left eye, negative for right eye. float GetProjectionCenterOffset() { updateIfDirty(); return ProjectionCenterOffset; } // GetDistortionConfig isn't const because XCenterOffset bay need to be recomputed. const DistortionConfig& GetDistortionConfig() { updateIfDirty(); return Distortion; } // Returns DistortionScale factor by which input texture size is increased to make // post-distortion result distortion fit the viewport. float GetDistortionScale() { updateIfDirty(); return Distortion.Scale; } // Returns the size of a pixel within 2D coordinate system. float Get2DUnitPixel() { updateIfDirty(); return (2.0f / (FovPixels * Distortion.Scale)); } // Returns full set of Stereo rendering parameters for the specified eye. const StereoEyeParams& GetEyeRenderParams(StereoEye eye); private: void updateIfDirty() { if (DirtyFlag) updateComputedState(); } void updateComputedState(); void updateDistortionOffsetAndScale(); void updateProjectionOffset(); void update2D(); void updateEyeParams(); // *** Modifiable State StereoMode Mode; float InterpupillaryDistance; float AspectMultiplier; // Multiplied into aspect ratio to change it. HMDInfo HMD; DistortionConfig Distortion; float DistortionFitX, DistortionFitY; // In [-1,1] half-screen viewport units. Viewport FullView; // Entire window viewport. float Area2DFov; // FOV range mapping to [-1, 1] 2D area. // *** Computed State bool DirtyFlag; // Set when any if the modifiable state changed. bool IPDOverride; // True after SetIPD was called. float YFov; // Vertical FOV. float Aspect; // Aspect ratio: (w/h)*AspectMultiplier. float ProjectionCenterOffset; StereoEyeParams EyeRenderParams[2]; // ** 2D Rendering // Number of 2D pixels in the FOV. This defines [-1,1] coordinate range for 2D. float FovPixels; Matrix4f OrthoCenter; float OrthoPixelOffset; }; }}} // OVR::Util::Render #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/linux/OVR_Linux_DeviceManager.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/linux/OVR_Linux_DeviceManager.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,319 @@ +/************************************************************************************ + +Filename : OVR_Linux_DeviceManager.h +Content : Linux implementation of DeviceManager. +Created : +Authors : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Linux_DeviceManager.h" + +// Sensor & HMD Factories +#include "OVR_LatencyTestImpl.h" +#include "OVR_SensorImpl.h" +#include "OVR_Linux_HIDDevice.h" +#include "OVR_Linux_HMDDevice.h" + +#include "Kernel/OVR_Timer.h" +#include "Kernel/OVR_Std.h" +#include "Kernel/OVR_Log.h" + +namespace OVR { namespace Linux { + + +//------------------------------------------------------------------------------------- +// **** Linux::DeviceManager + +DeviceManager::DeviceManager() +{ +} + +DeviceManager::~DeviceManager() +{ +} + +bool DeviceManager::Initialize(DeviceBase*) +{ + if (!DeviceManagerImpl::Initialize(0)) + return false; + + pThread = *new DeviceManagerThread(); + if (!pThread || !pThread->Start()) + return false; + + // Wait for the thread to be fully up and running. + pThread->StartupEvent.Wait(); + + // Do this now that we know the thread's run loop. + HidDeviceManager = *HIDDeviceManager::CreateInternal(this); + + pCreateDesc->pDevice = this; + LogText("OVR::DeviceManager - initialized.\n"); + return true; +} + +void DeviceManager::Shutdown() +{ + LogText("OVR::DeviceManager - shutting down.\n"); + + // Set Manager shutdown marker variable; this prevents + // any existing DeviceHandle objects from accessing device. + pCreateDesc->pLock->pManager = 0; + + // Push for thread shutdown *WITH NO WAIT*. + // This will have the following effect: + // - Exit command will get enqueued, which will be executed later on the thread itself. + // - Beyond this point, this DeviceManager object may be deleted by our caller. + // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will + // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued + // after pManager is null. + // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last + // reference to the thread object. + pThread->PushExitCommand(false); + pThread.Clear(); + + DeviceManagerImpl::Shutdown(); +} + +ThreadCommandQueue* DeviceManager::GetThreadQueue() +{ + return pThread; +} + +ThreadId DeviceManager::GetThreadId() const +{ + return pThread->GetThreadId(); +} + +bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_Manager) && + (info->InfoClassType != Device_None)) + return false; + + info->Type = Device_Manager; + info->Version = 0; + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager"); + OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc."); + return true; +} + +DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args) +{ + // TBD: Can this be avoided in the future, once proper device notification is in place? + pThread->PushCall((DeviceManagerImpl*)this, + &DeviceManager::EnumerateAllFactoryDevices, true); + + return DeviceManagerImpl::EnumerateDevicesEx(args); +} + + +//------------------------------------------------------------------------------------- +// ***** DeviceManager Thread + +DeviceManagerThread::DeviceManagerThread() + : Thread(ThreadStackSize) +{ + int result = pipe(CommandFd); + OVR_ASSERT(!result); + + AddSelectFd(NULL, CommandFd[0]); +} + +DeviceManagerThread::~DeviceManagerThread() +{ + if (CommandFd[0]) + { + RemoveSelectFd(NULL, CommandFd[0]); + close(CommandFd[0]); + close(CommandFd[1]); + } +} + +bool DeviceManagerThread::AddSelectFd(Notifier* notify, int fd) +{ + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLIN|POLLHUP|POLLERR; + pfd.revents = 0; + + FdNotifiers.PushBack(notify); + PollFds.PushBack(pfd); + + OVR_ASSERT(FdNotifiers.GetSize() == PollFds.GetSize()); + return true; +} + +bool DeviceManagerThread::RemoveSelectFd(Notifier* notify, int fd) +{ + // [0] is reserved for thread commands with notify of null, but we still + // can use this function to remove it. + for (UPInt i = 0; i < FdNotifiers.GetSize(); i++) + { + if ((FdNotifiers[i] == notify) && (PollFds[i].fd == fd)) + { + FdNotifiers.RemoveAt(i); + PollFds.RemoveAt(i); + return true; + } + } + return false; +} + + + +int DeviceManagerThread::Run() +{ + ThreadCommand::PopBuffer command; + + SetThreadName("OVR::DeviceManagerThread"); + LogText("OVR::DeviceManagerThread - running (ThreadId=%p).\n", GetThreadId()); + + // Signal to the parent thread that initialization has finished. + StartupEvent.SetEvent(); + + while(!IsExiting()) + { + // PopCommand will reset event on empty queue. + if (PopCommand(&command)) + { + command.Execute(); + } + else + { + bool commands = 0; + do + { + int waitMs = -1; + + // If devices have time-dependent logic registered, get the longest wait + // allowed based on current ticks. + if (!TicksNotifiers.IsEmpty()) + { + UInt64 ticksMks = Timer::GetTicks(); + int waitAllowed; + + for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++) + { + waitAllowed = (int)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs); + if (waitAllowed < waitMs) + waitMs = waitAllowed; + } + } + + // wait until there is data available on one of the devices or the timeout expires + int n = poll(&PollFds[0], PollFds.GetSize(), waitMs); + + if (n > 0) + { + // Iterate backwards through the list so the ordering will not be + // affected if the called object gets removed during the callback + // Also, the HID data streams are located toward the back of the list + // and servicing them first will allow a disconnect to be handled + // and cleaned directly at the device first instead of the general HID monitor + for (int i=PollFds.GetSize()-1; i>=0; i--) + { + if (PollFds[i].revents & POLLERR) + { + OVR_DEBUG_LOG(("poll: error on [%d]: %d", i, PollFds[i].fd)); + } + else if (PollFds[i].revents & POLLIN) + { + if (FdNotifiers[i]) + FdNotifiers[i]->OnEvent(i, PollFds[i].fd); + else if (i == 0) // command + { + char dummy[128]; + read(PollFds[i].fd, dummy, 128); + commands = 1; + } + } + + if (PollFds[i].revents & POLLHUP) + PollFds[i].events = 0; + + if (PollFds[i].revents != 0) + { + n--; + if (n == 0) + break; + } + } + } + } while (PollFds.GetSize() > 0 && !commands); + } + } + + LogText("OVR::DeviceManagerThread - exiting (ThreadId=%p).\n", GetThreadId()); + return 0; +} + +bool DeviceManagerThread::AddTicksNotifier(Notifier* notify) +{ + TicksNotifiers.PushBack(notify); + return true; +} + +bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify) +{ + for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++) + { + if (TicksNotifiers[i] == notify) + { + TicksNotifiers.RemoveAt(i); + return true; + } + } + return false; +} + +} // namespace Linux + + +//------------------------------------------------------------------------------------- +// ***** Creation + + +// Creates a new DeviceManager and initializes OVR. +DeviceManager* DeviceManager::Create() +{ + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new Linux::DeviceManager; + + if (manager) + { + if (manager->Initialize(0)) + { + manager->AddFactory(&LatencyTestDeviceFactory::Instance); + manager->AddFactory(&SensorDeviceFactory::Instance); + manager->AddFactory(&Linux::HMDDeviceFactory::Instance); + + manager->AddRef(); + } + else + { + manager.Clear(); + } + + } + + return manager.GetPtr(); +} + + +} // namespace OVR + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/linux/OVR_Linux_DeviceManager.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/linux/OVR_Linux_DeviceManager.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,111 @@ +/************************************************************************************ + +Filename : OVR_Linux_DeviceManager.h +Content : Linux-specific DeviceManager header. +Created : +Authors : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Linux_DeviceManager_h +#define OVR_Linux_DeviceManager_h + +#include "OVR_DeviceImpl.h" + +#include +#include + + +namespace OVR { namespace Linux { + +class DeviceManagerThread; + +//------------------------------------------------------------------------------------- +// ***** Linux DeviceManager + +class DeviceManager : public DeviceManagerImpl +{ +public: + DeviceManager(); + ~DeviceManager(); + + // Initialize/Shutdowncreate and shutdown manger thread. + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + virtual ThreadCommandQueue* GetThreadQueue(); + virtual ThreadId GetThreadId() const; + + virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args); + + virtual bool GetDeviceInfo(DeviceInfo* info) const; + + Ptr pThread; +}; + +//------------------------------------------------------------------------------------- +// ***** Device Manager Background Thread + +class DeviceManagerThread : public Thread, public ThreadCommandQueue +{ + friend class DeviceManager; + enum { ThreadStackSize = 64 * 1024 }; +public: + DeviceManagerThread(); + ~DeviceManagerThread(); + + virtual int Run(); + + // ThreadCommandQueue notifications for CommandEvent handling. + virtual void OnPushNonEmpty_Locked() { write(CommandFd[1], this, 1); } + virtual void OnPopEmpty_Locked() { } + + class Notifier + { + public: + // Called when I/O is received + virtual void OnEvent(int i, int fd) = 0; + + // Called when timing ticks are updated. + // Returns the largest number of microseconds this function can + // wait till next call. + virtual UInt64 OnTicks(UInt64 ticksMks) + { + OVR_UNUSED1(ticksMks); + return Timer::MksPerSecond * 1000; + } + }; + + // Add I/O notifier + bool AddSelectFd(Notifier* notify, int fd); + bool RemoveSelectFd(Notifier* notify, int fd); + + // Add notifier that will be called at regular intervals. + bool AddTicksNotifier(Notifier* notify); + bool RemoveTicksNotifier(Notifier* notify); + +private: + + bool threadInitialized() { return CommandFd[0] != 0; } + + // pipe used to signal commands + int CommandFd[2]; + + Array PollFds; + Array FdNotifiers; + + Event StartupEvent; + + // Ticks notifiers - used for time-dependent events such as keep-alive. + Array TicksNotifiers; +}; + +}} // namespace Linux::OVR + +#endif // OVR_Linux_DeviceManager_h diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/linux/OVR_Linux_HIDDevice.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/linux/OVR_Linux_HIDDevice.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,799 @@ +/************************************************************************************ +Filename : OVR_Linux_HIDDevice.cpp +Content : Linux HID device implementation. +Created : February 26, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Linux_HIDDevice.h" + +#include +#include +#include +#include +#include "OVR_HIDDeviceImpl.h" + +namespace OVR { namespace Linux { + +static const UInt32 MAX_QUEUED_INPUT_REPORTS = 5; + +//------------------------------------------------------------------------------------- +// **** Linux::DeviceManager +//----------------------------------------------------------------------------- +HIDDeviceManager::HIDDeviceManager(DeviceManager* manager) : DevManager(manager) +{ + UdevInstance = NULL; + HIDMonitor = NULL; + HIDMonHandle = -1; +} + +//----------------------------------------------------------------------------- +HIDDeviceManager::~HIDDeviceManager() +{ +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::initializeManager() +{ + if (HIDMonitor) + { + return true; + } + + // Create a udev_monitor handle to watch for device changes (hot-plug detection) + HIDMonitor = udev_monitor_new_from_netlink(UdevInstance, "udev"); + if (HIDMonitor == NULL) + { + return false; + } + + udev_monitor_filter_add_match_subsystem_devtype(HIDMonitor, "hidraw", NULL); // filter for hidraw only + + int err = udev_monitor_enable_receiving(HIDMonitor); + if (err) + { + udev_monitor_unref(HIDMonitor); + HIDMonitor = NULL; + return false; + } + + // Get the file descriptor (fd) for the monitor. + HIDMonHandle = udev_monitor_get_fd(HIDMonitor); + if (HIDMonHandle < 0) + { + udev_monitor_unref(HIDMonitor); + HIDMonitor = NULL; + return false; + } + + // This file handle will be polled along-side with the device hid handles for changes + // Add the handle to the polling list + if (!DevManager->pThread->AddSelectFd(this, HIDMonHandle)) + { + close(HIDMonHandle); + HIDMonHandle = -1; + + udev_monitor_unref(HIDMonitor); + HIDMonitor = NULL; + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::Initialize() +{ + // Get a udev library handle. This handle must stay active during the + // duration the lifetime of device monitoring handles + UdevInstance = udev_new(); + if (!UdevInstance) + return false; + + return initializeManager(); +} + +//----------------------------------------------------------------------------- +void HIDDeviceManager::Shutdown() +{ + OVR_ASSERT_LOG((UdevInstance), ("Should have called 'Initialize' before 'Shutdown'.")); + + if (HIDMonitor) + { + DevManager->pThread->RemoveSelectFd(this, HIDMonHandle); + close(HIDMonHandle); + HIDMonHandle = -1; + + udev_monitor_unref(HIDMonitor); + HIDMonitor = NULL; + } + + udev_unref(UdevInstance); // release the library + + LogText("OVR::Linux::HIDDeviceManager - shutting down.\n"); +} + +//------------------------------------------------------------------------------- +bool HIDDeviceManager::AddNotificationDevice(HIDDevice* device) +{ + NotificationDevices.PushBack(device); + return true; +} + +//------------------------------------------------------------------------------- +bool HIDDeviceManager::RemoveNotificationDevice(HIDDevice* device) +{ + for (UPInt i = 0; i < NotificationDevices.GetSize(); i++) + { + if (NotificationDevices[i] == device) + { + NotificationDevices.RemoveAt(i); + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::getIntProperty(udev_device* device, + const char* propertyName, + SInt32* pResult) +{ + const char* str = udev_device_get_sysattr_value(device, propertyName); + if (str) + { + *pResult = strtol(str, NULL, 16); + return true; + } + else + { + *pResult = 0; + return true; + } +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::initVendorProductVersion(udev_device* device, HIDDeviceDesc* pDevDesc) +{ + SInt32 result; + if (getIntProperty(device, "idVendor", &result)) + pDevDesc->VendorId = result; + else + return false; + + if (getIntProperty(device, "idProduct", &result)) + pDevDesc->ProductId = result; + else + return false; + + return true; +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::getStringProperty(udev_device* device, + const char* propertyName, + OVR::String* pResult) +{ + // Get the attribute in UTF8 + const char* str = udev_device_get_sysattr_value(device, propertyName); + if (str) + { // Copy the string into the return value + *pResult = String(str); + return true; + } + else + { + return false; + } +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor) +{ + + if (!initializeManager()) + { + return false; + } + + // Get a list of hid devices + udev_enumerate* devices = udev_enumerate_new(UdevInstance); + udev_enumerate_add_match_subsystem(devices, "hidraw"); + udev_enumerate_scan_devices(devices); + + udev_list_entry* entry = udev_enumerate_get_list_entry(devices); + + // Search each device for the matching vid/pid + while (entry != NULL) + { + // Get the device file name + const char* sysfs_path = udev_list_entry_get_name(entry); + udev_device* hid; // The device's HID udev node. + hid = udev_device_new_from_syspath(UdevInstance, sysfs_path); + const char* dev_path = udev_device_get_devnode(hid); + + // Get the USB device + hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device"); + if (hid) + { + HIDDeviceDesc devDesc; + + // Check the VID/PID for a match + if (dev_path && + initVendorProductVersion(hid, &devDesc) && + enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId)) + { + devDesc.Path = dev_path; + getFullDesc(hid, &devDesc); + + // Look for the device to check if it is already opened. + Ptr existingDevice = DevManager->FindHIDDevice(devDesc); + // if device exists and it is opened then most likely the device open() + // will fail; therefore, we just set Enumerated to 'true' and continue. + if (existingDevice && existingDevice->pDevice) + { + existingDevice->Enumerated = true; + } + else + { // open the device temporarily for startup communication + int device_handle = open(dev_path, O_RDWR); + if (device_handle >= 0) + { + // Construct minimal device that the visitor callback can get feature reports from + Linux::HIDDevice device(this, device_handle); + enumVisitor->Visit(device, devDesc); + + close(device_handle); // close the file handle + } + } + } + + udev_device_unref(hid); + entry = udev_list_entry_get_next(entry); + } + } + + // Free the enumerator and udev objects + udev_enumerate_unref(devices); + + return true; +} + +//----------------------------------------------------------------------------- +OVR::HIDDevice* HIDDeviceManager::Open(const String& path) +{ + Ptr device = *new Linux::HIDDevice(this); + + if (device->HIDInitialize(path)) + { + device->AddRef(); + return device; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::getFullDesc(udev_device* device, HIDDeviceDesc* desc) +{ + + if (!initVendorProductVersion(device, desc)) + { + return false; + } + + if (!getStringProperty(device, "serial", &(desc->SerialNumber))) + { + return false; + } + + getStringProperty(device, "manufacturer", &(desc->Manufacturer)); + getStringProperty(device, "product", &(desc->Product)); + + return true; +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::GetDescriptorFromPath(const char* dev_path, HIDDeviceDesc* desc) +{ + if (!initializeManager()) + { + return false; + } + + // Search for the udev device from the given pathname so we can + // have a handle to query device properties + + udev_enumerate* devices = udev_enumerate_new(UdevInstance); + udev_enumerate_add_match_subsystem(devices, "hidraw"); + udev_enumerate_scan_devices(devices); + + udev_list_entry* entry = udev_enumerate_get_list_entry(devices); + + bool success = false; + // Search for the device with the matching path + while (entry != NULL) + { + // Get the device file name + const char* sysfs_path = udev_list_entry_get_name(entry); + udev_device* hid; // The device's HID udev node. + hid = udev_device_new_from_syspath(UdevInstance, sysfs_path); + const char* path = udev_device_get_devnode(hid); + + if (OVR_strcmp(dev_path, path) == 0) + { // Found the device so lets collect the device descriptor + + // Get the USB device + hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device"); + if (hid) + { + desc->Path = dev_path; + success = getFullDesc(hid, desc); + } + + } + + udev_device_unref(hid); + entry = udev_list_entry_get_next(entry); + } + + // Free the enumerator + udev_enumerate_unref(devices); + + return success; +} + +//----------------------------------------------------------------------------- +void HIDDeviceManager::OnEvent(int i, int fd) +{ + // There is a device status change + udev_device* hid = udev_monitor_receive_device(HIDMonitor); + if (hid) + { + const char* dev_path = udev_device_get_devnode(hid); + const char* action = udev_device_get_action(hid); + + HIDDeviceDesc device_info; + device_info.Path = dev_path; + + MessageType notify_type; + if (OVR_strcmp(action, "add") == 0) + { + notify_type = Message_DeviceAdded; + + // Retrieve the device info. This can only be done on a connected + // device and is invalid for a disconnected device + + // Get the USB device + hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device"); + if (!hid) + { + return; + } + + getFullDesc(hid, &device_info); + } + else if (OVR_strcmp(action, "remove") == 0) + { + notify_type = Message_DeviceRemoved; + } + else + { + return; + } + + bool error = false; + bool deviceFound = false; + for (UPInt i = 0; i < NotificationDevices.GetSize(); i++) + { + if (NotificationDevices[i] && + NotificationDevices[i]->OnDeviceNotification(notify_type, &device_info, &error)) + { + // The notification was for an existing device + deviceFound = true; + break; + } + } + + if (notify_type == Message_DeviceAdded && !deviceFound) + { + DevManager->DetectHIDDevice(device_info); + } + + udev_device_unref(hid); + } +} + +//============================================================================= +// Linux::HIDDevice +//============================================================================= +HIDDevice::HIDDevice(HIDDeviceManager* manager) + : HIDManager(manager), InMinimalMode(false) +{ + DeviceHandle = -1; +} + +//----------------------------------------------------------------------------- +// This is a minimal constructor used during enumeration for us to pass +// a HIDDevice to the visit function (so that it can query feature reports). +HIDDevice::HIDDevice(HIDDeviceManager* manager, int device_handle) +: HIDManager(manager), DeviceHandle(device_handle), InMinimalMode(true) +{ +} + +//----------------------------------------------------------------------------- +HIDDevice::~HIDDevice() +{ + if (!InMinimalMode) + { + HIDShutdown(); + } +} + +//----------------------------------------------------------------------------- +bool HIDDevice::HIDInitialize(const String& path) +{ + const char* hid_path = path.ToCStr(); + if (!openDevice(hid_path)) + { + LogText("OVR::Linux::HIDDevice - Failed to open HIDDevice: %s", hid_path); + return false; + } + + HIDManager->DevManager->pThread->AddTicksNotifier(this); + HIDManager->AddNotificationDevice(this); + + LogText("OVR::Linux::HIDDevice - Opened '%s'\n" + " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n", + DevDesc.Path.ToCStr(), + DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(), + DevDesc.SerialNumber.ToCStr()); + + return true; +} + +//----------------------------------------------------------------------------- +bool HIDDevice::initInfo() +{ + // Device must have been successfully opened. + OVR_ASSERT(DeviceHandle >= 0); + + int desc_size = 0; + hidraw_report_descriptor rpt_desc; + memset(&rpt_desc, 0, sizeof(rpt_desc)); + + // get report descriptor size + int r = ioctl(DeviceHandle, HIDIOCGRDESCSIZE, &desc_size); + if (r < 0) + { + OVR_ASSERT_LOG(false, ("Failed to get report descriptor size.")); + return false; + } + + // Get the report descriptor + rpt_desc.size = desc_size; + r = ioctl(DeviceHandle, HIDIOCGRDESC, &rpt_desc); + if (r < 0) + { + OVR_ASSERT_LOG(false, ("Failed to get report descriptor.")); + return false; + } + + /* + // Get report lengths. + SInt32 bufferLength; + bool getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxInputReportSizeKey), &bufferLength); + OVR_ASSERT(getResult); + InputReportBufferLength = (UInt16) bufferLength; + + getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxOutputReportSizeKey), &bufferLength); + OVR_ASSERT(getResult); + OutputReportBufferLength = (UInt16) bufferLength; + + getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxFeatureReportSizeKey), &bufferLength); + OVR_ASSERT(getResult); + FeatureReportBufferLength = (UInt16) bufferLength; + + + if (ReadBufferSize < InputReportBufferLength) + { + OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer.")); + return false; + } + + // Get device desc. + if (!HIDManager->getFullDesc(Device, &DevDesc)) + { + OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device.")); + return false; + } + + return true; + */ + + // Get report lengths. +// TODO: hard-coded for now. Need to interpret these values from the report descriptor + InputReportBufferLength = 62; + OutputReportBufferLength = 0; + FeatureReportBufferLength = 69; + + if (ReadBufferSize < InputReportBufferLength) + { + OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer.")); + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +bool HIDDevice::openDevice(const char* device_path) +{ + // First fill out the device descriptor + if (!HIDManager->GetDescriptorFromPath(device_path, &DevDesc)) + { + return false; + } + + // Now open the device + DeviceHandle = open(device_path, O_RDWR); + if (DeviceHandle < 0) + { + OVR_DEBUG_LOG(("Failed 'CreateHIDFile' while opening device, error = 0x%X.", errno)); + DeviceHandle = -1; + return false; + } + + // fill out some values from the feature report descriptor + if (!initInfo()) + { + OVR_ASSERT_LOG(false, ("Failed to get HIDDevice info.")); + + close(DeviceHandle); + DeviceHandle = -1; + return false; + } + + // Add the device to the polling list + if (!HIDManager->DevManager->pThread->AddSelectFd(this, DeviceHandle)) + { + OVR_ASSERT_LOG(false, ("Failed to initialize polling for HIDDevice.")); + + close(DeviceHandle); + DeviceHandle = -1; + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +void HIDDevice::HIDShutdown() +{ + + HIDManager->DevManager->pThread->RemoveTicksNotifier(this); + HIDManager->RemoveNotificationDevice(this); + + if (DeviceHandle >= 0) // Device may already have been closed if unplugged. + { + closeDevice(false); + } + + LogText("OVR::Linux::HIDDevice - HIDShutdown '%s'\n", DevDesc.Path.ToCStr()); +} + +//----------------------------------------------------------------------------- +void HIDDevice::closeDevice(bool wasUnplugged) +{ + OVR_ASSERT(DeviceHandle >= 0); + + + HIDManager->DevManager->pThread->RemoveSelectFd(this, DeviceHandle); + + close(DeviceHandle); // close the file handle + DeviceHandle = -1; + + LogText("OVR::Linux::HIDDevice - HID Device Closed '%s'\n", DevDesc.Path.ToCStr()); +} + +//----------------------------------------------------------------------------- +void HIDDevice::closeDeviceOnIOError() +{ + LogText("OVR::Linux::HIDDevice - Lost connection to '%s'\n", DevDesc.Path.ToCStr()); + closeDevice(false); +} + +//----------------------------------------------------------------------------- +bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length) +{ + + if (DeviceHandle < 0) + return false; + + UByte reportID = data[0]; + + if (reportID == 0) + { + // Not using reports so remove from data packet. + data++; + length--; + } + + int r = ioctl(DeviceHandle, HIDIOCSFEATURE(length), data); + return (r >= 0); +} + +//----------------------------------------------------------------------------- +bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length) +{ + if (DeviceHandle < 0) + return false; + + int r = ioctl(DeviceHandle, HIDIOCGFEATURE(length), data); + return (r >= 0); +} + +//----------------------------------------------------------------------------- +UInt64 HIDDevice::OnTicks(UInt64 ticksMks) +{ + if (Handler) + { + return Handler->OnTicks(ticksMks); + } + + return DeviceManagerThread::Notifier::OnTicks(ticksMks); +} + +//----------------------------------------------------------------------------- +void HIDDevice::OnEvent(int i, int fd) +{ + // We have data to read from the device + int bytes = read(fd, ReadBuffer, ReadBufferSize); + if (bytes >= 0) + { +// TODO: I need to handle partial messages and package reconstruction + if (Handler) + { + Handler->OnInputReport(ReadBuffer, bytes); + } + } + else + { // Close the device on read error. + closeDeviceOnIOError(); + } +} + +//----------------------------------------------------------------------------- +bool HIDDevice::OnDeviceNotification(MessageType messageType, + HIDDeviceDesc* device_info, + bool* error) +{ + const char* device_path = device_info->Path.ToCStr(); + + if (messageType == Message_DeviceAdded && DeviceHandle < 0) + { + // Is this the correct device? + if (!(device_info->VendorId == DevDesc.VendorId + && device_info->ProductId == DevDesc.ProductId + && device_info->SerialNumber == DevDesc.SerialNumber)) + { + return false; + } + + // A closed device has been re-added. Try to reopen. + if (!openDevice(device_path)) + { + LogError("OVR::Linux::HIDDevice - Failed to reopen a device '%s' that was re-added.\n", + device_path); + *error = true; + return true; + } + + LogText("OVR::Linux::HIDDevice - Reopened device '%s'\n", device_path); + + if (Handler) + { + Handler->OnDeviceMessage(HIDHandler::HIDDeviceMessage_DeviceAdded); + } + } + else if (messageType == Message_DeviceRemoved) + { + // Is this the correct device? + // For disconnected device, the device description will be invalid so + // checking the path is the only way to match them + if (DevDesc.Path.CompareNoCase(device_path) != 0) + { + return false; + } + + if (DeviceHandle >= 0) + { + closeDevice(true); + } + + if (Handler) + { + Handler->OnDeviceMessage(HIDHandler::HIDDeviceMessage_DeviceRemoved); + } + } + else + { + OVR_ASSERT(0); + } + + *error = false; + return true; +} + +//----------------------------------------------------------------------------- +HIDDeviceManager* HIDDeviceManager::CreateInternal(Linux::DeviceManager* devManager) +{ + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new Linux::HIDDeviceManager(devManager); + + if (manager) + { + if (manager->Initialize()) + { + manager->AddRef(); + } + else + { + manager.Clear(); + } + } + + return manager.GetPtr(); +} + +} // namespace Linux + +//------------------------------------------------------------------------------------- +// ***** Creation + +// Creates a new HIDDeviceManager and initializes OVR. +HIDDeviceManager* HIDDeviceManager::Create() +{ + OVR_ASSERT_LOG(false, ("Standalone mode not implemented yet.")); + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new Linux::HIDDeviceManager(NULL); + + if (manager) + { + if (manager->Initialize()) + { + manager->AddRef(); + } + else + { + manager.Clear(); + } + } + + return manager.GetPtr(); +} + +} // namespace OVR diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/linux/OVR_Linux_HIDDevice.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/linux/OVR_Linux_HIDDevice.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,124 @@ +/************************************************************************************ +Filename : OVR_Linux_HIDDevice.h +Content : Linux HID device implementation. +Created : June 13, 2013 +Authors : Brant Lewis + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_LINUX_HIDDevice_h +#define OVR_LINUX_HIDDevice_h + +#include "OVR_HIDDevice.h" +#include "OVR_Linux_DeviceManager.h" +#include + +namespace OVR { namespace Linux { + +class HIDDeviceManager; + +//------------------------------------------------------------------------------------- +// ***** Linux HIDDevice + +class HIDDevice : public OVR::HIDDevice, public DeviceManagerThread::Notifier +{ +private: + friend class HIDDeviceManager; + +public: + HIDDevice(HIDDeviceManager* manager); + + // This is a minimal constructor used during enumeration for us to pass + // a HIDDevice to the visit function (so that it can query feature reports). + HIDDevice(HIDDeviceManager* manager, int device_handle); + + virtual ~HIDDevice(); + + bool HIDInitialize(const String& path); + void HIDShutdown(); + + virtual bool SetFeatureReport(UByte* data, UInt32 length); + virtual bool GetFeatureReport(UByte* data, UInt32 length); + + // DeviceManagerThread::Notifier + void OnEvent(int i, int fd); + UInt64 OnTicks(UInt64 ticksMks); + + bool OnDeviceNotification(MessageType messageType, + HIDDeviceDesc* device_info, + bool* error); + +private: + bool initInfo(); + bool openDevice(const char* dev_path); + void closeDevice(bool wasUnplugged); + void closeDeviceOnIOError(); + bool setupDevicePluggedInNotification(); + + bool InMinimalMode; + HIDDeviceManager* HIDManager; + int DeviceHandle; // file handle to the device + HIDDeviceDesc DevDesc; + + enum { ReadBufferSize = 96 }; + UByte ReadBuffer[ReadBufferSize]; + + UInt16 InputReportBufferLength; + UInt16 OutputReportBufferLength; + UInt16 FeatureReportBufferLength; +}; + + +//------------------------------------------------------------------------------------- +// ***** Linux HIDDeviceManager + +class HIDDeviceManager : public OVR::HIDDeviceManager, public DeviceManagerThread::Notifier +{ + friend class HIDDevice; + +public: + HIDDeviceManager(Linux::DeviceManager* Manager); + virtual ~HIDDeviceManager(); + + virtual bool Initialize(); + virtual void Shutdown(); + + virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor); + virtual OVR::HIDDevice* Open(const String& path); + + static HIDDeviceManager* CreateInternal(DeviceManager* manager); + + void OnEvent(int i, int fd); + +private: + bool initializeManager(); + bool initVendorProductVersion(udev_device* device, HIDDeviceDesc* pDevDesc); + bool getPath(udev_device* device, String* pPath); + bool getIntProperty(udev_device* device, const char* key, int32_t* pResult); + bool getStringProperty(udev_device* device, + const char* propertyName, + OVR::String* pResult); + bool getFullDesc(udev_device* device, HIDDeviceDesc* desc); + bool GetDescriptorFromPath(const char* dev_path, HIDDeviceDesc* desc); + + bool AddNotificationDevice(HIDDevice* device); + bool RemoveNotificationDevice(HIDDevice* device); + + DeviceManager* DevManager; + + udev* UdevInstance; // a handle to the udev library instance + udev_monitor* HIDMonitor; + int HIDMonHandle; // the udev_monitor file handle + + Array NotificationDevices; +}; + +}} // namespace OVR::Linux + +#endif // OVR_Linux_HIDDevice_h diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/linux/OVR_Linux_HMDDevice.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/linux/OVR_Linux_HMDDevice.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,392 @@ +/************************************************************************************ + +Filename : OVR_Linux_HMDDevice.h +Content : Linux HMDDevice implementation +Created : June 17, 2013 +Authors : Brant Lewis + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Linux_HMDDevice.h" + +#include "OVR_Linux_DeviceManager.h" + +#include "OVR_Profile.h" + +#include +#include + +namespace OVR { namespace Linux { + +//------------------------------------------------------------------------------------- + +HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory, const String& displayDeviceName, long dispId) + : DeviceCreateDesc(factory, Device_HMD), + DisplayDeviceName(displayDeviceName), + DesktopX(0), DesktopY(0), Contents(0), + HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0), + DisplayId(dispId) +{ + DeviceId = DisplayDeviceName; +} + +HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other) + : DeviceCreateDesc(other.pFactory, Device_HMD), + DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName), + DesktopX(other.DesktopX), DesktopY(other.DesktopY), Contents(other.Contents), + HResolution(other.HResolution), VResolution(other.VResolution), + HScreenSize(other.HScreenSize), VScreenSize(other.VScreenSize), + DisplayId(other.DisplayId) +{ +} + +HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc** pcandidate) const +{ + if ((other.Type != Device_HMD) || (other.pFactory != pFactory)) + return Match_None; + + // There are several reasons we can come in here: + // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc + // - Require exact device DeviceId/DeviceName match + // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc + // - This DeviceId is empty; becomes candidate + // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc + // - This other.DeviceId is empty; becomes candidate + + const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; + + if ((DeviceId == s2.DeviceId) && + (DisplayId == s2.DisplayId)) + { + // Non-null DeviceId may match while size is different if screen size was overwritten + // by SensorDisplayInfo in prior iteration. + if (!DeviceId.IsEmpty() || + ((HScreenSize == s2.HScreenSize) && + (VScreenSize == s2.VScreenSize)) ) + { + *pcandidate = 0; + return Match_Found; + } + } + + + // DisplayInfo takes precedence, although we try to match it first. + if ((HResolution == s2.HResolution) && + (VResolution == s2.VResolution) && + (HScreenSize == s2.HScreenSize) && + (VScreenSize == s2.VScreenSize)) + { + if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty()) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + + *pcandidate = 0; + return Match_Found; + } + + // SensorDisplayInfo may override resolution settings, so store as candidate. + if (s2.DeviceId.IsEmpty()) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + // OTHER HMD Monitor desc may initialize DeviceName/Id + else if (DeviceId.IsEmpty()) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + + return Match_None; +} + + +bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other, + bool* newDeviceFlag) +{ + // This candidate was the the "best fit" to apply sensor DisplayInfo to. + OVR_ASSERT(other.Type == Device_HMD); + + const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; + + // Force screen size on resolution from SensorDisplayInfo. + // We do this because USB detection is more reliable as compared to HDMI EDID, + // which may be corrupted by splitter reporting wrong monitor + if (s2.DeviceId.IsEmpty()) + { + HScreenSize = s2.HScreenSize; + VScreenSize = s2.VScreenSize; + Contents |= Contents_Screen; + + if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion) + { + memcpy(DistortionK, s2.DistortionK, sizeof(float)*4); + Contents |= Contents_Distortion; + } + DeviceId = s2.DeviceId; + DisplayId = s2.DisplayId; + DisplayDeviceName = s2.DisplayDeviceName; + if (newDeviceFlag) *newDeviceFlag = true; + } + else if (DeviceId.IsEmpty()) + { + DeviceId = s2.DeviceId; + DisplayId = s2.DisplayId; + DisplayDeviceName = s2.DisplayDeviceName; + + // ScreenSize and Resolution are NOT assigned here, since they may have + // come from a sensor DisplayInfo (which has precedence over HDMI). + + if (newDeviceFlag) *newDeviceFlag = true; + } + else + { + if (newDeviceFlag) *newDeviceFlag = false; + } + + return true; +} + +bool HMDDeviceCreateDesc::MatchDevice(const String& path) +{ + return DeviceId.CompareNoCase(path) == 0; +} + +//------------------------------------------------------------------------------------- +// ***** HMDDeviceFactory + +HMDDeviceFactory HMDDeviceFactory::Instance; + +void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) +{ + // For now we'll assume the Rift DK1 is attached in extended monitor mode. Ultimately we need to + // use XFree86 to enumerate X11 screens in case the Rift is attached as a separate screen. We also + // need to be able to read the EDID manufacturer product code to be able to differentiate between + // Rift models. + + bool foundHMD = false; + + Display* display = XOpenDisplay(NULL); + if (display && XineramaIsActive(display)) + { + int numberOfScreens; + XineramaScreenInfo* screens = XineramaQueryScreens(display, &numberOfScreens); + + for (int i = 0; i < numberOfScreens; i++) + { + XineramaScreenInfo screenInfo = screens[i]; + + if (screenInfo.width == 1280 && screenInfo.height == 800) + { + String deviceName = "OVR0001"; + + HMDDeviceCreateDesc hmdCreateDesc(this, deviceName, i); + hmdCreateDesc.SetScreenParameters(screenInfo.x_org, screenInfo.y_org, 1280, 800, 0.14976f, 0.0936f); + + OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %s - %d\n", + deviceName.ToCStr(), i)); + + // Notify caller about detected device. This will call EnumerateAddDevice + // if the this is the first time device was detected. + visitor.Visit(hmdCreateDesc); + foundHMD = true; + break; + } + } + + XFree(screens); + } + + + // Real HMD device is not found; however, we still may have a 'fake' HMD + // device created via SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo. + // Need to find it and set 'Enumerated' to true to avoid Removal notification. + if (!foundHMD) + { + Ptr hmdDevDesc = getManager()->FindDevice("", Device_HMD); + if (hmdDevDesc) + hmdDevDesc->Enumerated = true; + } +} + +DeviceBase* HMDDeviceCreateDesc::NewDeviceInstance() +{ + return new HMDDevice(this); +} + +bool HMDDeviceCreateDesc::Is7Inch() const +{ + return (strstr(DeviceId.ToCStr(), "OVR0001") != 0) || (Contents & Contents_7Inch); +} + +Profile* HMDDeviceCreateDesc::GetProfileAddRef() const +{ + // Create device may override profile name, so get it from there is possible. + ProfileManager* profileManager = GetManagerImpl()->GetProfileManager(); + ProfileType profileType = GetProfileType(); + const char * profileName = pDevice ? + ((HMDDevice*)pDevice)->GetProfileName() : + profileManager->GetDefaultProfileName(profileType); + + return profileName ? + profileManager->LoadProfile(profileType, profileName) : + profileManager->GetDeviceDefaultProfile(profileType); +} + + +bool HMDDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_HMD) && + (info->InfoClassType != Device_None)) + return false; + + bool is7Inch = Is7Inch(); + + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, + is7Inch ? "Oculus Rift DK1" : + ((HResolution >= 1920) ? "Oculus Rift DK HD" : "Oculus Rift DK1-Prototype") ); + OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, "Oculus VR"); + info->Type = Device_HMD; + info->Version = 0; + + // Display detection. + if (info->InfoClassType == Device_HMD) + { + HMDInfo* hmdInfo = static_cast(info); + + hmdInfo->DesktopX = DesktopX; + hmdInfo->DesktopY = DesktopY; + hmdInfo->HResolution = HResolution; + hmdInfo->VResolution = VResolution; + hmdInfo->HScreenSize = HScreenSize; + hmdInfo->VScreenSize = VScreenSize; + hmdInfo->VScreenCenter = VScreenSize * 0.5f; + hmdInfo->InterpupillaryDistance = 0.064f; // Default IPD; should be configurable. + hmdInfo->LensSeparationDistance = 0.0635f; + + // Obtain IPD from profile. + Ptr profile = *GetProfileAddRef(); + + if (profile) + { + hmdInfo->InterpupillaryDistance = profile->GetIPD(); + // TBD: Switch on EyeCup type. + } + + if (Contents & Contents_Distortion) + { + memcpy(hmdInfo->DistortionK, DistortionK, sizeof(float)*4); + } + else + { + if (is7Inch) + { + // 7" screen. + hmdInfo->DistortionK[0] = 1.0f; + hmdInfo->DistortionK[1] = 0.22f; + hmdInfo->DistortionK[2] = 0.24f; + hmdInfo->EyeToScreenDistance = 0.041f; + } + else + { + hmdInfo->DistortionK[0] = 1.0f; + hmdInfo->DistortionK[1] = 0.18f; + hmdInfo->DistortionK[2] = 0.115f; + + if (HResolution == 1920) + hmdInfo->EyeToScreenDistance = 0.040f; + else + hmdInfo->EyeToScreenDistance = 0.0387f; + } + + hmdInfo->ChromaAbCorrection[0] = 0.996f; + hmdInfo->ChromaAbCorrection[1] = -0.004f; + hmdInfo->ChromaAbCorrection[2] = 1.014f; + hmdInfo->ChromaAbCorrection[3] = 0.0f; + } + + OVR_strcpy(hmdInfo->DisplayDeviceName, sizeof(hmdInfo->DisplayDeviceName), + DisplayDeviceName.ToCStr()); + hmdInfo->DisplayId = DisplayId; + } + + return true; +} + +//------------------------------------------------------------------------------------- +// ***** HMDDevice + +HMDDevice::HMDDevice(HMDDeviceCreateDesc* createDesc) + : OVR::DeviceImpl(createDesc, 0) +{ +} +HMDDevice::~HMDDevice() +{ +} + +bool HMDDevice::Initialize(DeviceBase* parent) +{ + pParent = parent; + + // Initialize user profile to default for device. + ProfileManager* profileManager = GetManager()->GetProfileManager(); + ProfileName = profileManager->GetDefaultProfileName(getDesc()->GetProfileType()); + + return true; +} +void HMDDevice::Shutdown() +{ + ProfileName.Clear(); + pCachedProfile.Clear(); + pParent.Clear(); +} + +Profile* HMDDevice::GetProfile() const +{ + if (!pCachedProfile) + pCachedProfile = *getDesc()->GetProfileAddRef(); + return pCachedProfile.GetPtr(); +} + +const char* HMDDevice::GetProfileName() const +{ + return ProfileName.ToCStr(); +} + +bool HMDDevice::SetProfileName(const char* name) +{ + pCachedProfile.Clear(); + if (!name) + { + ProfileName.Clear(); + return 0; + } + if (GetManager()->GetProfileManager()->HasProfile(getDesc()->GetProfileType(), name)) + { + ProfileName = name; + return true; + } + return false; +} + +OVR::SensorDevice* HMDDevice::GetSensor() +{ + // Just return first sensor found since we have no way to match it yet. + OVR::SensorDevice* sensor = GetManager()->EnumerateDevices().CreateDevice(); + if (sensor) + sensor->SetCoordinateFrame(SensorDevice::Coord_HMD); + return sensor; +} + +}} // namespace OVR::Linux + + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/linux/OVR_Linux_HMDDevice.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/linux/OVR_Linux_HMDDevice.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,156 @@ +/************************************************************************************ + +Filename : OVR_Linux_HMDDevice.h +Content : Linux HMDDevice implementation +Created : June 17, 2013 +Authors : Brant Lewis + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Linux_HMDDevice_h +#define OVR_Linux_HMDDevice_h + +#include "OVR_Linux_DeviceManager.h" +#include "OVR_Profile.h" + +namespace OVR { namespace Linux { + +class HMDDevice; + +//------------------------------------------------------------------------------------- + +// HMDDeviceFactory enumerates attached Oculus HMD devices. +// +// This is currently done by matching monitor device strings. + +class HMDDeviceFactory : public DeviceFactory +{ +public: + static HMDDeviceFactory Instance; + + // Enumerates devices, creating and destroying relevant objects in manager. + virtual void EnumerateDevices(EnumerateVisitor& visitor); + +protected: + DeviceManager* getManager() const { return (DeviceManager*) pManager; } +}; + + +class HMDDeviceCreateDesc : public DeviceCreateDesc +{ + friend class HMDDevice; + +protected: + enum + { + Contents_Screen = 1, + Contents_Distortion = 2, + Contents_7Inch = 4, + }; + String DeviceId; + String DisplayDeviceName; + int DesktopX, DesktopY; + unsigned Contents; + unsigned HResolution, VResolution; + float HScreenSize, VScreenSize; + long DisplayId; + float DistortionK[4]; + +public: + HMDDeviceCreateDesc(DeviceFactory* factory, const String& displayDeviceName, long dispId); + HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other); + + virtual DeviceCreateDesc* Clone() const + { + return new HMDDeviceCreateDesc(*this); + } + + virtual DeviceBase* NewDeviceInstance(); + + virtual MatchResult MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc**) const; + + // Matches device by path. + virtual bool MatchDevice(const String& path); + + virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL); + + virtual bool GetDeviceInfo(DeviceInfo* info) const; + + // Requests the currently used default profile. This profile affects the + // settings reported by HMDInfo. + Profile* GetProfileAddRef() const; + + ProfileType GetProfileType() const + { + return (HResolution >= 1920) ? Profile_RiftDKHD : Profile_RiftDK1; + } + + + void SetScreenParameters(int x, int y, unsigned hres, unsigned vres, float hsize, float vsize) + { + DesktopX = x; + DesktopY = y; + HResolution = hres; + VResolution = vres; + HScreenSize = hsize; + VScreenSize = vsize; + Contents |= Contents_Screen; + } + void SetDistortion(const float* dks) + { + for (int i = 0; i < 4; i++) + DistortionK[i] = dks[i]; + Contents |= Contents_Distortion; + } + + void Set7Inch() { Contents |= Contents_7Inch; } + + bool Is7Inch() const; +}; + + +//------------------------------------------------------------------------------------- + +// HMDDevice represents an Oculus HMD device unit. An instance of this class +// is typically created from the DeviceManager. +// After HMD device is created, we its sensor data can be obtained by +// first creating a Sensor object and then wrappig it in SensorFusion. + +class HMDDevice : public DeviceImpl +{ +public: + HMDDevice(HMDDeviceCreateDesc* createDesc); + ~HMDDevice(); + + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + // Requests the currently used default profile. This profile affects the + // settings reported by HMDInfo. + virtual Profile* GetProfile() const; + virtual const char* GetProfileName() const; + virtual bool SetProfileName(const char* name); + + // Query associated sensor. + virtual OVR::SensorDevice* GetSensor(); + +protected: + HMDDeviceCreateDesc* getDesc() const { return (HMDDeviceCreateDesc*)pCreateDesc.GetPtr(); } + + // User name for the profile used with this device. + String ProfileName; + mutable Ptr pCachedProfile; +}; + + +}} // namespace OVR::Linux + +#endif // OVR_Linux_HMDDevice_h + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/linux/OVR_Linux_SensorDevice.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/linux/OVR_Linux_SensorDevice.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,48 @@ +/************************************************************************************ + +Filename : OVR_Linux_SensorDevice.cpp +Content : Linux SensorDevice implementation +Created : June 13, 2013 +Authors : Brant Lewis + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +//#include "OVR_OSX_HMDDevice.h" +#include "OVR_SensorImpl.h" +#include "OVR_DeviceImpl.h" + +namespace OVR { namespace OSX { + +} // namespace OSX + +//------------------------------------------------------------------------------------- +void SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo( const SensorDisplayInfoImpl& displayInfo, + DeviceFactory::EnumerateVisitor& visitor) +{ +/* + Linux::HMDDeviceCreateDesc hmdCreateDesc(&Linux::HMDDeviceFactory::Instance, 1, 1, "", 0); + + hmdCreateDesc.SetScreenParameters( 0, 0, + displayInfo.HResolution, displayInfo.VResolution, + displayInfo.HScreenSize, displayInfo.VScreenSize); + + if ((displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) == SensorDisplayInfoImpl::Base_Distortion) + hmdCreateDesc.SetDistortion(displayInfo.DistortionK); + if (displayInfo.HScreenSize > 0.14f) + hmdCreateDesc.Set7Inch(); + + visitor.Visit(hmdCreateDesc); + */ + + +} + +} // namespace OVR + + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/linux/OVR_ThreadsPthread.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/linux/OVR_ThreadsPthread.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ + #include "OVR_Threads.h" #include "OVR_Hash.h" #ifdef OVR_ENABLE_THREADS #include "OVR_Timer.h" #include "OVR_Log.h" #include #include #ifdef OVR_OS_PS3 #include #include #include #define sleep(x) sys_timer_sleep(x) #define usleep(x) sys_timer_usleep(x) using std::timespec; #else #include #include #include #endif 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) { 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_ASSERT(pthread_self() == LockedBy && LockCount > 0); unsigned lockCount; LockCount--; lockCount = LockCount; pthread_mutex_unlock(&SMutex); } bool MutexImpl::IsLockedByAnotherThread(Mutex* 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; ipImpl->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; #ifdef OVR_OS_PS3 sys_time_sec_t s; sys_time_nsec_t ns; sys_time_get_current_time(&s, &ns); ts.tv_sec = s + (delay / 1000); ts.tv_nsec = ns + (delay % 1000) * 1000000; #else struct timeval tv; gettimeofday(&tv, 0); ts.tv_sec = tv.tv_sec + (delay / 1000); ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000; #endif 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; iDoLock(); // 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 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; } /* 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 */ // ***** 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 (void*) result; } int Thread::InitAttr = 0; pthread_attr_t Thread::Attr; /* static */ int Thread::GetOSPriority(ThreadPriority p) //static inline int MapToSystemPrority(Thread::ThreadPriority p) { #ifdef OVR_OS_PS3 switch(p) { case Thread::CriticalPriority: return 0; case Thread::HighestPriority: return 300; case Thread::AboveNormalPriority: return 600; case Thread::NormalPriority: return 1000; case Thread::BelowNormalPriority: return 1500; case Thread::LowestPriority: return 2500; case Thread::IdlePriority: return 3071; } return 1000; #else OVR_UNUSED(p); return -1; #endif } 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((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; } #ifdef OVR_OS_PS3 sys_lwmutex_attribute_t Lock::LockAttr = { SYS_SYNC_PRIORITY, SYS_SYNC_RECURSIVE }; #endif } #endif // OVR_ENABLE_THREADS \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/osx/OVR_OSX_DeviceManager.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/osx/OVR_OSX_DeviceManager.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,349 @@ +/************************************************************************************ + +Filename : OVR_OSX_DeviceManager.cpp +Content : OSX specific DeviceManager implementation. +Created : March 14, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_OSX_DeviceManager.h" + +// Sensor & HMD Factories +#include "OVR_LatencyTestImpl.h" +#include "OVR_SensorImpl.h" +#include "OVR_OSX_HMDDevice.h" +#include "OVR_OSX_HIDDevice.h" + +#include "Kernel/OVR_Timer.h" +#include "Kernel/OVR_Std.h" +#include "Kernel/OVR_Log.h" + +#include +#include + + +namespace OVR { namespace OSX { + +//------------------------------------------------------------------------------------- +// **** OSX::DeviceManager + +DeviceManager::DeviceManager() +{ +} + +DeviceManager::~DeviceManager() +{ + OVR_DEBUG_LOG(("OSX::DeviceManager::~DeviceManager was called")); +} + +bool DeviceManager::Initialize(DeviceBase*) +{ + if (!DeviceManagerImpl::Initialize(0)) + return false; + + // Start the background thread. + pThread = *new DeviceManagerThread(); + if (!pThread || !pThread->Start()) + return false; + + // Wait for the thread to be fully up and running. + pThread->StartupEvent.Wait(); + + // Do this now that we know the thread's run loop. + HidDeviceManager = *HIDDeviceManager::CreateInternal(this); + + CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallBack, this); + + pCreateDesc->pDevice = this; + LogText("OVR::DeviceManager - initialized.\n"); + + return true; +} + +void DeviceManager::Shutdown() +{ + LogText("OVR::DeviceManager - shutting down.\n"); + + CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallBack, this); + + // Set Manager shutdown marker variable; this prevents + // any existing DeviceHandle objects from accessing device. + pCreateDesc->pLock->pManager = 0; + + // Push for thread shutdown *WITH NO WAIT*. + // This will have the following effect: + // - Exit command will get enqueued, which will be executed later on the thread itself. + // - Beyond this point, this DeviceManager object may be deleted by our caller. + // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will + // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued + // after pManager is null. + // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last + // reference to the thread object. + pThread->Shutdown(); + pThread.Clear(); + + DeviceManagerImpl::Shutdown(); +} + +ThreadCommandQueue* DeviceManager::GetThreadQueue() +{ + return pThread; +} + +ThreadId DeviceManager::GetThreadId() const +{ + return pThread->GetThreadId(); +} + +bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_Manager) && + (info->InfoClassType != Device_None)) + return false; + + info->Type = Device_Manager; + info->Version = 0; + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager"); + OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc."); + return true; +} + +DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args) +{ + // TBD: Can this be avoided in the future, once proper device notification is in place? + pThread->PushCall((DeviceManagerImpl*)this, + &DeviceManager::EnumerateAllFactoryDevices, true); + + return DeviceManagerImpl::EnumerateDevicesEx(args); +} + +void DeviceManager::displayReconfigurationCallBack (CGDirectDisplayID display, + CGDisplayChangeSummaryFlags flags, + void *userInfo) +{ + DeviceManager* manager = reinterpret_cast(userInfo); + OVR_UNUSED(manager); + + if (flags & kCGDisplayAddFlag) + { + LogText("Display Added, id = %d\n", int(display)); + manager->EnumerateDevices(); + } + else if (flags & kCGDisplayRemoveFlag) + { + LogText("Display Removed, id = %d\n", int(display)); + manager->EnumerateDevices(); + } +} + +//------------------------------------------------------------------------------------- +// ***** DeviceManager Thread + +DeviceManagerThread::DeviceManagerThread() + : Thread(ThreadStackSize) +{ +} + +DeviceManagerThread::~DeviceManagerThread() +{ +} + +int DeviceManagerThread::Run() +{ + + SetThreadName("OVR::DeviceManagerThread"); + LogText("OVR::DeviceManagerThread - running (ThreadId=0x%p).\n", GetThreadId()); + + // Store out the run loop ref. + RunLoop = CFRunLoopGetCurrent(); + + // Create a 'source' to enable us to signal the run loop to process the command queue. + CFRunLoopSourceContext sourceContext; + memset(&sourceContext, 0, sizeof(sourceContext)); + sourceContext.version = 0; + sourceContext.info = this; + sourceContext.perform = &staticCommandQueueSourceCallback; + + CommandQueueSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 , &sourceContext); + + CFRunLoopAddSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode); + + + // Signal to the parent thread that initialization has finished. + StartupEvent.SetEvent(); + + + ThreadCommand::PopBuffer command; + + while(!IsExiting()) + { + // PopCommand will reset event on empty queue. + if (PopCommand(&command)) + { + command.Execute(); + } + else + { + SInt32 exitReason = 0; + do { + + UInt32 waitMs = INT_MAX; + + // If devices have time-dependent logic registered, get the longest wait + // allowed based on current ticks. + if (!TicksNotifiers.IsEmpty()) + { + UInt64 ticksMks = Timer::GetTicks(); + UInt32 waitAllowed; + + for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++) + { + waitAllowed = (UInt32)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs); + if (waitAllowed < waitMs) + waitMs = waitAllowed; + } + } + + // Enter blocking run loop. We may continue until we timeout in which + // case it's time to service the ticks. Or if commands arrive in the command + // queue then the source callback will call 'CFRunLoopStop' causing this + // to return. + CFTimeInterval blockInterval = 0.001 * (double) waitMs; + exitReason = CFRunLoopRunInMode(kCFRunLoopDefaultMode, blockInterval, false); + + if (exitReason == kCFRunLoopRunFinished) + { + // Maybe this will occur during shutdown? + break; + } + else if (exitReason == kCFRunLoopRunStopped ) + { + // Commands need processing or we're being shutdown. + break; + } + else if (exitReason == kCFRunLoopRunTimedOut) + { + // Timed out so that we can service our ticks callbacks. + continue; + } + else if (exitReason == kCFRunLoopRunHandledSource) + { + // Should never occur since the last param when we call + // 'CFRunLoopRunInMode' is false. + OVR_ASSERT(false); + break; + } + else + { + OVR_ASSERT_LOG(false, ("CFRunLoopRunInMode returned unexpected code")); + break; + } + } + while(true); + } + } + + + CFRunLoopRemoveSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode); + CFRelease(CommandQueueSource); + + LogText("OVR::DeviceManagerThread - exiting (ThreadId=0x%p).\n", GetThreadId()); + + return 0; +} + +void DeviceManagerThread::staticCommandQueueSourceCallback(void* pContext) +{ + DeviceManagerThread* pThread = (DeviceManagerThread*) pContext; + pThread->commandQueueSourceCallback(); +} + +void DeviceManagerThread::commandQueueSourceCallback() +{ + CFRunLoopStop(RunLoop); +} + +bool DeviceManagerThread::AddTicksNotifier(Notifier* notify) +{ + TicksNotifiers.PushBack(notify); + return true; +} + +bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify) +{ + for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++) + { + if (TicksNotifiers[i] == notify) + { + TicksNotifiers.RemoveAt(i); + return true; + } + } + return false; +} + +void DeviceManagerThread::Shutdown() +{ + // Push for thread shutdown *WITH NO WAIT*. + // This will have the following effect: + // - Exit command will get enqueued, which will be executed later on the thread itself. + // - Beyond this point, this DeviceManager object may be deleted by our caller. + // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will + // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued + // after pManager is null. + // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last + // reference to the thread object. + PushExitCommand(false); + + // make sure CFRunLoopRunInMode is woken up + CFRunLoopSourceSignal(CommandQueueSource); + CFRunLoopWakeUp(RunLoop); +} + +} // namespace OSX + + +//------------------------------------------------------------------------------------- +// ***** Creation + +// Creates a new DeviceManager and initializes OVR. +DeviceManager* DeviceManager::Create() +{ + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new OSX::DeviceManager; + + if (manager) + { + if (manager->Initialize(0)) + { + manager->AddFactory(&LatencyTestDeviceFactory::Instance); + manager->AddFactory(&SensorDeviceFactory::Instance); + manager->AddFactory(&OSX::HMDDeviceFactory::Instance); + + manager->AddRef(); + } + else + { + manager.Clear(); + } + } + + return manager.GetPtr(); +} + +} // namespace OVR diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/osx/OVR_OSX_DeviceManager.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/osx/OVR_OSX_DeviceManager.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,119 @@ +/************************************************************************************ + +Filename : OVR_OSX_DeviceManager.h +Content : OSX specific DeviceManager header. +Created : March 14, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_OSX_DeviceManager_h +#define OVR_OSX_DeviceManager_h + +#include "OVR_DeviceImpl.h" + +#include "Kernel/OVR_Timer.h" + +#include +#include +#include + + +namespace OVR { namespace OSX { + +class DeviceManagerThread; + +//------------------------------------------------------------------------------------- +// ***** OSX DeviceManager + +class DeviceManager : public DeviceManagerImpl +{ +public: + DeviceManager(); + ~DeviceManager(); + + // Initialize/Shutdown manager thread. + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + virtual ThreadCommandQueue* GetThreadQueue(); + virtual ThreadId GetThreadId() const; + + virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args); + + virtual bool GetDeviceInfo(DeviceInfo* info) const; + +protected: + static void displayReconfigurationCallBack (CGDirectDisplayID display, + CGDisplayChangeSummaryFlags flags, + void *userInfo); + +public: // data + Ptr pThread; +}; + +//------------------------------------------------------------------------------------- +// ***** Device Manager Background Thread + +class DeviceManagerThread : public Thread, public ThreadCommandQueue +{ + friend class DeviceManager; + enum { ThreadStackSize = 32 * 1024 }; +public: + DeviceManagerThread(); + ~DeviceManagerThread(); + + virtual int Run(); + + // ThreadCommandQueue notifications for CommandEvent handling. + virtual void OnPushNonEmpty_Locked() + { + CFRunLoopSourceSignal(CommandQueueSource); + CFRunLoopWakeUp(RunLoop); + } + + virtual void OnPopEmpty_Locked() {} + + + // Notifier used for different updates (EVENT or regular timing or messages). + class Notifier + { + public: + + // Called when timing ticks are updated. // Returns the largest number of microseconds + // this function can wait till next call. + virtual UInt64 OnTicks(UInt64 ticksMks) + { OVR_UNUSED1(ticksMks); return Timer::MksPerSecond * 1000; } + }; + + // Add notifier that will be called at regular intervals. + bool AddTicksNotifier(Notifier* notify); + bool RemoveTicksNotifier(Notifier* notify); + + CFRunLoopRef GetRunLoop() + { return RunLoop; } + + void Shutdown(); +private: + CFRunLoopRef RunLoop; + + CFRunLoopSourceRef CommandQueueSource; + + static void staticCommandQueueSourceCallback(void* pContext); + void commandQueueSourceCallback(); + + Event StartupEvent; + + // Ticks notifiers. Used for time-dependent events such as keep-alive. + Array TicksNotifiers; +}; + +}} // namespace OSX::OVR + +#endif // OVR_OSX_DeviceManager_h diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/osx/OVR_OSX_HIDDevice.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/osx/OVR_OSX_HIDDevice.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,899 @@ +/************************************************************************************ +Filename : OVR_OSX_HIDDevice.cpp +Content : OSX HID device implementation. +Created : February 26, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_OSX_HIDDevice.h" + +#include + +namespace OVR { namespace OSX { + +static const UInt32 MAX_QUEUED_INPUT_REPORTS = 5; + +//------------------------------------------------------------------------------------- +// **** OSX::DeviceManager + +HIDDeviceManager::HIDDeviceManager(DeviceManager* manager) + : DevManager(manager) +{ + HIDManager = NULL; +} + +HIDDeviceManager::~HIDDeviceManager() +{ +} + +CFRunLoopRef HIDDeviceManager::getRunLoop() +{ + if (DevManager != NULL) + { + return DevManager->pThread->GetRunLoop(); + } + + return CFRunLoopGetCurrent(); +} + +bool HIDDeviceManager::initializeManager() +{ + if (HIDManager != NULL) + { + return true; + } + + HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + + if (!HIDManager) + { + return false; + } + + // Create a Matching Dictionary + CFMutableDictionaryRef matchDict = + CFDictionaryCreateMutable(kCFAllocatorDefault, + 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + // Specify a device manufacturer in the Matching Dictionary + UInt32 vendorId = Oculus_VendorId; + CFNumberRef vendorIdRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendorId); + CFDictionarySetValue(matchDict, + CFSTR(kIOHIDVendorIDKey), + vendorIdRef); + // Register the Matching Dictionary to the HID Manager + IOHIDManagerSetDeviceMatching(HIDManager, matchDict); + CFRelease(vendorIdRef); + CFRelease(matchDict); + + // Register a callback for USB device detection with the HID Manager + IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, &staticDeviceMatchingCallback, this); + + IOHIDManagerScheduleWithRunLoop(HIDManager, getRunLoop(), kCFRunLoopDefaultMode); + + return true; +} + +bool HIDDeviceManager::Initialize() +{ + return initializeManager(); +} + +void HIDDeviceManager::Shutdown() +{ + OVR_ASSERT_LOG(HIDManager, ("Should have called 'Initialize' before 'Shutdown'.")); + CFRelease(HIDManager); + + LogText("OVR::OSX::HIDDeviceManager - shutting down.\n"); +} + +bool HIDDeviceManager::getIntProperty(IOHIDDeviceRef device, CFStringRef propertyName, SInt32* pResult) +{ + + CFTypeRef ref = IOHIDDeviceGetProperty(device, propertyName); + + if (!ref) + { + return false; + } + + if (CFGetTypeID(ref) != CFNumberGetTypeID()) + { + return false; + } + + CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, pResult); + + return true; +} + +bool HIDDeviceManager::initVendorProductVersion(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) +{ + + if (!getVendorId(device, &(pDevDesc->VendorId))) + { + return false; + } + + if (!getProductId(device, &(pDevDesc->ProductId))) + { + return false; + } + + return true; +} + +bool HIDDeviceManager::initUsage(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) +{ + + SInt32 result; + + if (!getIntProperty(device, CFSTR(kIOHIDPrimaryUsagePageKey), &result)) + { + return false; + } + + pDevDesc->UsagePage = result; + + + if (!getIntProperty(device, CFSTR(kIOHIDPrimaryUsageKey), &result)) + { + return false; + } + + pDevDesc->Usage = result; + + return true; +} + +bool HIDDeviceManager::initSerialNumber(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) +{ + return getSerialNumberString(device, &(pDevDesc->SerialNumber)); +} + +bool HIDDeviceManager::initStrings(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) +{ + + // Regardless of whether they fail we'll try and get the remaining. + getStringProperty(device, CFSTR(kIOHIDManufacturerKey), &(pDevDesc->Manufacturer)); + getStringProperty(device, CFSTR(kIOHIDProductKey), &(pDevDesc->Product)); + + return true; +} + +bool HIDDeviceManager::getStringProperty(IOHIDDeviceRef device, + CFStringRef propertyName, + String* pResult) +{ + + CFStringRef str = (CFStringRef) IOHIDDeviceGetProperty(device, propertyName); + + if (!str) + { + return false; + } + + CFIndex length = CFStringGetLength(str); + CFRange range = CFRangeMake(0, length); + + // Test the conversion first to get required buffer size. + CFIndex bufferLength; + CFIndex numberOfChars = CFStringGetBytes(str, + range, + kCFStringEncodingUTF8, + (char) '?', + FALSE, + NULL, + 0, + &bufferLength); + + if (numberOfChars == 0) + { + return false; + } + + // Now allocate buffer. + char* buffer = new char[bufferLength+1]; + + numberOfChars = CFStringGetBytes(str, + range, + kCFStringEncodingUTF8, + (char) '?', + FALSE, + (UInt8*) buffer, + bufferLength, + NULL); + OVR_ASSERT_LOG(numberOfChars != 0, ("CFStringGetBytes failed.")); + + buffer[bufferLength] = '\0'; + *pResult = String(buffer); + + return true; +} + +bool HIDDeviceManager::getVendorId(IOHIDDeviceRef device, UInt16* pResult) +{ + SInt32 result; + + if (!getIntProperty(device, CFSTR(kIOHIDVendorIDKey), &result)) + { + return false; + } + + *pResult = result; + + return true; +} + +bool HIDDeviceManager::getProductId(IOHIDDeviceRef device, UInt16* pResult) +{ + SInt32 result; + + if (!getIntProperty(device, CFSTR(kIOHIDProductIDKey), &result)) + { + return false; + } + + *pResult = result; + + return true; +} + +bool HIDDeviceManager::getLocationId(IOHIDDeviceRef device, SInt32* pResult) +{ + SInt32 result; + + if (!getIntProperty(device, CFSTR(kIOHIDLocationIDKey), &result)) + { + return false; + } + + *pResult = result; + + return true; +} + +bool HIDDeviceManager::getSerialNumberString(IOHIDDeviceRef device, String* pResult) +{ + + if (!getStringProperty(device, CFSTR(kIOHIDSerialNumberKey), pResult)) + { + return false; + } + + return true; +} + +bool HIDDeviceManager::getPath(IOHIDDeviceRef device, String* pPath) +{ + + String transport; + if (!getStringProperty(device, CFSTR(kIOHIDTransportKey), &transport)) + { + return false; + } + + UInt16 vendorId; + if (!getVendorId(device, &vendorId)) + { + return false; + } + + UInt16 productId; + if (!getProductId(device, &productId)) + { + return false; + } + + String serialNumber; + if (!getSerialNumberString(device, &serialNumber)) + { + return false; + } + + + StringBuffer buffer; + buffer.AppendFormat("%s:vid=%04hx:pid=%04hx:ser=%s", + transport.ToCStr(), + vendorId, + productId, + serialNumber.ToCStr()); + + *pPath = String(buffer); + + return true; +} + +bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor) +{ + if (!initializeManager()) + { + return false; + } + + + CFSetRef deviceSet = IOHIDManagerCopyDevices(HIDManager); + if (!deviceSet) + return false; + + CFIndex deviceCount = CFSetGetCount(deviceSet); + + // Allocate a block of memory and read the set into it. + IOHIDDeviceRef* devices = (IOHIDDeviceRef*) OVR_ALLOC(sizeof(IOHIDDeviceRef) * deviceCount); + CFSetGetValues(deviceSet, (const void **) devices); + + + // Iterate over devices. + for (CFIndex deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) + { + IOHIDDeviceRef hidDev = devices[deviceIndex]; + + if (!hidDev) + { + continue; + } + + HIDDeviceDesc devDesc; + + if (getPath(hidDev, &(devDesc.Path)) && + initVendorProductVersion(hidDev, &devDesc) && + enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId) && + initUsage(hidDev, &devDesc)) + { + initStrings(hidDev, &devDesc); + initSerialNumber(hidDev, &devDesc); + + // Look for the device to check if it is already opened. + Ptr existingDevice = DevManager->FindHIDDevice(devDesc); + // if device exists and it is opened then most likely the CreateHIDFile + // will fail; therefore, we just set Enumerated to 'true' and continue. + if (existingDevice && existingDevice->pDevice) + { + existingDevice->Enumerated = true; + continue; + } + + // Construct minimal device that the visitor callback can get feature reports from. + OSX::HIDDevice device(this, hidDev); + + enumVisitor->Visit(device, devDesc); + } + } + + OVR_FREE(devices); + CFRelease(deviceSet); + + return true; +} + +OVR::HIDDevice* HIDDeviceManager::Open(const String& path) +{ + + Ptr device = *new OSX::HIDDevice(this); + + if (!device->HIDInitialize(path)) + { + return NULL; + } + + device->AddRef(); + + return device; +} + +bool HIDDeviceManager::getFullDesc(IOHIDDeviceRef device, HIDDeviceDesc* desc) +{ + + if (!initVendorProductVersion(device, desc)) + { + return false; + } + + if (!initUsage(device, desc)) + { + return false; + } + + if (!initSerialNumber(device, desc)) + { + return false; + } + + initStrings(device, desc); + + return true; +} + +// New USB device specified in the matching dictionary has been added (callback function) +void HIDDeviceManager::staticDeviceMatchingCallback(void *inContext, + IOReturn inResult, + void *inSender, + IOHIDDeviceRef inIOHIDDeviceRef) +{ + HIDDeviceManager* hidMgr = static_cast(inContext); + HIDDeviceDesc hidDevDesc; + hidMgr->getPath(inIOHIDDeviceRef, &hidDevDesc.Path); + hidMgr->getFullDesc(inIOHIDDeviceRef, &hidDevDesc); + + hidMgr->DevManager->DetectHIDDevice(hidDevDesc); +} + +//------------------------------------------------------------------------------------- +// **** OSX::HIDDevice + +HIDDevice::HIDDevice(HIDDeviceManager* manager) + : HIDManager(manager), InMinimalMode(false) +{ + Device = NULL; + RepluggedNotificationPort = 0; +} + +// This is a minimal constructor used during enumeration for us to pass +// a HIDDevice to the visit function (so that it can query feature reports). +HIDDevice::HIDDevice(HIDDeviceManager* manager, IOHIDDeviceRef device) +: HIDManager(manager), Device(device), InMinimalMode(true) +{ + RepluggedNotificationPort = 0; +} + +HIDDevice::~HIDDevice() +{ + if (!InMinimalMode) + { + HIDShutdown(); + } +} + +bool HIDDevice::HIDInitialize(const String& path) +{ + + DevDesc.Path = path; + + if (!openDevice()) + { + LogText("OVR::OSX::HIDDevice - Failed to open HIDDevice: %s", path.ToCStr()); + return false; + } + + // Setup notification for when a device is unplugged and plugged back in. + if (!setupDevicePluggedInNotification()) + { + LogText("OVR::OSX::HIDDevice - Failed to setup notification for when device plugged back in."); + closeDevice(false); + return false; + } + + HIDManager->DevManager->pThread->AddTicksNotifier(this); + + + LogText("OVR::OSX::HIDDevice - Opened '%s'\n" + " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n", + DevDesc.Path.ToCStr(), + DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(), + DevDesc.SerialNumber.ToCStr()); + + return true; +} + +bool HIDDevice::initInfo() +{ + // Device must have been successfully opened. + OVR_ASSERT(Device); + + + // Get report lengths. + SInt32 bufferLength; + bool getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxInputReportSizeKey), &bufferLength); + OVR_ASSERT(getResult); + InputReportBufferLength = (UInt16) bufferLength; + + getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxOutputReportSizeKey), &bufferLength); + OVR_ASSERT(getResult); + OutputReportBufferLength = (UInt16) bufferLength; + + getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxFeatureReportSizeKey), &bufferLength); + OVR_ASSERT(getResult); + FeatureReportBufferLength = (UInt16) bufferLength; + + + if (ReadBufferSize < InputReportBufferLength) + { + OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer.")); + return false; + } + + // Get device desc. + if (!HIDManager->getFullDesc(Device, &DevDesc)) + { + OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device.")); + return false; + } + + return true; +} + +void HIDDevice::staticDeviceAddedCallback(void* pContext, io_iterator_t iterator) +{ + HIDDevice* pDevice = (HIDDevice*) pContext; + pDevice->deviceAddedCallback(iterator); +} + +void HIDDevice::deviceAddedCallback(io_iterator_t iterator) +{ + + if (Device == NULL) + { + if (openDevice()) + { + LogText("OVR::OSX::HIDDevice - Reopened device : %s", DevDesc.Path.ToCStr()); + + Ptr existingHIDDev = HIDManager->DevManager->FindHIDDevice(DevDesc); + if (existingHIDDev && existingHIDDev->pDevice) + { + HIDManager->DevManager->CallOnDeviceAdded(existingHIDDev); + } + } + } + + // Reset callback. + while (IOIteratorNext(iterator)) + ; +} + +bool HIDDevice::openDevice() +{ + + // Have to iterate through devices again to generate paths. + CFSetRef deviceSet = IOHIDManagerCopyDevices(HIDManager->HIDManager); + CFIndex deviceCount = CFSetGetCount(deviceSet); + + // Allocate a block of memory and read the set into it. + IOHIDDeviceRef* devices = (IOHIDDeviceRef*) OVR_ALLOC(sizeof(IOHIDDeviceRef) * deviceCount); + CFSetGetValues(deviceSet, (const void **) devices); + + + // Iterate over devices. + IOHIDDeviceRef device = NULL; + + for (CFIndex deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) + { + IOHIDDeviceRef tmpDevice = devices[deviceIndex]; + + if (!tmpDevice) + { + continue; + } + + String path; + if (!HIDManager->getPath(tmpDevice, &path)) + { + continue; + } + + if (path == DevDesc.Path) + { + device = tmpDevice; + break; + } + } + + + OVR_FREE(devices); + + if (!device) + { + CFRelease(deviceSet); + return false; + } + + // Attempt to open device. + if (IOHIDDeviceOpen(device, kIOHIDOptionsTypeSeizeDevice) + != kIOReturnSuccess) + { + CFRelease(deviceSet); + return false; + } + + // Retain the device before we release the set. + CFRetain(device); + CFRelease(deviceSet); + + + Device = device; + + + if (!initInfo()) + { + IOHIDDeviceClose(Device, kIOHIDOptionsTypeSeizeDevice); + CFRelease(Device); + Device = NULL; + return false; + } + + + // Setup the Run Loop and callbacks. + IOHIDDeviceScheduleWithRunLoop(Device, + HIDManager->getRunLoop(), + kCFRunLoopDefaultMode); + + IOHIDDeviceRegisterInputReportCallback(Device, + ReadBuffer, + ReadBufferSize, + staticHIDReportCallback, + this); + + IOHIDDeviceRegisterRemovalCallback(Device, + staticDeviceRemovedCallback, + this); + + return true; +} + +void HIDDevice::HIDShutdown() +{ + + HIDManager->DevManager->pThread->RemoveTicksNotifier(this); + + if (Device != NULL) // Device may already have been closed if unplugged. + { + closeDevice(false); + } + + IOObjectRelease(RepluggedNotification); + if (RepluggedNotificationPort) + IONotificationPortDestroy(RepluggedNotificationPort); + + LogText("OVR::OSX::HIDDevice - HIDShutdown '%s'\n", DevDesc.Path.ToCStr()); +} + +bool HIDDevice::setupDevicePluggedInNotification() +{ + + // Setup notification when devices are plugged in. + RepluggedNotificationPort = IONotificationPortCreate(kIOMasterPortDefault); + + CFRunLoopSourceRef notificationRunLoopSource = + IONotificationPortGetRunLoopSource(RepluggedNotificationPort); + + CFRunLoopAddSource(HIDManager->getRunLoop(), + notificationRunLoopSource, + kCFRunLoopDefaultMode); + + CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName); + + // Have to specify vendorId and productId. Doesn't seem to accept additional + // things like serial number. + SInt32 vendorId = DevDesc.VendorId; + CFNumberRef numberRef = CFNumberCreate(kCFAllocatorDefault, + kCFNumberSInt32Type, + &vendorId); + CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), numberRef); + CFRelease(numberRef); + + SInt32 deviceProductId = DevDesc.ProductId; + numberRef = CFNumberCreate(kCFAllocatorDefault, + kCFNumberSInt32Type, + &deviceProductId); + CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), numberRef); + CFRelease(numberRef); + + kern_return_t result = + IOServiceAddMatchingNotification(RepluggedNotificationPort, + kIOMatchedNotification, + matchingDict, + staticDeviceAddedCallback, + this, + &RepluggedNotification); + + if (result != KERN_SUCCESS) + { + CFRelease(RepluggedNotificationPort); + RepluggedNotificationPort = 0; + return false; + } + + // Iterate through to arm. + while (IOIteratorNext(RepluggedNotification)) + { + } + + return true; +} + +void HIDDevice::closeDevice(bool wasUnplugged) +{ + OVR_ASSERT(Device != NULL); + + if (!wasUnplugged) + { + // Clear the registered callbacks. + IOHIDDeviceRegisterInputReportCallback(Device, + ReadBuffer, + InputReportBufferLength, + NULL, + this); + + IOHIDDeviceRegisterRemovalCallback(Device, NULL, this); + + IOHIDDeviceUnscheduleFromRunLoop(Device, + HIDManager->getRunLoop(), + kCFRunLoopDefaultMode); + IOHIDDeviceClose(Device, kIOHIDOptionsTypeNone); + } + + CFRelease(Device); + Device = NULL; + + LogText("OVR::OSX::HIDDevice - HID Device Closed '%s'\n", DevDesc.Path.ToCStr()); +} + +void HIDDevice::staticHIDReportCallback(void* pContext, + IOReturn result, + void* pSender, + IOHIDReportType reportType, + uint32_t reportId, + uint8_t* pReport, + CFIndex reportLength) +{ + HIDDevice* pDevice = (HIDDevice*) pContext; + return pDevice->hidReportCallback(pReport, (UInt32)reportLength); +} + +void HIDDevice::hidReportCallback(UByte* pData, UInt32 length) +{ + + // We got data. + if (Handler) + { + Handler->OnInputReport(pData, length); + } +} + +void HIDDevice::staticDeviceRemovedCallback(void* pContext, IOReturn result, void* pSender) +{ + HIDDevice* pDevice = (HIDDevice*) pContext; + pDevice->deviceRemovedCallback(); +} + +void HIDDevice::deviceRemovedCallback() +{ + Ptr _this(this); // prevent from release + + Ptr existingHIDDev = HIDManager->DevManager->FindHIDDevice(DevDesc); + if (existingHIDDev && existingHIDDev->pDevice) + { + HIDManager->DevManager->CallOnDeviceRemoved(existingHIDDev); + } + closeDevice(true); +} + +CFStringRef HIDDevice::generateRunLoopModeString(IOHIDDeviceRef device) +{ + const UInt32 safeBuffSize = 256; + char nameBuff[safeBuffSize]; + OVR_sprintf(nameBuff, safeBuffSize, "%016lX", device); + + return CFStringCreateWithCString(NULL, nameBuff, kCFStringEncodingASCII); +} + +bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length) +{ + + if (!Device) + return false; + + UByte reportID = data[0]; + + if (reportID == 0) + { + // Not using reports so remove from data packet. + data++; + length--; + } + + IOReturn result = IOHIDDeviceSetReport( Device, + kIOHIDReportTypeFeature, + reportID, + data, + length); + + return (result == kIOReturnSuccess); +} + +bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length) +{ + if (!Device) + return false; + + CFIndex bufferLength = length; + + // Report id is in first byte of the buffer. + IOReturn result = IOHIDDeviceGetReport(Device, kIOHIDReportTypeFeature, data[0], data, &bufferLength); + + return (result == kIOReturnSuccess); +} + +UInt64 HIDDevice::OnTicks(UInt64 ticksMks) +{ + + if (Handler) + { + return Handler->OnTicks(ticksMks); + } + + return DeviceManagerThread::Notifier::OnTicks(ticksMks); +} + +HIDDeviceManager* HIDDeviceManager::CreateInternal(OSX::DeviceManager* devManager) +{ + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new OSX::HIDDeviceManager(devManager); + + if (manager) + { + if (manager->Initialize()) + { + manager->AddRef(); + } + else + { + manager.Clear(); + } + } + + return manager.GetPtr(); +} + +} // namespace OSX + +//------------------------------------------------------------------------------------- +// ***** Creation + +// Creates a new HIDDeviceManager and initializes OVR. +HIDDeviceManager* HIDDeviceManager::Create() +{ + OVR_ASSERT_LOG(false, ("Standalone mode not implemented yet.")); + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new OSX::HIDDeviceManager(NULL); + + if (manager) + { + if (manager->Initialize()) + { + manager->AddRef(); + } + else + { + manager.Clear(); + } + } + + return manager.GetPtr(); +} + +} // namespace OVR diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/osx/OVR_OSX_HIDDevice.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/osx/OVR_OSX_HIDDevice.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,149 @@ +/************************************************************************************ +Filename : OVR_OSX_HIDDevice.h +Content : OSX HID device implementation. +Created : February 26, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_OSX_HIDDevice_h +#define OVR_OSX_HIDDevice_h + +#include "OVR_HIDDevice.h" + +#include "OVR_OSX_DeviceManager.h" + +#include + +namespace OVR { namespace OSX { + +class HIDDeviceManager; + +//------------------------------------------------------------------------------------- +// ***** OSX HIDDevice + +class HIDDevice : public OVR::HIDDevice, public DeviceManagerThread::Notifier +{ +private: + friend class HIDDeviceManager; + +public: + HIDDevice(HIDDeviceManager* manager); + + // This is a minimal constructor used during enumeration for us to pass + // a HIDDevice to the visit function (so that it can query feature reports). + HIDDevice(HIDDeviceManager* manager, IOHIDDeviceRef device); + + virtual ~HIDDevice(); + + bool HIDInitialize(const String& path); + void HIDShutdown(); + + virtual bool SetFeatureReport(UByte* data, UInt32 length); + virtual bool GetFeatureReport(UByte* data, UInt32 length); + + bool Write(UByte* data, UInt32 length); + + bool Read(UByte* pData, UInt32 length, UInt32 timeoutMilliS); + bool ReadBlocking(UByte* pData, UInt32 length); + + + // DeviceManagerThread::Notifier + UInt64 OnTicks(UInt64 ticksMks); + +private: + bool initInfo(); + bool openDevice(); + void closeDevice(bool wasUnplugged); + bool setupDevicePluggedInNotification(); + CFStringRef generateRunLoopModeString(IOHIDDeviceRef device); + + static void staticHIDReportCallback(void* pContext, + IOReturn result, + void* pSender, + IOHIDReportType reportType, + uint32_t reportId, + uint8_t* pReport, + CFIndex reportLength); + void hidReportCallback(UByte* pData, UInt32 length); + + static void staticDeviceRemovedCallback(void* pContext, + IOReturn result, + void* pSender); + void deviceRemovedCallback(); + + static void staticDeviceAddedCallback(void* pContext, + io_iterator_t iterator); + void deviceAddedCallback(io_iterator_t iterator); + + bool InMinimalMode; + HIDDeviceManager* HIDManager; + IOHIDDeviceRef Device; + HIDDeviceDesc DevDesc; + + enum { ReadBufferSize = 96 }; + UByte ReadBuffer[ReadBufferSize]; + + UInt16 InputReportBufferLength; + UInt16 OutputReportBufferLength; + UInt16 FeatureReportBufferLength; + + IONotificationPortRef RepluggedNotificationPort; + io_iterator_t RepluggedNotification; +}; + + +//------------------------------------------------------------------------------------- +// ***** OSX HIDDeviceManager + +class HIDDeviceManager : public OVR::HIDDeviceManager +{ + friend class HIDDevice; + +public: + HIDDeviceManager(OSX::DeviceManager* Manager); + virtual ~HIDDeviceManager(); + + virtual bool Initialize(); + virtual void Shutdown(); + + virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor); + virtual OVR::HIDDevice* Open(const String& path); + + static HIDDeviceManager* CreateInternal(DeviceManager* manager); + +private: + CFRunLoopRef getRunLoop(); + bool initializeManager(); + bool initVendorProductVersion(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc); + bool initUsage(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc); + bool initStrings(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc); + bool initSerialNumber(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc); + bool getVendorId(IOHIDDeviceRef device, UInt16* pResult); + bool getProductId(IOHIDDeviceRef device, UInt16* pResult); + bool getLocationId(IOHIDDeviceRef device, SInt32* pResult); + bool getSerialNumberString(IOHIDDeviceRef device, String* pResult); + bool getPath(IOHIDDeviceRef device, String* pPath); + bool getIntProperty(IOHIDDeviceRef device, CFStringRef key, int32_t* pResult); + bool getStringProperty(IOHIDDeviceRef device, CFStringRef propertyName, String* pResult); + bool getFullDesc(IOHIDDeviceRef device, HIDDeviceDesc* desc); + + static void staticDeviceMatchingCallback(void *inContext, + IOReturn inResult, + void *inSender, + IOHIDDeviceRef inIOHIDDeviceRef); + + DeviceManager* DevManager; + + IOHIDManagerRef HIDManager; +}; + +}} // namespace OVR::OSX + +#endif // OVR_OSX_HIDDevice_h diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/osx/OVR_OSX_HMDDevice.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/osx/OVR_OSX_HMDDevice.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,400 @@ +/************************************************************************************ + +Filename : OVR_OSX_HMDDevice.cpp +Content : OSX Interface to HMD - detects HMD display +Created : September 21, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_OSX_HMDDevice.h" +#include +#include +#include +#include +#include + +namespace OVR { namespace OSX { + +//------------------------------------------------------------------------------------- + +HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory, + UInt32 vend, UInt32 prod, const String& displayDeviceName, long dispId) + : DeviceCreateDesc(factory, Device_HMD), + DisplayDeviceName(displayDeviceName), + DesktopX(0), DesktopY(0), Contents(0), + HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0), + DisplayId(dispId) +{ + /* //?????????? + char idstring[9]; + idstring[0] = 'A'-1+((vend>>10) & 31); + idstring[1] = 'A'-1+((vend>>5) & 31); + idstring[2] = 'A'-1+((vend>>0) & 31); + snprintf(idstring+3, 5, "%04d", prod); + DeviceId = idstring;*/ + DeviceId = DisplayDeviceName; +} + +HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other) + : DeviceCreateDesc(other.pFactory, Device_HMD), + DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName), + DesktopX(other.DesktopX), DesktopY(other.DesktopY), Contents(other.Contents), + HResolution(other.HResolution), VResolution(other.VResolution), + HScreenSize(other.HScreenSize), VScreenSize(other.VScreenSize), + DisplayId(other.DisplayId) +{ +} + +HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc** pcandidate) const +{ + if ((other.Type != Device_HMD) || (other.pFactory != pFactory)) + return Match_None; + + // There are several reasons we can come in here: + // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc + // - Require exact device DeviceId/DeviceName match + // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc + // - This DeviceId is empty; becomes candidate + // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc + // - This other.DeviceId is empty; becomes candidate + + const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; + + if ((DeviceId == s2.DeviceId) && + (DisplayId == s2.DisplayId)) + { + // Non-null DeviceId may match while size is different if screen size was overwritten + // by SensorDisplayInfo in prior iteration. + if (!DeviceId.IsEmpty() || + ((HScreenSize == s2.HScreenSize) && + (VScreenSize == s2.VScreenSize)) ) + { + *pcandidate = 0; + return Match_Found; + } + } + + + // DisplayInfo takes precedence, although we try to match it first. + if ((HResolution == s2.HResolution) && + (VResolution == s2.VResolution) && + (HScreenSize == s2.HScreenSize) && + (VScreenSize == s2.VScreenSize)) + { + if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty()) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + + *pcandidate = 0; + return Match_Found; + } + + // SensorDisplayInfo may override resolution settings, so store as candidiate. + if (s2.DeviceId.IsEmpty() && s2.DisplayId == 0) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + // OTHER HMD Monitor desc may initialize DeviceName/Id + else if (DeviceId.IsEmpty() && DisplayId == 0) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + + return Match_None; +} + + +bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other, bool* newDeviceFlag) +{ + // This candidate was the the "best fit" to apply sensor DisplayInfo to. + OVR_ASSERT(other.Type == Device_HMD); + + const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; + + // Force screen size on resolution from SensorDisplayInfo. + // We do this because USB detection is more reliable as compared to HDMI EDID, + // which may be corrupted by splitter reporting wrong monitor + if (s2.DeviceId.IsEmpty() && s2.DisplayId == 0) + { + // disconnected HMD: replace old descriptor by the 'fake' one. + HScreenSize = s2.HScreenSize; + VScreenSize = s2.VScreenSize; + Contents |= Contents_Screen; + + if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion) + { + memcpy(DistortionK, s2.DistortionK, sizeof(float)*4); + Contents |= Contents_Distortion; + } + DeviceId = s2.DeviceId; + DisplayId = s2.DisplayId; + DisplayDeviceName = s2.DisplayDeviceName; + if (newDeviceFlag) *newDeviceFlag = true; + } + else if (DeviceId.IsEmpty()) + { + // This branch is executed when 'fake' HMD descriptor is being replaced by + // the real one. + DeviceId = s2.DeviceId; + DisplayId = s2.DisplayId; + DisplayDeviceName = s2.DisplayDeviceName; + if (newDeviceFlag) *newDeviceFlag = true; + } + else + { + if (newDeviceFlag) *newDeviceFlag = false; + } + + return true; +} + + +//------------------------------------------------------------------------------------- + + +//------------------------------------------------------------------------------------- +// ***** HMDDeviceFactory + +HMDDeviceFactory HMDDeviceFactory::Instance; + +void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) +{ + CGDirectDisplayID Displays[32]; + uint32_t NDisplays = 0; + CGGetOnlineDisplayList(32, Displays, &NDisplays); + + for (int i = 0; i < NDisplays; i++) + { + io_service_t port = CGDisplayIOServicePort(Displays[i]); + CFDictionaryRef DispInfo = IODisplayCreateInfoDictionary(port, kIODisplayMatchingInfo); + + uint32_t vendor = CGDisplayVendorNumber(Displays[i]); + uint32_t product = CGDisplayModelNumber(Displays[i]); + unsigned mwidth = (unsigned)CGDisplayPixelsWide(Displays[i]); + unsigned mheight = (unsigned)CGDisplayPixelsHigh(Displays[i]); + CGRect desktop = CGDisplayBounds(Displays[i]); + + if (vendor == 16082 && product == 1) + { + char idstring[9]; + idstring[0] = 'A'-1+((vendor>>10) & 31); + idstring[1] = 'A'-1+((vendor>>5) & 31); + idstring[2] = 'A'-1+((vendor>>0) & 31); + snprintf(idstring+3, 5, "%04d", product); + + HMDDeviceCreateDesc hmdCreateDesc(this, vendor, product, idstring, Displays[i]); + + if (product == 2) + { + hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, + mwidth, mheight, 0.12096f, 0.06804f); + } + else + { + if (hmdCreateDesc.Is7Inch()) + { + // Physical dimension of SLA screen. + hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, + mwidth, mheight, 0.14976f, 0.0936f); + } + else + { + hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, + mwidth, mheight, 0.12096f, 0.0756f); + } + } + + OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %x:%x\n", vendor, product)); + + // Notify caller about detected device. This will call EnumerateAddDevice + // if the this is the first time device was detected. + visitor.Visit(hmdCreateDesc); + } + CFRelease(DispInfo); + } +} + +DeviceBase* HMDDeviceCreateDesc::NewDeviceInstance() +{ + return new HMDDevice(this); +} + +bool HMDDeviceCreateDesc::Is7Inch() const +{ + return (strstr(DeviceId.ToCStr(), "OVR0001") != 0) || (Contents & Contents_7Inch); +} + +Profile* HMDDeviceCreateDesc::GetProfileAddRef() const +{ + // Create device may override profile name, so get it from there is possible. + ProfileManager* profileManager = GetManagerImpl()->GetProfileManager(); + ProfileType profileType = GetProfileType(); + const char * profileName = pDevice ? + ((HMDDevice*)pDevice)->GetProfileName() : + profileManager->GetDefaultProfileName(profileType); + + return profileName ? + profileManager->LoadProfile(profileType, profileName) : + profileManager->GetDeviceDefaultProfile(profileType); +} + +bool HMDDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_HMD) && + (info->InfoClassType != Device_None)) + return false; + + bool is7Inch = Is7Inch(); + + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, + is7Inch ? "Oculus Rift DK1" : + ((HResolution >= 1920) ? "Oculus Rift DK HD" : "Oculus Rift DK1-Prototype") ); + OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, "Oculus VR"); + info->Type = Device_HMD; + info->Version = 0; + + // Display detection. + if (info->InfoClassType == Device_HMD) + { + HMDInfo* hmdInfo = static_cast(info); + + hmdInfo->DesktopX = DesktopX; + hmdInfo->DesktopY = DesktopY; + hmdInfo->HResolution = HResolution; + hmdInfo->VResolution = VResolution; + hmdInfo->HScreenSize = HScreenSize; + hmdInfo->VScreenSize = VScreenSize; + hmdInfo->VScreenCenter = VScreenSize * 0.5f; + hmdInfo->InterpupillaryDistance = 0.064f; // Default IPD; should be configurable. + hmdInfo->LensSeparationDistance = 0.0635f; + + // Obtain IPD from profile. + Ptr profile = *GetProfileAddRef(); + + if (profile) + { + hmdInfo->InterpupillaryDistance = profile->GetIPD(); + // TBD: Switch on EyeCup type. + } + + if (Contents & Contents_Distortion) + { + memcpy(hmdInfo->DistortionK, DistortionK, sizeof(float)*4); + } + else + { + if (is7Inch) + { + // 7" screen. + hmdInfo->DistortionK[0] = 1.0f; + hmdInfo->DistortionK[1] = 0.22f; + hmdInfo->DistortionK[2] = 0.24f; + hmdInfo->EyeToScreenDistance = 0.041f; + } + else + { + hmdInfo->DistortionK[0] = 1.0f; + hmdInfo->DistortionK[1] = 0.18f; + hmdInfo->DistortionK[2] = 0.115f; + + if (HResolution == 1920) + hmdInfo->EyeToScreenDistance = 0.040f; + else + hmdInfo->EyeToScreenDistance = 0.0387f; + } + + hmdInfo->ChromaAbCorrection[0] = 0.996f; + hmdInfo->ChromaAbCorrection[1] = -0.004f; + hmdInfo->ChromaAbCorrection[2] = 1.014f; + hmdInfo->ChromaAbCorrection[3] = 0.0f; + } + + OVR_strcpy(hmdInfo->DisplayDeviceName, sizeof(hmdInfo->DisplayDeviceName), + DisplayDeviceName.ToCStr()); + hmdInfo->DisplayId = DisplayId; + } + + return true; +} + +//------------------------------------------------------------------------------------- +// ***** HMDDevice + +HMDDevice::HMDDevice(HMDDeviceCreateDesc* createDesc) + : OVR::DeviceImpl(createDesc, 0) +{ +} +HMDDevice::~HMDDevice() +{ +} + +bool HMDDevice::Initialize(DeviceBase* parent) +{ + pParent = parent; + + // Initialize user profile to default for device. + ProfileManager* profileManager = GetManager()->GetProfileManager(); + ProfileName = profileManager->GetDefaultProfileName(getDesc()->GetProfileType()); + + return true; +} +void HMDDevice::Shutdown() +{ + ProfileName.Clear(); + pCachedProfile.Clear(); + pParent.Clear(); +} + +Profile* HMDDevice::GetProfile() const +{ + if (!pCachedProfile) + pCachedProfile = *getDesc()->GetProfileAddRef(); + return pCachedProfile.GetPtr(); +} + +const char* HMDDevice::GetProfileName() const +{ + return ProfileName.ToCStr(); +} + +bool HMDDevice::SetProfileName(const char* name) +{ + pCachedProfile.Clear(); + if (!name) + { + ProfileName.Clear(); + return 0; + } + if (GetManager()->GetProfileManager()->HasProfile(getDesc()->GetProfileType(), name)) + { + ProfileName = name; + return true; + } + return false; +} + +OVR::SensorDevice* HMDDevice::GetSensor() +{ + // Just return first sensor found since we have no way to match it yet. + OVR::SensorDevice* sensor = GetManager()->EnumerateDevices().CreateDevice(); + if (sensor) + sensor->SetCoordinateFrame(SensorDevice::Coord_HMD); + return sensor; +} + + +}} // namespace OVR::OSX + + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/osx/OVR_OSX_HMDDevice.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/osx/OVR_OSX_HMDDevice.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,160 @@ +/************************************************************************************ + +Filename : OVR_OSX_HMDDevice.h +Content : OSX HMDDevice implementation +Created : September 21, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_OSX_HMDDevice_h +#define OVR_OSX_HMDDevice_h + +#include "OVR_DeviceImpl.h" +#include +#include "OVR_Profile.h" + +namespace OVR { namespace OSX { + +class HMDDevice; + + +//------------------------------------------------------------------------------------- + +// HMDDeviceFactory enumerates attached Oculus HMD devices. +// +// This is currently done by matching monitor device strings. + +class HMDDeviceFactory : public DeviceFactory +{ +public: + static HMDDeviceFactory Instance; + + // Enumerates devices, creating and destroying relevant objects in manager. + virtual void EnumerateDevices(EnumerateVisitor& visitor); + +protected: + DeviceManager* getManager() const { return (DeviceManager*) pManager; } +}; + + +class HMDDeviceCreateDesc : public DeviceCreateDesc +{ + friend class HMDDevice; + +protected: + enum + { + Contents_Screen = 1, + Contents_Distortion = 2, + Contents_7Inch = 4, + }; + +public: + + HMDDeviceCreateDesc(DeviceFactory* factory, + UInt32 vendor, UInt32 product, const String& displayDeviceName, long dispId); + HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other); + + virtual DeviceCreateDesc* Clone() const + { + return new HMDDeviceCreateDesc(*this); + } + + virtual DeviceBase* NewDeviceInstance(); + + virtual MatchResult MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc**) const; + + virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL); + + virtual bool GetDeviceInfo(DeviceInfo* info) const; + + // Requests the currently used default profile. This profile affects the + // settings reported by HMDInfo. + Profile* GetProfileAddRef() const; + + ProfileType GetProfileType() const + { + return (HResolution >= 1920) ? Profile_RiftDKHD : Profile_RiftDK1; + } + + void SetScreenParameters(int x, int y, unsigned hres, unsigned vres, float hsize, float vsize) + { + DesktopX = x; + DesktopY = y; + HResolution = hres; + VResolution = vres; + HScreenSize = hsize; + VScreenSize = vsize; + Contents |= Contents_Screen; + } + + void SetDistortion(const float* dks) + { + for (int i = 0; i < 4; i++) + DistortionK[i] = dks[i]; + Contents |= Contents_Distortion; + } + + void Set7Inch() { Contents |= Contents_7Inch; } + + bool Is7Inch() const; + +protected: + String DeviceId; + String DisplayDeviceName; + int DesktopX, DesktopY; + unsigned Contents; + unsigned HResolution, VResolution; + float HScreenSize, VScreenSize; + long DisplayId; + float DistortionK[4]; +}; + + +//------------------------------------------------------------------------------------- + +// HMDDevice represents an Oculus HMD device unit. An instance of this class +// is typically created from the DeviceManager. +// After HMD device is created, we its sensor data can be obtained by +// first creating a Sensor object and then wrappig it in SensorFusion. + +class HMDDevice : public DeviceImpl +{ +public: + HMDDevice(HMDDeviceCreateDesc* createDesc); + ~HMDDevice(); + + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + + // Requests the currently used default profile. This profile affects the + // settings reported by HMDInfo. + virtual Profile* GetProfile() const; + virtual const char* GetProfileName() const; + virtual bool SetProfileName(const char* name); + + // Query associated sensor. + virtual OVR::SensorDevice* GetSensor(); + +protected: + HMDDeviceCreateDesc* getDesc() const { return (HMDDeviceCreateDesc*)pCreateDesc.GetPtr(); } + + // User name for the profile used with this device. + String ProfileName; + mutable Ptr pCachedProfile; +}; + + +}} // namespace OVR::OSX + +#endif // OVR_OSX_HMDDevice_h + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/osx/OVR_OSX_SensorDevice.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/osx/OVR_OSX_SensorDevice.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,45 @@ +/************************************************************************************ + +Filename : OVR_OSX_SensorDevice.cpp +Content : OSX SensorDevice implementation +Created : March 14, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_OSX_HMDDevice.h" +#include "OVR_SensorImpl.h" +#include "OVR_DeviceImpl.h" + +namespace OVR { namespace OSX { + +} // namespace OSX + +//------------------------------------------------------------------------------------- +void SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo( const SensorDisplayInfoImpl& displayInfo, + DeviceFactory::EnumerateVisitor& visitor) +{ + + OSX::HMDDeviceCreateDesc hmdCreateDesc(&OSX::HMDDeviceFactory::Instance, 1, 1, "", 0); + + hmdCreateDesc.SetScreenParameters( 0, 0, + displayInfo.HResolution, displayInfo.VResolution, + displayInfo.HScreenSize, displayInfo.VScreenSize); + + if ((displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) == SensorDisplayInfoImpl::Base_Distortion) + hmdCreateDesc.SetDistortion(displayInfo.DistortionK); + if (displayInfo.HScreenSize > 0.14f) + hmdCreateDesc.Set7Inch(); + + visitor.Visit(hmdCreateDesc); +} + +} // namespace OVR + + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/osx/OVR_ThreadsPthread.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/osx/OVR_ThreadsPthread.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ + #include "OVR_Threads.h" #include "OVR_Hash.h" #ifdef OVR_ENABLE_THREADS #include "OVR_Timer.h" #include "OVR_Log.h" #include #include #ifdef OVR_OS_PS3 #include #include #include #define sleep(x) sys_timer_sleep(x) #define usleep(x) sys_timer_usleep(x) using std::timespec; #else #include #include #include #endif 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) { 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_ASSERT(pthread_self() == LockedBy && LockCount > 0); unsigned lockCount; LockCount--; lockCount = LockCount; pthread_mutex_unlock(&SMutex); } bool MutexImpl::IsLockedByAnotherThread(Mutex* 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; ipImpl->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; #ifdef OVR_OS_PS3 sys_time_sec_t s; sys_time_nsec_t ns; sys_time_get_current_time(&s, &ns); ts.tv_sec = s + (delay / 1000); ts.tv_nsec = ns + (delay % 1000) * 1000000; #else struct timeval tv; gettimeofday(&tv, 0); ts.tv_sec = tv.tv_sec + (delay / 1000); ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000; #endif 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; iDoLock(); // 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 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; } /* 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 */ // ***** 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 (void*) result; } int Thread::InitAttr = 0; pthread_attr_t Thread::Attr; /* static */ int Thread::GetOSPriority(ThreadPriority p) //static inline int MapToSystemPrority(Thread::ThreadPriority p) { #ifdef OVR_OS_PS3 switch(p) { case Thread::CriticalPriority: return 0; case Thread::HighestPriority: return 300; case Thread::AboveNormalPriority: return 600; case Thread::NormalPriority: return 1000; case Thread::BelowNormalPriority: return 1500; case Thread::LowestPriority: return 2500; case Thread::IdlePriority: return 3071; } return 1000; #else OVR_UNUSED(p); return -1; #endif } 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((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; } #ifdef OVR_OS_PS3 sys_lwmutex_attribute_t Lock::LockAttr = { SYS_SYNC_PRIORITY, SYS_SYNC_RECURSIVE }; #endif } #endif // OVR_ENABLE_THREADS \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/win32/OVR_ThreadsWinAPI.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/win32/OVR_ThreadsWinAPI.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,1 @@ +/************************************************************************************ Filename : OVR_ThreadsWinAPI.cpp Platform : WinAPI Content : Windows specific thread-related (safe) functionality Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_Threads.h" #include "OVR_Hash.h" #include "OVR_Log.h" #ifdef OVR_ENABLE_THREADS // For _beginthreadex / _endtheadex #include namespace OVR { //----------------------------------------------------------------------------------- // *** Internal Mutex implementation class class MutexImpl : public NewOverrideBase { // System mutex or semaphore HANDLE hMutexOrSemaphore; bool Recursive; volatile unsigned LockCount; friend class WaitConditionImpl; public: // Constructor/destructor MutexImpl(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); }; // *** Constructor/destructor MutexImpl::MutexImpl(bool recursive) { Recursive = recursive; LockCount = 0; hMutexOrSemaphore = Recursive ? CreateMutex(NULL, 0, NULL) : CreateSemaphore(NULL, 1, 1, NULL); } MutexImpl::~MutexImpl() { CloseHandle(hMutexOrSemaphore); } // Lock and try lock void MutexImpl::DoLock() { if (::WaitForSingleObject(hMutexOrSemaphore, INFINITE) != WAIT_OBJECT_0) return; LockCount++; } bool MutexImpl::TryLock() { DWORD ret; if ((ret=::WaitForSingleObject(hMutexOrSemaphore, 0)) != WAIT_OBJECT_0) return 0; LockCount++; return 1; } void MutexImpl::Unlock(Mutex* pmutex) { OVR_UNUSED(pmutex); unsigned lockCount; LockCount--; lockCount = LockCount; // Release mutex if ((Recursive ? ReleaseMutex(hMutexOrSemaphore) : ReleaseSemaphore(hMutexOrSemaphore, 1, NULL)) != 0) { // This used to call Wait handlers if lockCount == 0. } } bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) { // There could be multiple interpretations of IsLocked with respect to current thread if (LockCount == 0) return 0; if (!TryLock()) return 1; Unlock(pmutex); 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) { pImpl = new MutexImpl(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(); } //----------------------------------------------------------------------------------- // ***** Win32 Wait Condition Implementation // Internal implementation class class WaitConditionImpl : public NewOverrideBase { // Event pool entries for extra events struct EventPoolEntry : public NewOverrideBase { HANDLE hEvent; EventPoolEntry *pNext; EventPoolEntry *pPrev; }; Lock WaitQueueLoc; // Stores free events that can be used later EventPoolEntry * pFreeEventList; // A queue of waiting objects to be signaled EventPoolEntry* pQueueHead; EventPoolEntry* pQueueTail; // Allocation functions for free events EventPoolEntry* GetNewEvent(); void ReleaseEvent(EventPoolEntry* pevent); // Queue operations void QueuePush(EventPoolEntry* pentry); EventPoolEntry* QueuePop(); void QueueFindAndRemove(EventPoolEntry* pentry); public: // Constructor/destructor WaitConditionImpl(); ~WaitConditionImpl(); // Release mutex and wait for condition. The mutex is re-acqured 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() { pFreeEventList = 0; pQueueHead = pQueueTail = 0; } WaitConditionImpl::~WaitConditionImpl() { // Free all the resources EventPoolEntry* p = pFreeEventList; EventPoolEntry* pentry; while(p) { // Move to next pentry = p; p = p->pNext; // Delete old ::CloseHandle(pentry->hEvent); delete pentry; } // Shouldn't we also consider the queue? // To be safe pFreeEventList = 0; pQueueHead = pQueueTail = 0; } // Allocation functions for free events WaitConditionImpl::EventPoolEntry* WaitConditionImpl::GetNewEvent() { EventPoolEntry* pentry; // If there are any free nodes, use them if (pFreeEventList) { pentry = pFreeEventList; pFreeEventList = pFreeEventList->pNext; } else { // Allocate a new node pentry = new EventPoolEntry; pentry->pNext = 0; pentry->pPrev = 0; // Non-signaled manual event pentry->hEvent = ::CreateEvent(NULL, TRUE, 0, NULL); } return pentry; } void WaitConditionImpl::ReleaseEvent(EventPoolEntry* pevent) { // Mark event as non-signaled ::ResetEvent(pevent->hEvent); // And add it to free pool pevent->pNext = pFreeEventList; pevent->pPrev = 0; pFreeEventList = pevent; } // Queue operations void WaitConditionImpl::QueuePush(EventPoolEntry* pentry) { // Items already exist? Just add to tail if (pQueueTail) { pentry->pPrev = pQueueTail; pQueueTail->pNext = pentry; pentry->pNext = 0; pQueueTail = pentry; } else { // No items in queue pentry->pNext = pentry->pPrev = 0; pQueueHead = pQueueTail = pentry; } } WaitConditionImpl::EventPoolEntry* WaitConditionImpl::QueuePop() { EventPoolEntry* pentry = pQueueHead; // No items, null pointer if (pentry) { // More items after this one? just grab the first item if (pQueueHead->pNext) { pQueueHead = pentry->pNext; pQueueHead->pPrev = 0; } else { // Last item left pQueueTail = pQueueHead = 0; } } return pentry; } void WaitConditionImpl::QueueFindAndRemove(EventPoolEntry* pentry) { // Do an exhaustive search looking for an entry EventPoolEntry* p = pQueueHead; while(p) { // Entry found? Remove. if (p == pentry) { // Remove the node form the list // Prev link if (pentry->pPrev) pentry->pPrev->pNext = pentry->pNext; else pQueueHead = pentry->pNext; // Next link if (pentry->pNext) pentry->pNext->pPrev = pentry->pPrev; else pQueueTail = pentry->pPrev; // Done return; } // Move to next item p = p->pNext; } } bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay) { bool result = 0; unsigned i; unsigned lockCount = pmutex->pImpl->LockCount; EventPoolEntry* pentry; // Mutex must have been locked if (lockCount == 0) return 0; // Add an object to the wait queue WaitQueueLoc.DoLock(); QueuePush(pentry = GetNewEvent()); WaitQueueLoc.Unlock(); // Finally, release a mutex or semaphore if (pmutex->pImpl->Recursive) { // Release the recursive mutex N times pmutex->pImpl->LockCount = 0; for(i=0; ipImpl->hMutexOrSemaphore); } else { pmutex->pImpl->LockCount = 0; ::ReleaseSemaphore(pmutex->pImpl->hMutexOrSemaphore, 1, NULL); } // Note that there is a gap here between mutex.Unlock() and Wait(). However, // if notify() comes in at this point in the other thread it will set our // corresponding event so wait will just fall through, as expected. // Block and wait on the event DWORD waitResult = ::WaitForSingleObject(pentry->hEvent, (delay == OVR_WAIT_INFINITE) ? INFINITE : delay); /* repeat_wait: DWORD waitResult = ::MsgWaitForMultipleObjects(1, &pentry->hEvent, FALSE, (delay == OVR_WAIT_INFINITE) ? INFINITE : delay, QS_ALLINPUT); */ WaitQueueLoc.DoLock(); switch(waitResult) { case WAIT_ABANDONED: case WAIT_OBJECT_0: result = 1; // Wait was successful, therefore the event entry should already be removed // So just add entry back to a free list ReleaseEvent(pentry); break; /* case WAIT_OBJECT_0 + 1: // Messages in WINDOWS queue { MSG msg; PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE); WaitQueueLoc.Unlock(); goto repeat_wait; } break; */ default: // Timeout, our entry should still be in a queue QueueFindAndRemove(pentry); ReleaseEvent(pentry); } WaitQueueLoc.Unlock(); // Re-aquire the mutex for(i=0; iDoLock(); // Return the result return result; } // Notify a condition, releasing the least object in a queue void WaitConditionImpl::Notify() { Lock::Locker lock(&WaitQueueLoc); // Pop last entry & signal it EventPoolEntry* pentry = QueuePop(); if (pentry) ::SetEvent(pentry->hEvent); } // Notify a condition, releasing all objects waiting void WaitConditionImpl::NotifyAll() { Lock::Locker lock(&WaitQueueLoc); // Pop and signal all events // NOTE : There is no need to release the events, it's the waiters job to do so EventPoolEntry* pentry = QueuePop(); while (pentry) { ::SetEvent(pentry->hEvent); pentry = QueuePop(); } } // *** Actual implementation of WaitCondition WaitCondition::WaitCondition() { pImpl = new WaitConditionImpl; } WaitCondition::~WaitCondition() { delete pImpl; } // Wait without a mutex bool WaitCondition::Wait(Mutex *pmutex, unsigned delay) { return pImpl->Wait(pmutex, delay); } // Notification void WaitCondition::Notify() { pImpl->Notify(); } void WaitCondition::NotifyAll() { pImpl->NotifyAll(); } //----------------------------------------------------------------------------------- // ***** Thread Class // Per-thread variable // MA: Don't use TLS for now - portability issues with DLLs, etc. /* #if !defined(OVR_CC_MSVC) || (OVR_CC_MSVC < 1300) __declspec(thread) Thread* pCurrentThread = 0; #else #pragma data_seg(".tls$") __declspec(thread) Thread* pCurrentThread = 0; #pragma data_seg(".rwdata") #endif */ // *** Thread constructors. Thread::Thread(UPInt stackSize, int processor) { 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; IdValue = 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. CleanupSystemThread(); 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 tack all created threads class ThreadList : public NewOverrideBase { //------------------------------------------------------------------------ struct ThreadHashOp { UPInt operator()(const Thread* ptr) { return (((UPInt)ptr) >> 6) ^ (UPInt)ptr; } }; HashSet ThreadSet; Mutex ThreadMutex; WaitCondition ThreadsEmpty; // Track the root thread that created us. ThreadId 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(GetCurrentThreadId() == RootThreadId); Mutex::Locker lock(&ThreadMutex); while (ThreadSet.GetSize() != 0) ThreadsEmpty.Wait(&ThreadMutex); } public: ThreadList() { RootThreadId = GetCurrentThreadId(); } ~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; } /* MA: Don't use TLS for now. // 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; } */ // *** 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; } // ***** Thread management /* static */ int Thread::GetOSPriority(ThreadPriority p) { switch(p) { case Thread::CriticalPriority: return THREAD_PRIORITY_TIME_CRITICAL; case Thread::HighestPriority: return THREAD_PRIORITY_HIGHEST; case Thread::AboveNormalPriority: return THREAD_PRIORITY_ABOVE_NORMAL; case Thread::NormalPriority: return THREAD_PRIORITY_NORMAL; case Thread::BelowNormalPriority: return THREAD_PRIORITY_BELOW_NORMAL; case Thread::LowestPriority: return THREAD_PRIORITY_LOWEST; case Thread::IdlePriority: return THREAD_PRIORITY_IDLE; } return THREAD_PRIORITY_NORMAL; } // The actual first function called on thread start unsigned WINAPI Thread_Win32StartFn(void * phandle) { Thread * pthread = (Thread*)phandle; if (pthread->Processor != -1) { DWORD_PTR ret = SetThreadAffinityMask(GetCurrentThread(), (DWORD)pthread->Processor); if (ret == 0) OVR_DEBUG_LOG(("Could not set hardware processor for the thread")); } BOOL ret = ::SetThreadPriority(GetCurrentThread(), Thread::GetOSPriority(pthread->Priority)); if (ret == 0) OVR_DEBUG_LOG(("Could not set thread priority")); OVR_UNUSED(ret); // Ensure that ThreadId is assigned once thread is running, in case // beginthread hasn't filled it in yet. pthread->IdValue = (ThreadId)::GetCurrentThreadId(); DWORD 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 (unsigned) result; } 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; } // Free old thread handle before creating the new one CleanupSystemThread(); // AddRef to us until the thread is finished. AddRef(); ThreadList::AddRunningThread(this); ExitCode = 0; SuspendCount = 0; ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED; ThreadHandle = (HANDLE) _beginthreadex(0, (unsigned)StackSize, Thread_Win32StartFn, this, 0, (unsigned*)&IdValue); // Failed? Fail the function if (ThreadHandle == 0) { ThreadFlags = 0; Release(); ThreadList::RemoveRunningThread(this); return 0; } return 1; } // Suspend the thread until resumed bool Thread::Suspend() { // Can't suspend a thread that wasn't started if (!(ThreadFlags & OVR_THREAD_STARTED)) return 0; if (::SuspendThread(ThreadHandle) != 0xFFFFFFFF) { SuspendCount++; return 1; } return 0; } // Resumes currently suspended thread bool Thread::Resume() { // Can't suspend a thread that wasn't started if (!(ThreadFlags & OVR_THREAD_STARTED)) return 0; // Decrement count, and resume thread if it is 0 SInt32 oldCount = SuspendCount.ExchangeAdd_Acquire(-1); if (oldCount >= 1) { if (oldCount == 1) { if (::ResumeThread(ThreadHandle) != 0xFFFFFFFF) return 1; } else { return 1; } } return 0; } // Quits with an exit code void Thread::Exit(int exitCode) { // Can only exist the current thread. // MA: Don't use TLS for now. //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); // Call the exit function. _endthreadex((unsigned)exitCode); } void Thread::CleanupSystemThread() { if (ThreadHandle != 0) { ::CloseHandle(ThreadHandle); ThreadHandle = 0; } } // *** Sleep functions // static bool Thread::Sleep(unsigned secs) { ::Sleep(secs*1000); return 1; } // static bool Thread::MSleep(unsigned msecs) { ::Sleep(msecs); return 1; } void Thread::SetThreadName( const char* name ) { #if !defined(OVR_BUILD_SHIPPING) || defined(OVR_BUILD_PROFILING) // Looks ugly, but it is the recommended way to name a thread. typedef struct tagTHREADNAME_INFO { DWORD dwType; // Must be 0x1000 LPCSTR szName; // Pointer to name (in user address space) DWORD dwThreadID; // Thread ID (-1 for caller thread) DWORD dwFlags; // Reserved for future use; must be zero } THREADNAME_INFO; THREADNAME_INFO info; info.dwType = 0x1000; info.szName = name; info.dwThreadID = reinterpret_cast(GetThreadId()); info.dwFlags = 0; __try { #ifdef _WIN64 RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (const ULONG_PTR *)&info ); #else RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD *)&info ); #endif } __except( GetExceptionCode()==0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER ) { } #endif // OVR_BUILD_SHIPPING } // static int Thread::GetCPUCount() { SYSTEM_INFO sysInfo; GetSystemInfo(&sysInfo); return (int) sysInfo.dwNumberOfProcessors; } // Returns the unique Id of a thread it is called on, intended for // comparison purposes. ThreadId GetCurrentThreadId() { return (ThreadId)::GetCurrentThreadId(); } } // OVR #endif \ No newline at end of file diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/win32/OVR_Win32_DeviceManager.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/win32/OVR_Win32_DeviceManager.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,423 @@ +/************************************************************************************ + +Filename : OVR_Win32_DeviceManager.cpp +Content : Win32 implementation of DeviceManager. +Created : September 21, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Win32_DeviceManager.h" + +// Sensor & HMD Factories +#include "OVR_SensorImpl.h" +#include "OVR_LatencyTestImpl.h" +#include "OVR_Win32_HMDDevice.h" +#include "OVR_Win32_DeviceStatus.h" +#include "OVR_Win32_HIDDevice.h" + +#include "Kernel/OVR_Timer.h" +#include "Kernel/OVR_Std.h" +#include "Kernel/OVR_Log.h" + +DWORD Debug_WaitedObjectCount = 0; + +namespace OVR { namespace Win32 { + + +//------------------------------------------------------------------------------------- +// **** Win32::DeviceManager + +DeviceManager::DeviceManager() +{ + HidDeviceManager = *HIDDeviceManager::CreateInternal(this); +} + +DeviceManager::~DeviceManager() +{ + // make sure Shutdown was called. + OVR_ASSERT(!pThread); +} + +bool DeviceManager::Initialize(DeviceBase*) +{ + if (!DeviceManagerImpl::Initialize(0)) + return false; + + pThread = *new DeviceManagerThread(this); + if (!pThread || !pThread->Start()) + return false; + + pCreateDesc->pDevice = this; + LogText("OVR::DeviceManager - initialized.\n"); + return true; +} + +void DeviceManager::Shutdown() +{ + LogText("OVR::DeviceManager - shutting down.\n"); + + // Set Manager shutdown marker variable; this prevents + // any existing DeviceHandle objects from accessing device. + pCreateDesc->pLock->pManager = 0; + + // Push for thread shutdown *WITH NO WAIT*. + // This will have the following effect: + // - Exit command will get enqueued, which will be executed later on the thread itself. + // - Beyond this point, this DeviceManager object may be deleted by our caller. + // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will + // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued + // after pManager is null. + // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last + // reference to the thread object. + pThread->PushExitCommand(false); + pThread->DetachDeviceManager(); + pThread.Clear(); + + DeviceManagerImpl::Shutdown(); +} + +ThreadCommandQueue* DeviceManager::GetThreadQueue() +{ + return pThread; +} + +bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_Manager) && + (info->InfoClassType != Device_None)) + return false; + + info->Type = Device_Manager; + info->Version = 0; + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager"); + OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc."); + return true; +} + +DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args) +{ + // TBD: Can this be avoided in the future, once proper device notification is in place? + if (GetThreadId() != OVR::GetCurrentThreadId()) + { + pThread->PushCall((DeviceManagerImpl*)this, + &DeviceManager::EnumerateAllFactoryDevices, true); + } + else + DeviceManager::EnumerateAllFactoryDevices(); + + return DeviceManagerImpl::EnumerateDevicesEx(args); +} + +ThreadId DeviceManager::GetThreadId() const +{ + return pThread->GetThreadId(); +} + +bool DeviceManager::GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const +{ + if (GetHIDDeviceManager()) + return static_cast(GetHIDDeviceManager())->GetHIDDeviceDesc(path, pdevDesc); + return false; +} + + +//------------------------------------------------------------------------------------- +// ***** DeviceManager Thread + +DeviceManagerThread::DeviceManagerThread(DeviceManager* pdevMgr) + : Thread(ThreadStackSize), hCommandEvent(0), pDeviceMgr(pdevMgr) +{ + // Create a non-signaled manual-reset event. + hCommandEvent = ::CreateEvent(0, TRUE, FALSE, 0); + if (!hCommandEvent) + return; + + // Must add event before starting. + AddOverlappedEvent(0, hCommandEvent); + + // Create device messages object. + pStatusObject = *new DeviceStatus(this); +} + +DeviceManagerThread::~DeviceManagerThread() +{ + // Remove overlapped event [0], after thread service exit. + if (hCommandEvent) + { + RemoveOverlappedEvent(0, hCommandEvent); + ::CloseHandle(hCommandEvent); + hCommandEvent = 0; + } +} + +int DeviceManagerThread::Run() +{ + ThreadCommand::PopBuffer command; + + SetThreadName("OVR::DeviceManagerThread"); + LogText("OVR::DeviceManagerThread - running (ThreadId=0x%X).\n", GetThreadId()); + + if (!pStatusObject->Initialize()) + { + LogText("OVR::DeviceManagerThread - failed to initialize MessageObject.\n"); + } + + while(!IsExiting()) + { + // PopCommand will reset event on empty queue. + if (PopCommand(&command)) + { + command.Execute(); + } + else + { + DWORD eventIndex = 0; + do { + UPInt numberOfWaitHandles = WaitHandles.GetSize(); + Debug_WaitedObjectCount = (DWORD)numberOfWaitHandles; + + DWORD waitMs = INFINITE; + + // If devices have time-dependent logic registered, get the longest wait + // allowed based on current ticks. + if (!TicksNotifiers.IsEmpty()) + { + UInt64 ticksMks = Timer::GetTicks(); + DWORD waitAllowed; + + for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++) + { + waitAllowed = (DWORD)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs); + if (waitAllowed < waitMs) + waitMs = waitAllowed; + } + } + + // Wait for event signals or window messages. + eventIndex = MsgWaitForMultipleObjects((DWORD)numberOfWaitHandles, &WaitHandles[0], FALSE, waitMs, QS_ALLINPUT); + + if (eventIndex != WAIT_FAILED) + { + if (eventIndex == WAIT_TIMEOUT) + continue; + + // TBD: Does this ever apply? + OVR_ASSERT(eventIndex < WAIT_ABANDONED_0); + + if (eventIndex == WAIT_OBJECT_0) + { + // Handle [0] services commands. + break; + } + else if (eventIndex == WAIT_OBJECT_0 + numberOfWaitHandles) + { + // Handle Windows messages. + pStatusObject->ProcessMessages(); + } + else + { + // Notify waiting device that its event is signaled. + unsigned i = eventIndex - WAIT_OBJECT_0; + OVR_ASSERT(i < numberOfWaitHandles); + if (WaitNotifiers[i]) + WaitNotifiers[i]->OnOverlappedEvent(WaitHandles[i]); + } + } + + } while(eventIndex != WAIT_FAILED); + + } + } + + pStatusObject->ShutDown(); + + LogText("OVR::DeviceManagerThread - exiting (ThreadId=0x%X).\n", GetThreadId()); + return 0; +} + +bool DeviceManagerThread::AddOverlappedEvent(Notifier* notify, HANDLE hevent) +{ + WaitNotifiers.PushBack(notify); + WaitHandles.PushBack(hevent); + + OVR_ASSERT(WaitNotifiers.GetSize() <= MAXIMUM_WAIT_OBJECTS); + return true; +} + +bool DeviceManagerThread::RemoveOverlappedEvent(Notifier* notify, HANDLE hevent) +{ + // [0] is reserved for thread commands with notify of null, but we still + // can use this function to remove it. + for (UPInt i = 0; i < WaitNotifiers.GetSize(); i++) + { + if ((WaitNotifiers[i] == notify) && (WaitHandles[i] == hevent)) + { + WaitNotifiers.RemoveAt(i); + WaitHandles.RemoveAt(i); + return true; + } + } + return false; +} + +bool DeviceManagerThread::AddTicksNotifier(Notifier* notify) +{ + TicksNotifiers.PushBack(notify); + return true; +} + +bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify) +{ + for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++) + { + if (TicksNotifiers[i] == notify) + { + TicksNotifiers.RemoveAt(i); + return true; + } + } + return false; +} + +bool DeviceManagerThread::AddMessageNotifier(Notifier* notify) +{ + MessageNotifiers.PushBack(notify); + return true; +} + +bool DeviceManagerThread::RemoveMessageNotifier(Notifier* notify) +{ + for (UPInt i = 0; i < MessageNotifiers.GetSize(); i++) + { + if (MessageNotifiers[i] == notify) + { + MessageNotifiers.RemoveAt(i); + return true; + } + } + return false; +} + +bool DeviceManagerThread::OnMessage(MessageType type, const String& devicePath) +{ + Notifier::DeviceMessageType notifierMessageType = Notifier::DeviceMessage_DeviceAdded; + if (type == DeviceAdded) + { + } + else if (type == DeviceRemoved) + { + notifierMessageType = Notifier::DeviceMessage_DeviceRemoved; + } + else + { + OVR_ASSERT(false); + } + + bool error = false; + bool deviceFound = false; + for (UPInt i = 0; i < MessageNotifiers.GetSize(); i++) + { + if (MessageNotifiers[i] && + MessageNotifiers[i]->OnDeviceMessage(notifierMessageType, devicePath, &error)) + { + // The notifier belonged to a device with the specified device name so we're done. + deviceFound = true; + break; + } + } + if (type == DeviceAdded && !deviceFound) + { + Lock::Locker devMgrLock(&DevMgrLock); + // a new device was connected. Go through all device factories and + // try to detect the device using HIDDeviceDesc. + HIDDeviceDesc devDesc; + if (pDeviceMgr->GetHIDDeviceDesc(devicePath, &devDesc)) + { + Lock::Locker deviceLock(pDeviceMgr->GetLock()); + DeviceFactory* factory = pDeviceMgr->Factories.GetFirst(); + while(!pDeviceMgr->Factories.IsNull(factory)) + { + if (factory->DetectHIDDevice(pDeviceMgr, devDesc)) + { + deviceFound = true; + break; + } + factory = factory->pNext; + } + } + } + + if (!deviceFound && strstr(devicePath.ToCStr(), "#OVR00")) + { + Ptr pmgr; + { + Lock::Locker devMgrLock(&DevMgrLock); + pmgr = pDeviceMgr; + } + // HMD plugged/unplugged + // This is not a final solution to enumerate HMD devices and get + // a first available handle. This won't work with multiple rifts. + // @TODO (!AB) + pmgr->EnumerateDevices(); + } + + return !error; +} + +void DeviceManagerThread::DetachDeviceManager() +{ + Lock::Locker devMgrLock(&DevMgrLock); + pDeviceMgr = NULL; +} + +} // namespace Win32 + + +//------------------------------------------------------------------------------------- +// ***** Creation + + +// Creates a new DeviceManager and initializes OVR. +DeviceManager* DeviceManager::Create() +{ + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new Win32::DeviceManager; + + if (manager) + { + if (manager->Initialize(0)) + { + manager->AddFactory(&SensorDeviceFactory::Instance); + manager->AddFactory(&LatencyTestDeviceFactory::Instance); + manager->AddFactory(&Win32::HMDDeviceFactory::Instance); + + manager->AddRef(); + } + else + { + manager.Clear(); + } + + } + + return manager.GetPtr(); +} + + +} // namespace OVR + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/win32/OVR_Win32_DeviceManager.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/win32/OVR_Win32_DeviceManager.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,146 @@ +/************************************************************************************ + +Filename : OVR_Win32_DeviceManager.h +Content : Win32-specific DeviceManager header. +Created : September 21, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Win32_DeviceManager_h +#define OVR_Win32_DeviceManager_h + +#include "OVR_DeviceImpl.h" +#include "OVR_Win32_DeviceStatus.h" + +#include "Kernel/OVR_Timer.h" + + +namespace OVR { namespace Win32 { + +class DeviceManagerThread; + +//------------------------------------------------------------------------------------- +// ***** Win32 DeviceManager + +class DeviceManager : public DeviceManagerImpl +{ +public: + DeviceManager(); + ~DeviceManager(); + + // Initialize/Shutdowncreate and shutdown manger thread. + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + virtual ThreadCommandQueue* GetThreadQueue(); + virtual ThreadId GetThreadId() const; + + virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args); + + virtual bool GetDeviceInfo(DeviceInfo* info) const; + + // Fills HIDDeviceDesc by using the path. + // Returns 'true' if successful, 'false' otherwise. + bool GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const; + + Ptr pThread; +}; + +//------------------------------------------------------------------------------------- +// ***** Device Manager Background Thread + +class DeviceManagerThread : public Thread, public ThreadCommandQueue, public DeviceStatus::Notifier +{ + friend class DeviceManager; + enum { ThreadStackSize = 32 * 1024 }; +public: + DeviceManagerThread(DeviceManager* pdevMgr); + ~DeviceManagerThread(); + + virtual int Run(); + + // ThreadCommandQueue notifications for CommandEvent handling. + virtual void OnPushNonEmpty_Locked() { ::SetEvent(hCommandEvent); } + virtual void OnPopEmpty_Locked() { ::ResetEvent(hCommandEvent); } + + + // Notifier used for different updates (EVENT or regular timing or messages). + class Notifier + { + public: + // Called when overlapped I/O handle is signaled. + virtual void OnOverlappedEvent(HANDLE hevent) { OVR_UNUSED1(hevent); } + + // Called when timing ticks are updated. + // Returns the largest number of microseconds this function can + // wait till next call. + virtual UInt64 OnTicks(UInt64 ticksMks) + { OVR_UNUSED1(ticksMks); return Timer::MksPerSecond * 1000; } + + enum DeviceMessageType + { + DeviceMessage_DeviceAdded = 0, + DeviceMessage_DeviceRemoved = 1, + }; + + // Called to notify device object. + virtual bool OnDeviceMessage(DeviceMessageType messageType, + const String& devicePath, + bool* error) + { OVR_UNUSED3(messageType, devicePath, error); return false; } + }; + + + // Adds device's OVERLAPPED structure for I/O. + // After it's added, Overlapped object will be signaled if a message arrives. + bool AddOverlappedEvent(Notifier* notify, HANDLE hevent); + bool RemoveOverlappedEvent(Notifier* notify, HANDLE hevent); + + // Add notifier that will be called at regular intervals. + bool AddTicksNotifier(Notifier* notify); + bool RemoveTicksNotifier(Notifier* notify); + + bool AddMessageNotifier(Notifier* notify); + bool RemoveMessageNotifier(Notifier* notify); + + // DeviceStatus::Notifier interface. + bool OnMessage(MessageType type, const String& devicePath); + + void DetachDeviceManager(); + +private: + bool threadInitialized() { return hCommandEvent != 0; } + + // Event used to wake us up thread commands are enqueued. + HANDLE hCommandEvent; + + // Event notifications for devices whose OVERLAPPED I/O we service. + // This list is modified through AddDeviceOverlappedEvent. + // WaitHandles[0] always == hCommandEvent, with null device. + Array WaitHandles; + Array WaitNotifiers; + + // Ticks notifiers - used for time-dependent events such as keep-alive. + Array TicksNotifiers; + + // Message notifiers. + Array MessageNotifiers; + + // Object that manages notifications originating from Windows messages. + Ptr pStatusObject; + + Lock DevMgrLock; + // pDeviceMgr should be accessed under DevMgrLock + DeviceManager* pDeviceMgr; // back ptr, no addref. +}; + +}} // namespace Win32::OVR + +#endif // OVR_Win32_DeviceManager_h diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/win32/OVR_Win32_DeviceStatus.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/win32/OVR_Win32_DeviceStatus.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,350 @@ +/************************************************************************************ + +Filename : OVR_Win32_DeviceStatus.cpp +Content : Win32 implementation of DeviceStatus. +Created : January 24, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Win32_DeviceStatus.h" + +#include "OVR_Win32_HIDDevice.h" + +#include "Kernel/OVR_Log.h" + +#include + +namespace OVR { namespace Win32 { + +static TCHAR windowClassName[] = TEXT("LibOVR_DeviceStatus_WindowClass"); + +//------------------------------------------------------------------------------------- +DeviceStatus::DeviceStatus(Notifier* const pClient) + : pNotificationClient(pClient), LastTimerId(0) +{ +} + +bool DeviceStatus::Initialize() +{ + + WNDCLASS wndClass; + wndClass.style = CS_HREDRAW | CS_VREDRAW; + wndClass.lpfnWndProc = WindowsMessageCallback; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = 0; + wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); + wndClass.lpszMenuName = NULL; + wndClass.lpszClassName = windowClassName; + + if (!RegisterClass(&wndClass)) + { + OVR_ASSERT_LOG(false, ("Failed to register window class.")); + return false; + } + + // We're going to create a 'message-only' window. This will be hidden, can't be enumerated etc. + // To do this we supply 'HWND_MESSAGE' as the hWndParent. + // http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only + hMessageWindow = CreateWindow( windowClassName, + windowClassName, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + HWND_MESSAGE, + NULL, + 0, + this); // Pass this object via the CREATESTRUCT mechanism + // so that we can attach it to the window user data. + + if (hMessageWindow == NULL) + { + OVR_ASSERT_LOG(false, ("Failed to create window.")); + return false; + } + + // According to MS, topmost windows receive WM_DEVICECHANGE faster. + ::SetWindowPos(hMessageWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); + UpdateWindow(hMessageWindow); + + + // Register notification for additional HID messages. + HIDDeviceManager* hidDeviceManager = new HIDDeviceManager(NULL); + HidGuid = hidDeviceManager->GetHIDGuid(); + hidDeviceManager->Release(); + + DEV_BROADCAST_DEVICEINTERFACE notificationFilter; + + ZeroMemory(¬ificationFilter, sizeof(notificationFilter)); + notificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); + notificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + //notificationFilter.dbcc_classguid = hidguid; + + // We need DEVICE_NOTIFY_ALL_INTERFACE_CLASSES to detect + // HDMI plug/unplug events. + hDeviceNotify = RegisterDeviceNotification( + hMessageWindow, + ¬ificationFilter, + DEVICE_NOTIFY_ALL_INTERFACE_CLASSES|DEVICE_NOTIFY_WINDOW_HANDLE); + + if (hDeviceNotify == NULL) + { + OVR_ASSERT_LOG(false, ("Failed to register for device notifications.")); + return false; + } + + return true; +} + +void DeviceStatus::ShutDown() +{ + OVR_ASSERT(hMessageWindow); + + if (!UnregisterDeviceNotification(hDeviceNotify)) + { + OVR_ASSERT_LOG(false, ("Failed to unregister device notification.")); + } + + PostMessage(hMessageWindow, WM_CLOSE, 0, 0); + + while (hMessageWindow != NULL) + { + ProcessMessages(); + Sleep(1); + } + + if (!UnregisterClass(windowClassName, NULL)) + { + OVR_ASSERT_LOG(false, ("Failed to unregister window class.")); + } +} + +DeviceStatus::~DeviceStatus() +{ + OVR_ASSERT_LOG(hMessageWindow == NULL, ("Need to call 'ShutDown' from DeviceManagerThread.")); +} + +void DeviceStatus::ProcessMessages() +{ + OVR_ASSERT_LOG(hMessageWindow != NULL, ("Need to call 'Initialize' before first use.")); + + MSG msg; + + // Note WM_DEVICECHANGED messages are dispatched but not retrieved by PeekMessage. + // I think this is because they are pending, non-queued messages. + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +bool DeviceStatus::MessageCallback(WORD messageType, const String& devicePath) +{ + bool rv = true; + if (messageType == DBT_DEVICEARRIVAL) + { + rv = pNotificationClient->OnMessage(Notifier::DeviceAdded, devicePath); + } + else if (messageType == DBT_DEVICEREMOVECOMPLETE) + { + pNotificationClient->OnMessage(Notifier::DeviceRemoved, devicePath); + } + else + { + OVR_ASSERT(0); + } + return rv; +} + +void DeviceStatus::CleanupRecoveryTimer(UPInt index) +{ + ::KillTimer(hMessageWindow, RecoveryTimers[index].TimerId); + RecoveryTimers.RemoveAt(index); +} + +DeviceStatus::RecoveryTimerDesc* +DeviceStatus::FindRecoveryTimer(UINT_PTR timerId, UPInt* pindex) +{ + for (UPInt i = 0, n = RecoveryTimers.GetSize(); i < n; ++i) + { + RecoveryTimerDesc* pdesc = &RecoveryTimers[i]; + if (pdesc->TimerId == timerId) + { + *pindex = i; + return pdesc; + } + } + return NULL; +} + +void DeviceStatus::FindAndCleanupRecoveryTimer(const String& devicePath) +{ + for (UPInt i = 0, n = RecoveryTimers.GetSize(); i < n; ++i) + { + RecoveryTimerDesc* pdesc = &RecoveryTimers[i]; + if (pdesc->DevicePath.CompareNoCase(devicePath)) + { + CleanupRecoveryTimer(i); + break; + } + } +} + +LRESULT CALLBACK DeviceStatus::WindowsMessageCallback( HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + switch (message) + { + case WM_CREATE: + { + // Setup window user data with device status object pointer. + LPCREATESTRUCT create_struct = reinterpret_cast(lParam); + void *lpCreateParam = create_struct->lpCreateParams; + DeviceStatus *pDeviceStatus = reinterpret_cast(lpCreateParam); + + SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(pDeviceStatus)); + } + return 0; // Return 0 for successfully handled WM_CREATE. + + case WM_DEVICECHANGE: + { + WORD loword = LOWORD(wParam); + + if (loword != DBT_DEVICEARRIVAL && + loword != DBT_DEVICEREMOVECOMPLETE) + { + // Ignore messages other than device arrive and remove complete + // (we're not handling intermediate ones). + return TRUE; // Grant WM_DEVICECHANGE request. + } + + DEV_BROADCAST_DEVICEINTERFACE* hdr; + hdr = (DEV_BROADCAST_DEVICEINTERFACE*) lParam; + + if (hdr->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE) + { + // Ignore non interface device messages. + return TRUE; // Grant WM_DEVICECHANGE request. + } + + LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA); + OVR_ASSERT(userData != NULL); + + // Call callback on device messages object with the device path. + DeviceStatus* pDeviceStatus = (DeviceStatus*) userData; + String devicePath(hdr->dbcc_name); + + // check if HID device caused the event... + if (pDeviceStatus->HidGuid == hdr->dbcc_classguid) + { + // check if recovery timer is already running; stop it and + // remove it, if so. + pDeviceStatus->FindAndCleanupRecoveryTimer(devicePath); + + if (!pDeviceStatus->MessageCallback(loword, devicePath)) + { + // hmmm.... unsuccessful + if (loword == DBT_DEVICEARRIVAL) + { + // Windows sometimes may return errors ERROR_SHARING_VIOLATION and + // ERROR_FILE_NOT_FOUND when trying to open an USB device via + // CreateFile. Need to start a recovery timer that will try to + // re-open the device again. + OVR_DEBUG_LOG(("Adding failed, recovering through a timer...")); + UINT_PTR tid = ::SetTimer(hwnd, ++pDeviceStatus->LastTimerId, + USBRecoveryTimeInterval, NULL); + RecoveryTimerDesc rtDesc; + rtDesc.TimerId = tid; + rtDesc.DevicePath = devicePath; + rtDesc.NumAttempts= 0; + pDeviceStatus->RecoveryTimers.PushBack(rtDesc); + // wrap around the timer counter, avoid timerId == 0... + if (pDeviceStatus->LastTimerId + 1 == 0) + pDeviceStatus->LastTimerId = 0; + } + } + } + // Check if Oculus HDMI device was plugged/unplugged, preliminary + // filtering. (is there any way to get GUID? !AB) + //else if (strstr(devicePath.ToCStr(), "DISPLAY#")) + else if (strstr(devicePath.ToCStr(), "#OVR00")) + { + pDeviceStatus->MessageCallback(loword, devicePath); + } + } + return TRUE; // Grant WM_DEVICECHANGE request. + + case WM_TIMER: + { + if (wParam != 0) + { + LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA); + OVR_ASSERT(userData != NULL); + + // Call callback on device messages object with the device path. + DeviceStatus* pDeviceStatus = (DeviceStatus*) userData; + + // Check if we have recovery timer running (actually, we must be!) + UPInt rtIndex; + RecoveryTimerDesc* prtDesc = pDeviceStatus->FindRecoveryTimer(wParam, &rtIndex); + if (prtDesc) + { + if (pDeviceStatus->MessageCallback(DBT_DEVICEARRIVAL, prtDesc->DevicePath)) + { + OVR_DEBUG_LOG(("Recovered, adding is successful, cleaning up the timer...")); + // now it is successful, kill the timer and cleanup + pDeviceStatus->CleanupRecoveryTimer(rtIndex); + } + else + { + if (++prtDesc->NumAttempts >= MaxUSBRecoveryAttempts) + { + OVR_DEBUG_LOG(("Failed to recover USB after %d attempts, path = '%s', aborting...", + prtDesc->NumAttempts, prtDesc->DevicePath.ToCStr())); + pDeviceStatus->CleanupRecoveryTimer(rtIndex); + } + else + { + OVR_DEBUG_LOG(("Failed to recover USB, %d attempts, path = '%s'", + prtDesc->NumAttempts, prtDesc->DevicePath.ToCStr())); + } + } + } + } + } + return 0; + + case WM_CLOSE: + { + LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA); + OVR_ASSERT(userData != NULL); + DeviceStatus* pDeviceStatus = (DeviceStatus*) userData; + pDeviceStatus->hMessageWindow = NULL; + + DestroyWindow(hwnd); + } + return 0; // We processed the WM_CLOSE message. + + case WM_DESTROY: + PostQuitMessage(0); + return 0; // We processed the WM_DESTROY message. + } + + return DefWindowProc(hwnd, message, wParam, lParam); +} + +}} // namespace OVR::Win32 diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/win32/OVR_Win32_DeviceStatus.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/win32/OVR_Win32_DeviceStatus.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,101 @@ +/************************************************************************************ + +Filename : OVR_Win32_DeviceStatus.h +Content : Win32-specific DeviceStatus header. +Created : January 24, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Win32_DeviceStatus_h +#define OVR_Win32_DeviceStatus_h + +#include +#include "Kernel/OVR_String.h" +#include "Kernel/OVR_RefCount.h" +#include "Kernel/OVR_Array.h" + +namespace OVR { namespace Win32 { + +//------------------------------------------------------------------------------------- +// ***** DeviceStatus +// +// DeviceStatus abstracts the handling of windows messages of interest for +// example the WM_DEVICECHANGED message which occurs when a device is plugged/unplugged. +// The device manager thread creates an instance of this class and passes its pointer +// in the constructor. That thread is also responsible for periodically calling 'ProcessMessages' +// to process queued windows messages. The client is notified via the 'OnMessage' method +// declared in the 'DeviceMessages::Notifier' interface. +class DeviceStatus : public RefCountBase +{ +public: + + // Notifier used for device messages. + class Notifier + { + public: + enum MessageType + { + DeviceAdded = 0, + DeviceRemoved = 1, + }; + + virtual bool OnMessage(MessageType type, const String& devicePath) + { OVR_UNUSED2(type, devicePath); return true; } + }; + + DeviceStatus(Notifier* const pClient); + ~DeviceStatus(); + + void operator = (const DeviceStatus&); // No assignment implementation. + + bool Initialize(); + void ShutDown(); + + void ProcessMessages(); + +private: + enum + { + MaxUSBRecoveryAttempts = 20, + USBRecoveryTimeInterval = 500 // ms + }; + struct RecoveryTimerDesc + { + UINT_PTR TimerId; + String DevicePath; + unsigned NumAttempts; + }; + + static LRESULT CALLBACK WindowsMessageCallback( HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam); + + bool MessageCallback(WORD messageType, const String& devicePath); + + void CleanupRecoveryTimer(UPInt index); + RecoveryTimerDesc* FindRecoveryTimer(UINT_PTR timerId, UPInt* pindex); + void FindAndCleanupRecoveryTimer(const String& devicePath); + +private: // data + Notifier* const pNotificationClient; // Don't reference count a back-pointer. + + HWND hMessageWindow; + HDEVNOTIFY hDeviceNotify; + + UINT_PTR LastTimerId; + Array RecoveryTimers; + + GUID HidGuid; +}; + +}} // namespace OVR::Win32 + +#endif // OVR_Win32_DeviceStatus_h diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/win32/OVR_Win32_HIDDevice.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/win32/OVR_Win32_HIDDevice.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,637 @@ +/************************************************************************************ + +Filename : OVR_Win32_HIDDevice.cpp +Content : Win32 HID device implementation. +Created : February 22, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Win32_HIDDevice.h" +#include "OVR_Win32_DeviceManager.h" + +#include "Kernel/OVR_System.h" +#include "Kernel/OVR_Log.h" + +namespace OVR { namespace Win32 { + +//------------------------------------------------------------------------------------- +// HIDDevicePathWrapper is a simple class used to extract HID device file path +// through SetupDiGetDeviceInterfaceDetail. We use a class since this is a bit messy. +class HIDDevicePathWrapper +{ + SP_INTERFACE_DEVICE_DETAIL_DATA_A* pData; +public: + HIDDevicePathWrapper() : pData(0) { } + ~HIDDevicePathWrapper() { if (pData) OVR_FREE(pData); } + + const char* GetPath() const { return pData ? pData->DevicePath : 0; } + + bool InitPathFromInterfaceData(HDEVINFO hdevInfoSet, SP_DEVICE_INTERFACE_DATA* pidata); +}; + +bool HIDDevicePathWrapper::InitPathFromInterfaceData(HDEVINFO hdevInfoSet, SP_DEVICE_INTERFACE_DATA* pidata) +{ + DWORD detailSize = 0; + // SetupDiGetDeviceInterfaceDetailA returns "not enough buffer error code" + // doe size request. Just check valid size. + SetupDiGetDeviceInterfaceDetailA(hdevInfoSet, pidata, NULL, 0, &detailSize, NULL); + if (!detailSize || + ((pData = (SP_INTERFACE_DEVICE_DETAIL_DATA_A*)OVR_ALLOC(detailSize)) == 0)) + return false; + pData->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA_A); + + if (!SetupDiGetDeviceInterfaceDetailA(hdevInfoSet, pidata, pData, detailSize, NULL, NULL)) + return false; + return true; +} + + +//------------------------------------------------------------------------------------- +// **** Win32::DeviceManager + +HIDDeviceManager::HIDDeviceManager(DeviceManager* manager) + : Manager(manager) +{ + hHidLib = ::LoadLibraryA("hid.dll"); + OVR_ASSERT_LOG(hHidLib, ("Couldn't load Win32 'hid.dll'.")); + + OVR_RESOLVE_HIDFUNC(HidD_GetHidGuid); + OVR_RESOLVE_HIDFUNC(HidD_SetNumInputBuffers); + OVR_RESOLVE_HIDFUNC(HidD_GetFeature); + OVR_RESOLVE_HIDFUNC(HidD_SetFeature); + OVR_RESOLVE_HIDFUNC(HidD_GetAttributes); + OVR_RESOLVE_HIDFUNC(HidD_GetManufacturerString); + OVR_RESOLVE_HIDFUNC(HidD_GetProductString); + OVR_RESOLVE_HIDFUNC(HidD_GetSerialNumberString); + OVR_RESOLVE_HIDFUNC(HidD_GetPreparsedData); + OVR_RESOLVE_HIDFUNC(HidD_FreePreparsedData); + OVR_RESOLVE_HIDFUNC(HidP_GetCaps); + + if (HidD_GetHidGuid) + HidD_GetHidGuid(&HidGuid); +} + +HIDDeviceManager::~HIDDeviceManager() +{ + ::FreeLibrary(hHidLib); +} + +bool HIDDeviceManager::Initialize() +{ + return true; +} + +void HIDDeviceManager::Shutdown() +{ + LogText("OVR::Win32::HIDDeviceManager - shutting down.\n"); +} + +bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor) +{ + HDEVINFO hdevInfoSet; + SP_DEVICE_INTERFACE_DATA interfaceData; + interfaceData.cbSize = sizeof(interfaceData); + + // Get handle to info data set describing all available HIDs. + hdevInfoSet = SetupDiGetClassDevsA(&HidGuid, NULL, NULL, DIGCF_INTERFACEDEVICE | DIGCF_PRESENT); + if (hdevInfoSet == INVALID_HANDLE_VALUE) + return false; + + for(int deviceIndex = 0; + SetupDiEnumDeviceInterfaces(hdevInfoSet, NULL, &HidGuid, deviceIndex, &interfaceData); + deviceIndex++) + { + // For each device, we extract its file path and open it to get attributes, + // such as vendor and product id. If anything goes wrong, we move onto next device. + HIDDevicePathWrapper pathWrapper; + if (!pathWrapper.InitPathFromInterfaceData(hdevInfoSet, &interfaceData)) + continue; + + // Look for the device to check if it is already opened. + Ptr existingDevice = Manager->FindDevice(pathWrapper.GetPath()); + // if device exists and it is opened then most likely the CreateHIDFile + // will fail; therefore, we just set Enumerated to 'true' and continue. + if (existingDevice && existingDevice->pDevice) + { + existingDevice->Enumerated = true; + continue; + } + + // open device in non-exclusive mode for detection... + HANDLE hidDev = CreateHIDFile(pathWrapper.GetPath(), false); + if (hidDev == INVALID_HANDLE_VALUE) + continue; + + HIDDeviceDesc devDesc; + devDesc.Path = pathWrapper.GetPath(); + if (initVendorProductVersion(hidDev, &devDesc) && + enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId) && + initUsage(hidDev, &devDesc)) + { + initStrings(hidDev, &devDesc); + + // Construct minimal device that the visitor callback can get feature reports from. + Win32::HIDDevice device(this, hidDev); + enumVisitor->Visit(device, devDesc); + } + + ::CloseHandle(hidDev); + } + + SetupDiDestroyDeviceInfoList(hdevInfoSet); + return true; +} + +bool HIDDeviceManager::GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const +{ + // open device in non-exclusive mode for detection... + HANDLE hidDev = CreateHIDFile(path, false); + if (hidDev == INVALID_HANDLE_VALUE) + return false; + + pdevDesc->Path = path; + getFullDesc(hidDev, pdevDesc); + + ::CloseHandle(hidDev); + return true; +} + +OVR::HIDDevice* HIDDeviceManager::Open(const String& path) +{ + + Ptr device = *new Win32::HIDDevice(this); + + if (device->HIDInitialize(path)) + { + device->AddRef(); + return device; + } + + return NULL; +} + +bool HIDDeviceManager::getFullDesc(HANDLE hidDev, HIDDeviceDesc* desc) const +{ + + if (!initVendorProductVersion(hidDev, desc)) + { + return false; + } + + if (!initUsage(hidDev, desc)) + { + return false; + } + + initStrings(hidDev, desc); + + return true; +} + +bool HIDDeviceManager::initVendorProductVersion(HANDLE hidDev, HIDDeviceDesc* desc) const +{ + HIDD_ATTRIBUTES attr; + attr.Size = sizeof(attr); + if (!HidD_GetAttributes(hidDev, &attr)) + return false; + desc->VendorId = attr.VendorID; + desc->ProductId = attr.ProductID; + desc->VersionNumber = attr.VersionNumber; + return true; +} + +bool HIDDeviceManager::initUsage(HANDLE hidDev, HIDDeviceDesc* desc) const +{ + bool result = false; + HIDP_CAPS caps; + HIDP_PREPARSED_DATA* preparsedData = 0; + + if (!HidD_GetPreparsedData(hidDev, &preparsedData)) + return false; + + if (HidP_GetCaps(preparsedData, &caps) == HIDP_STATUS_SUCCESS) + { + desc->Usage = caps.Usage; + desc->UsagePage = caps.UsagePage; + result = true; + } + HidD_FreePreparsedData(preparsedData); + return result; +} + +void HIDDeviceManager::initStrings(HANDLE hidDev, HIDDeviceDesc* desc) const +{ + // Documentation mentions 126 as being the max for USB. + wchar_t strBuffer[196]; + + // HidD_Get*String functions return nothing in buffer on failure, + // so it's ok to do this without further error checking. + strBuffer[0] = 0; + HidD_GetManufacturerString(hidDev, strBuffer, sizeof(strBuffer)); + desc->Manufacturer = strBuffer; + + strBuffer[0] = 0; + HidD_GetProductString(hidDev, strBuffer, sizeof(strBuffer)); + desc->Product = strBuffer; + + strBuffer[0] = 0; + HidD_GetSerialNumberString(hidDev, strBuffer, sizeof(strBuffer)); + desc->SerialNumber = strBuffer; +} + +//------------------------------------------------------------------------------------- +// **** Win32::HIDDevice + +HIDDevice::HIDDevice(HIDDeviceManager* manager) + : HIDManager(manager), inMinimalMode(false), Device(0), ReadRequested(false) +{ + memset(&ReadOverlapped, 0, sizeof(OVERLAPPED)); +} + +// This is a minimal constructor used during enumeration for us to pass +// a HIDDevice to the visit function (so that it can query feature reports). +HIDDevice::HIDDevice(HIDDeviceManager* manager, HANDLE device) + : HIDManager(manager), inMinimalMode(true), Device(device), ReadRequested(true) +{ + memset(&ReadOverlapped, 0, sizeof(OVERLAPPED)); +} + +HIDDevice::~HIDDevice() +{ + if (!inMinimalMode) + { + HIDShutdown(); + } +} + +bool HIDDevice::HIDInitialize(const String& path) +{ + + DevDesc.Path = path; + + if (!openDevice()) + { + LogText("OVR::Win32::HIDDevice - Failed to open HIDDevice: ", path); + return false; + } + + + HIDManager->Manager->pThread->AddTicksNotifier(this); + HIDManager->Manager->pThread->AddMessageNotifier(this); + + LogText("OVR::Win32::HIDDevice - Opened '%s'\n" + " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n", + DevDesc.Path.ToCStr(), + DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(), + DevDesc.SerialNumber.ToCStr()); + + return true; +} + +bool HIDDevice::initInfo() +{ + // Device must have been successfully opened. + OVR_ASSERT(Device); + + // Get report lengths. + HIDP_PREPARSED_DATA* preparsedData = 0; + if (!HIDManager->HidD_GetPreparsedData(Device, &preparsedData)) + { + return false; + } + + HIDP_CAPS caps; + if (HIDManager->HidP_GetCaps(preparsedData, &caps) != HIDP_STATUS_SUCCESS) + { + HIDManager->HidD_FreePreparsedData(preparsedData); + return false; + } + + InputReportBufferLength = caps.InputReportByteLength; + OutputReportBufferLength = caps.OutputReportByteLength; + FeatureReportBufferLength= caps.FeatureReportByteLength; + HIDManager->HidD_FreePreparsedData(preparsedData); + + if (ReadBufferSize < InputReportBufferLength) + { + OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer.")); + return false; + } + + // Get device desc. + if (!HIDManager->getFullDesc(Device, &DevDesc)) + { + OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device.")); + return false; + } + + return true; +} + +bool HIDDevice::openDevice() +{ + memset(&ReadOverlapped, 0, sizeof(OVERLAPPED)); + + Device = HIDManager->CreateHIDFile(DevDesc.Path.ToCStr()); + if (Device == INVALID_HANDLE_VALUE) + { + OVR_DEBUG_LOG(("Failed 'CreateHIDFile' while opening device, error = 0x%X.", + ::GetLastError())); + Device = 0; + return false; + } + + if (!HIDManager->HidD_SetNumInputBuffers(Device, 128)) + { + OVR_ASSERT_LOG(false, ("Failed 'HidD_SetNumInputBuffers' while initializing device.")); + ::CloseHandle(Device); + Device = 0; + return false; + } + + + // Create a manual-reset non-signaled event. + ReadOverlapped.hEvent = ::CreateEvent(0, TRUE, FALSE, 0); + + if (!ReadOverlapped.hEvent) + { + OVR_ASSERT_LOG(false, ("Failed to create event.")); + ::CloseHandle(Device); + Device = 0; + return false; + } + + if (!initInfo()) + { + OVR_ASSERT_LOG(false, ("Failed to get HIDDevice info.")); + + ::CloseHandle(ReadOverlapped.hEvent); + memset(&ReadOverlapped, 0, sizeof(OVERLAPPED)); + + ::CloseHandle(Device); + Device = 0; + return false; + } + + if (!initializeRead()) + { + OVR_ASSERT_LOG(false, ("Failed to get intialize read for HIDDevice.")); + + ::CloseHandle(ReadOverlapped.hEvent); + memset(&ReadOverlapped, 0, sizeof(OVERLAPPED)); + + ::CloseHandle(Device); + Device = 0; + return false; + } + + return true; +} + +void HIDDevice::HIDShutdown() +{ + + HIDManager->Manager->pThread->RemoveTicksNotifier(this); + HIDManager->Manager->pThread->RemoveMessageNotifier(this); + + closeDevice(); + LogText("OVR::Win32::HIDDevice - Closed '%s'\n", DevDesc.Path.ToCStr()); +} + +bool HIDDevice::initializeRead() +{ + + if (!ReadRequested) + { + HIDManager->Manager->pThread->AddOverlappedEvent(this, ReadOverlapped.hEvent); + ReadRequested = true; + } + + // Read resets the event... + while(::ReadFile(Device, ReadBuffer, InputReportBufferLength, 0, &ReadOverlapped)) + { + processReadResult(); + } + + if (GetLastError() != ERROR_IO_PENDING) + { + // Some other error (such as unplugged). + closeDeviceOnIOError(); + return false; + } + + return true; +} + +bool HIDDevice::processReadResult() +{ + + OVR_ASSERT(ReadRequested); + + DWORD bytesRead = 0; + + if (GetOverlappedResult(Device, &ReadOverlapped, &bytesRead, FALSE)) + { + // We've got data. + if (Handler) + { + Handler->OnInputReport(ReadBuffer, bytesRead); + } + + // TBD: Not needed? + // Event should be reset by Read call... + ReadOverlapped.Pointer = 0; + ReadOverlapped.Internal = 0; + ReadOverlapped.InternalHigh = 0; + return true; + } + else + { + if (GetLastError() != ERROR_IO_PENDING) + { + closeDeviceOnIOError(); + return false; + } + } + + return false; +} + +void HIDDevice::closeDevice() +{ + if (ReadRequested) + { + HIDManager->Manager->pThread->RemoveOverlappedEvent(this, ReadOverlapped.hEvent); + ReadRequested = false; + // Must call this to avoid Win32 assertion; CloseHandle is not enough. + ::CancelIo(Device); + } + + ::CloseHandle(ReadOverlapped.hEvent); + memset(&ReadOverlapped, 0, sizeof(OVERLAPPED)); + + ::CloseHandle(Device); + Device = 0; +} + +void HIDDevice::closeDeviceOnIOError() +{ + LogText("OVR::Win32::HIDDevice - Lost connection to '%s'\n", DevDesc.Path.ToCStr()); + closeDevice(); +} + +bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length) +{ + if (!ReadRequested) + return false; + + return HIDManager->HidD_SetFeature(Device, data, (ULONG) length) != FALSE; +} + +bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length) +{ + if (!ReadRequested) + return false; + + return HIDManager->HidD_GetFeature(Device, data, (ULONG) length) != FALSE; +} + +void HIDDevice::OnOverlappedEvent(HANDLE hevent) +{ + OVR_UNUSED(hevent); + OVR_ASSERT(hevent == ReadOverlapped.hEvent); + + if (processReadResult()) + { + // Proceed to read again. + initializeRead(); + } +} + +UInt64 HIDDevice::OnTicks(UInt64 ticksMks) +{ + if (Handler) + { + return Handler->OnTicks(ticksMks); + } + + return DeviceManagerThread::Notifier::OnTicks(ticksMks); +} + +bool HIDDevice::OnDeviceMessage(DeviceMessageType messageType, + const String& devicePath, + bool* error) +{ + + // Is this the correct device? + if (DevDesc.Path.CompareNoCase(devicePath) != 0) + { + return false; + } + + if (messageType == DeviceMessage_DeviceAdded && !Device) + { + // A closed device has been re-added. Try to reopen. + if (!openDevice()) + { + LogError("OVR::Win32::HIDDevice - Failed to reopen a device '%s' that was re-added.\n", devicePath.ToCStr()); + *error = true; + return true; + } + + LogText("OVR::Win32::HIDDevice - Reopened device '%s'\n", devicePath.ToCStr()); + } + + HIDHandler::HIDDeviceMessageType handlerMessageType = HIDHandler::HIDDeviceMessage_DeviceAdded; + if (messageType == DeviceMessage_DeviceAdded) + { + } + else if (messageType == DeviceMessage_DeviceRemoved) + { + handlerMessageType = HIDHandler::HIDDeviceMessage_DeviceRemoved; + } + else + { + OVR_ASSERT(0); + } + + if (Handler) + { + Handler->OnDeviceMessage(handlerMessageType); + } + + *error = false; + return true; +} + +HIDDeviceManager* HIDDeviceManager::CreateInternal(Win32::DeviceManager* devManager) +{ + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new Win32::HIDDeviceManager(devManager); + + if (manager) + { + if (manager->Initialize()) + { + manager->AddRef(); + } + else + { + manager.Clear(); + } + } + + return manager.GetPtr(); +} + +} // namespace Win32 + +//------------------------------------------------------------------------------------- +// ***** Creation + +// Creates a new HIDDeviceManager and initializes OVR. +HIDDeviceManager* HIDDeviceManager::Create() +{ + OVR_ASSERT_LOG(false, ("Standalone mode not implemented yet.")); + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new Win32::HIDDeviceManager(NULL); + + if (manager) + { + if (manager->Initialize()) + { + manager->AddRef(); + } + else + { + manager.Clear(); + } + } + + return manager.GetPtr(); +} + +} // namespace OVR diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/win32/OVR_Win32_HIDDevice.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/win32/OVR_Win32_HIDDevice.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,194 @@ +/************************************************************************************ + +Filename : OVR_Win32_HIDDevice.h +Content : Win32 HID device implementation. +Created : February 22, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Win32_HIDDevice_h +#define OVR_Win32_HIDDevice_h + +#include "OVR_HIDDevice.h" +#include "OVR_Win32_DeviceManager.h" + +#include +#include + +//------------------------------------------------------------------------------------- +// Define needed "hidsdi.h" functionality to avoid requiring DDK installation. +// #include "hidsdi.h" + +#ifndef _HIDSDI_H +#define _HIDSDI_H +#include + +#define HIDP_STATUS_SUCCESS (0x11 << 16) +struct HIDP_PREPARSED_DATA; + +struct HIDD_ATTRIBUTES +{ + ULONG Size; // = sizeof (struct _HIDD_ATTRIBUTES) + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +}; + +struct HIDP_CAPS +{ + USHORT Usage; + USHORT UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +}; + +#include +#endif + + +namespace OVR { namespace Win32 { + +class HIDDeviceManager; +class DeviceManager; + +//------------------------------------------------------------------------------------- +// ***** Win32 HIDDevice + +class HIDDevice : public OVR::HIDDevice, public DeviceManagerThread::Notifier +{ +public: + + HIDDevice(HIDDeviceManager* manager); + + // This is a minimal constructor used during enumeration for us to pass + // a HIDDevice to the visit function (so that it can query feature reports). + HIDDevice(HIDDeviceManager* manager, HANDLE device); + + ~HIDDevice(); + + bool HIDInitialize(const String& path); + void HIDShutdown(); + + // OVR::HIDDevice + bool SetFeatureReport(UByte* data, UInt32 length); + bool GetFeatureReport(UByte* data, UInt32 length); + + + // DeviceManagerThread::Notifier + void OnOverlappedEvent(HANDLE hevent); + UInt64 OnTicks(UInt64 ticksMks); + bool OnDeviceMessage(DeviceMessageType messageType, const String& devicePath, bool* error); + +private: + bool openDevice(); + bool initInfo(); + bool initializeRead(); + bool processReadResult(); + void closeDevice(); + void closeDeviceOnIOError(); + + bool inMinimalMode; + HIDDeviceManager* HIDManager; + HANDLE Device; + HIDDeviceDesc DevDesc; + + OVERLAPPED ReadOverlapped; + bool ReadRequested; + + enum { ReadBufferSize = 96 }; + UByte ReadBuffer[ReadBufferSize]; + + UInt16 InputReportBufferLength; + UInt16 OutputReportBufferLength; + UInt16 FeatureReportBufferLength; +}; + +//------------------------------------------------------------------------------------- +// ***** Win32 HIDDeviceManager + +class HIDDeviceManager : public OVR::HIDDeviceManager +{ + friend class HIDDevice; +public: + + HIDDeviceManager(DeviceManager* manager); + virtual ~HIDDeviceManager(); + + virtual bool Initialize(); + virtual void Shutdown(); + + virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor); + virtual OVR::HIDDevice* Open(const String& path); + + // Fills HIDDeviceDesc by using the path. + // Returns 'true' if successful, 'false' otherwise. + bool GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const; + + GUID GetHIDGuid() { return HidGuid; } + + static HIDDeviceManager* CreateInternal(DeviceManager* manager); + +private: + + DeviceManager* Manager; // Back pointer can just be a raw pointer. + + HMODULE hHidLib; + GUID HidGuid; + + // Macros to declare and resolve needed functions from library. +#define OVR_DECLARE_HIDFUNC(func, rettype, args) \ +typedef rettype (__stdcall *PFn_##func) args; \ +PFn_##func func; +#define OVR_RESOLVE_HIDFUNC(func) \ +func = (PFn_##func)::GetProcAddress(hHidLib, #func) + + OVR_DECLARE_HIDFUNC(HidD_GetHidGuid, void, (GUID *hidGuid)); + OVR_DECLARE_HIDFUNC(HidD_SetNumInputBuffers, BOOLEAN, (HANDLE hidDev, ULONG numberBuffers)); + OVR_DECLARE_HIDFUNC(HidD_GetFeature, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength)); + OVR_DECLARE_HIDFUNC(HidD_SetFeature, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength)); + OVR_DECLARE_HIDFUNC(HidD_GetAttributes, BOOLEAN, (HANDLE hidDev, HIDD_ATTRIBUTES *attributes)); + OVR_DECLARE_HIDFUNC(HidD_GetManufacturerString, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength)); + OVR_DECLARE_HIDFUNC(HidD_GetProductString, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength)); + OVR_DECLARE_HIDFUNC(HidD_GetSerialNumberString, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength)); + OVR_DECLARE_HIDFUNC(HidD_GetPreparsedData, BOOLEAN, (HANDLE hidDev, HIDP_PREPARSED_DATA **preparsedData)); + OVR_DECLARE_HIDFUNC(HidD_FreePreparsedData, BOOLEAN, (HIDP_PREPARSED_DATA *preparsedData)); + OVR_DECLARE_HIDFUNC(HidP_GetCaps, NTSTATUS,(HIDP_PREPARSED_DATA *preparsedData, HIDP_CAPS* caps)); + + HANDLE CreateHIDFile(const char* path, bool exclusiveAccess = true) const + { + return ::CreateFileA(path, GENERIC_WRITE|GENERIC_READ, + (!exclusiveAccess) ? (FILE_SHARE_READ|FILE_SHARE_WRITE) : 0x0, + NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + } + + // Helper functions to fill in HIDDeviceDesc from open device handle. + bool initVendorProductVersion(HANDLE hidDev, HIDDeviceDesc* desc) const; + bool initUsage(HANDLE hidDev, HIDDeviceDesc* desc) const; + void initStrings(HANDLE hidDev, HIDDeviceDesc* desc) const; + + bool getFullDesc(HANDLE hidDev, HIDDeviceDesc* desc) const; +}; + +}} // namespace OVR::Win32 + +#endif // OVR_Win32_HIDDevice_h diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/win32/OVR_Win32_HMDDevice.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/win32/OVR_Win32_HMDDevice.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,498 @@ +/************************************************************************************ + +Filename : OVR_Win32_HMDDevice.cpp +Content : Win32 Interface to HMD - detects HMD display +Created : September 21, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Win32_HMDDevice.h" + +#include "OVR_Win32_DeviceManager.h" + +#include + +namespace OVR { namespace Win32 { + +//------------------------------------------------------------------------------------- + +HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory, + const String& deviceId, const String& displayDeviceName) + : DeviceCreateDesc(factory, Device_HMD), + DeviceId(deviceId), DisplayDeviceName(displayDeviceName), + DesktopX(0), DesktopY(0), Contents(0), + HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0) +{ +} +HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other) + : DeviceCreateDesc(other.pFactory, Device_HMD), + DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName), + DesktopX(other.DesktopX), DesktopY(other.DesktopY), Contents(other.Contents), + HResolution(other.HResolution), VResolution(other.VResolution), + HScreenSize(other.HScreenSize), VScreenSize(other.VScreenSize) +{ +} + +HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc** pcandidate) const +{ + if ((other.Type != Device_HMD) || (other.pFactory != pFactory)) + return Match_None; + + // There are several reasons we can come in here: + // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc + // - Require exact device DeviceId/DeviceName match + // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc + // - This DeviceId is empty; becomes candidate + // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc + // - This other.DeviceId is empty; becomes candidate + + const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; + + if ((DeviceId == s2.DeviceId) && + (DisplayDeviceName == s2.DisplayDeviceName)) + { + // Non-null DeviceId may match while size is different if screen size was overwritten + // by SensorDisplayInfo in prior iteration. + if (!DeviceId.IsEmpty() || + ((HScreenSize == s2.HScreenSize) && + (VScreenSize == s2.VScreenSize)) ) + { + *pcandidate = 0; + return Match_Found; + } + } + + + // DisplayInfo takes precedence, although we try to match it first. + if ((HResolution == s2.HResolution) && + (VResolution == s2.VResolution) && + (HScreenSize == s2.HScreenSize) && + (VScreenSize == s2.VScreenSize)) + { + if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty()) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + + *pcandidate = 0; + return Match_Found; + } + + // SensorDisplayInfo may override resolution settings, so store as candidate. + if (s2.DeviceId.IsEmpty()) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + // OTHER HMD Monitor desc may initialize DeviceName/Id + else if (DeviceId.IsEmpty()) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + + return Match_None; +} + + +bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other, + bool* newDeviceFlag) +{ + // This candidate was the the "best fit" to apply sensor DisplayInfo to. + OVR_ASSERT(other.Type == Device_HMD); + + const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; + + // Force screen size on resolution from SensorDisplayInfo. + // We do this because USB detection is more reliable as compared to HDMI EDID, + // which may be corrupted by splitter reporting wrong monitor + if (s2.DeviceId.IsEmpty()) + { + HScreenSize = s2.HScreenSize; + VScreenSize = s2.VScreenSize; + Contents |= Contents_Screen; + + if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion) + { + memcpy(DistortionK, s2.DistortionK, sizeof(float)*4); + Contents |= Contents_Distortion; + } + DeviceId = s2.DeviceId; + DisplayDeviceName = s2.DisplayDeviceName; + DesktopX = s2.DesktopX; + DesktopY = s2.DesktopY; + if (newDeviceFlag) *newDeviceFlag = true; + } + else if (DeviceId.IsEmpty()) + { + DeviceId = s2.DeviceId; + DisplayDeviceName = s2.DisplayDeviceName; + DesktopX = s2.DesktopX; + DesktopY = s2.DesktopY; + + // ScreenSize and Resolution are NOT assigned here, since they may have + // come from a sensor DisplayInfo (which has precedence over HDMI). + + if (newDeviceFlag) *newDeviceFlag = true; + } + else + { + if (newDeviceFlag) *newDeviceFlag = false; + } + + return true; +} + +bool HMDDeviceCreateDesc::MatchDevice(const String& path) +{ + return DeviceId.CompareNoCase(path) == 0; +} + +//------------------------------------------------------------------------------------- + + +const wchar_t* FormatDisplayStateFlags(wchar_t* buff, int length, DWORD flags) +{ + buff[0] = 0; + if (flags & DISPLAY_DEVICE_ACTIVE) + wcscat_s(buff, length, L"Active "); + if (flags & DISPLAY_DEVICE_MIRRORING_DRIVER) + wcscat_s(buff, length, L"Mirroring_Driver "); + if (flags & DISPLAY_DEVICE_MODESPRUNED) + wcscat_s(buff, length, L"ModesPruned "); + if (flags & DISPLAY_DEVICE_PRIMARY_DEVICE) + wcscat_s(buff, length, L"Primary "); + if (flags & DISPLAY_DEVICE_REMOVABLE) + wcscat_s(buff, length, L"Removable "); + if (flags & DISPLAY_DEVICE_VGA_COMPATIBLE) + wcscat_s(buff, length, L"VGA_Compatible "); + return buff; +} + + +//------------------------------------------------------------------------------------- +// Callback for monitor enumeration to store all the monitor handles + +// Used to capture all the active monitor handles +struct MonitorSet +{ + enum { MaxMonitors = 8 }; + HMONITOR Monitors[MaxMonitors]; + int MonitorCount; +}; + +BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData) +{ + MonitorSet* monitorSet = (MonitorSet*)dwData; + if (monitorSet->MonitorCount > MonitorSet::MaxMonitors) + return FALSE; + + monitorSet->Monitors[monitorSet->MonitorCount] = hMonitor; + monitorSet->MonitorCount++; + return TRUE; +}; + +//------------------------------------------------------------------------------------- +// ***** HMDDeviceFactory + +HMDDeviceFactory HMDDeviceFactory::Instance; + +void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) +{ + MonitorSet monitors; + monitors.MonitorCount = 0; + // Get all the monitor handles + EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&monitors); + + bool foundHMD = false; + + // DeviceManager* manager = getManager(); + DISPLAY_DEVICE dd, ddm; + UINT i, j; + + for (i = 0; + (ZeroMemory(&dd, sizeof(dd)), dd.cb = sizeof(dd), + EnumDisplayDevices(0, i, &dd, 0)) != 0; i++) + { + + /* + wchar_t buff[500], flagsBuff[200]; + + swprintf_s(buff, 500, L"\nDEV: \"%s\" \"%s\" 0x%08x=%s\n \"%s\" \"%s\"\n", + dd.DeviceName, dd.DeviceString, + dd.StateFlags, FormatDisplayStateFlags(flagsBuff, 200, dd.StateFlags), + dd.DeviceID, dd.DeviceKey); + ::OutputDebugString(buff); + */ + + for (j = 0; + (ZeroMemory(&ddm, sizeof(ddm)), ddm.cb = sizeof(ddm), + EnumDisplayDevices(dd.DeviceName, j, &ddm, 0)) != 0; j++) + { + /* + wchar_t mbuff[500]; + swprintf_s(mbuff, 500, L"MON: \"%s\" \"%s\" 0x%08x=%s\n \"%s\" \"%s\"\n", + ddm.DeviceName, ddm.DeviceString, + ddm.StateFlags, FormatDisplayStateFlags(flagsBuff, 200, ddm.StateFlags), + ddm.DeviceID, ddm.DeviceKey); + ::OutputDebugString(mbuff); + */ + + // Our monitor hardware has string "RTD2205" in it + // Nate's device "CVT0003" + if (wcsstr(ddm.DeviceID, L"RTD2205") || + wcsstr(ddm.DeviceID, L"CVT0003") || + wcsstr(ddm.DeviceID, L"MST0030") || + wcsstr(ddm.DeviceID, L"OVR00") ) // Part of Oculus EDID. + { + String deviceId(ddm.DeviceID); + String displayDeviceName(ddm.DeviceName); + + // The default monitor coordinates + int mx = 0; + int my = 0; + int mwidth = 1280; + int mheight = 800; + + // Find the matching MONITORINFOEX for this device so we can get the + // screen coordinates + MONITORINFOEX info; + for (int m=0; m < monitors.MonitorCount; m++) + { + info.cbSize = sizeof(MONITORINFOEX); + GetMonitorInfo(monitors.Monitors[m], &info); + if (_tcsstr(ddm.DeviceName, info.szDevice) == ddm.DeviceName) + { // If the device name starts with the monitor name + // then we found the matching DISPLAY_DEVICE and MONITORINFO + // so we can gather the monitor coordinates + mx = info.rcMonitor.left; + my = info.rcMonitor.top; + //mwidth = info.rcMonitor.right - info.rcMonitor.left; + //mheight = info.rcMonitor.bottom - info.rcMonitor.top; + break; + } + } + + HMDDeviceCreateDesc hmdCreateDesc(this, deviceId, displayDeviceName); + + if (wcsstr(ddm.DeviceID, L"OVR0002")) + { + hmdCreateDesc.SetScreenParameters(mx, my, 1920, 1080, 0.12096f, 0.06804f); + } + else + { + if (hmdCreateDesc.Is7Inch()) + { + // Physical dimension of SLA screen. + hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.14976f, 0.0936f); + } + else + { + hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.12096f, 0.0756f); + } + } + + + OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %s - %s\n", + deviceId.ToCStr(), displayDeviceName.ToCStr())); + + // Notify caller about detected device. This will call EnumerateAddDevice + // if the this is the first time device was detected. + visitor.Visit(hmdCreateDesc); + foundHMD = true; + break; + } + } + } + + // Real HMD device is not found; however, we still may have a 'fake' HMD + // device created via SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo. + // Need to find it and set 'Enumerated' to true to avoid Removal notification. + if (!foundHMD) + { + Ptr hmdDevDesc = getManager()->FindDevice("", Device_HMD); + if (hmdDevDesc) + hmdDevDesc->Enumerated = true; + } +} + +DeviceBase* HMDDeviceCreateDesc::NewDeviceInstance() +{ + return new HMDDevice(this); +} + +bool HMDDeviceCreateDesc::Is7Inch() const +{ + return (strstr(DeviceId.ToCStr(), "OVR0001") != 0) || (Contents & Contents_7Inch); +} + +Profile* HMDDeviceCreateDesc::GetProfileAddRef() const +{ + // Create device may override profile name, so get it from there is possible. + ProfileManager* profileManager = GetManagerImpl()->GetProfileManager(); + ProfileType profileType = GetProfileType(); + const char * profileName = pDevice ? + ((HMDDevice*)pDevice)->GetProfileName() : + profileManager->GetDefaultProfileName(profileType); + + return profileName ? + profileManager->LoadProfile(profileType, profileName) : + profileManager->GetDeviceDefaultProfile(profileType); +} + + +bool HMDDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_HMD) && + (info->InfoClassType != Device_None)) + return false; + + bool is7Inch = Is7Inch(); + + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, + is7Inch ? "Oculus Rift DK1" : + ((HResolution >= 1920) ? "Oculus Rift DK HD" : "Oculus Rift DK1-Prototype") ); + OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, "Oculus VR"); + info->Type = Device_HMD; + info->Version = 0; + + // Display detection. + if (info->InfoClassType == Device_HMD) + { + HMDInfo* hmdInfo = static_cast(info); + + hmdInfo->DesktopX = DesktopX; + hmdInfo->DesktopY = DesktopY; + hmdInfo->HResolution = HResolution; + hmdInfo->VResolution = VResolution; + hmdInfo->HScreenSize = HScreenSize; + hmdInfo->VScreenSize = VScreenSize; + hmdInfo->VScreenCenter = VScreenSize * 0.5f; + hmdInfo->InterpupillaryDistance = 0.064f; // Default IPD; should be configurable. + hmdInfo->LensSeparationDistance = 0.0635f; + + // Obtain IPD from profile. + Ptr profile = *GetProfileAddRef(); + + if (profile) + { + hmdInfo->InterpupillaryDistance = profile->GetIPD(); + // TBD: Switch on EyeCup type. + } + + if (Contents & Contents_Distortion) + { + memcpy(hmdInfo->DistortionK, DistortionK, sizeof(float)*4); + } + else + { + if (is7Inch) + { + // 7" screen. + hmdInfo->DistortionK[0] = 1.0f; + hmdInfo->DistortionK[1] = 0.22f; + hmdInfo->DistortionK[2] = 0.24f; + hmdInfo->EyeToScreenDistance = 0.041f; + } + else + { + hmdInfo->DistortionK[0] = 1.0f; + hmdInfo->DistortionK[1] = 0.18f; + hmdInfo->DistortionK[2] = 0.115f; + + if (HResolution == 1920) + hmdInfo->EyeToScreenDistance = 0.040f; + else + hmdInfo->EyeToScreenDistance = 0.0387f; + } + + hmdInfo->ChromaAbCorrection[0] = 0.996f; + hmdInfo->ChromaAbCorrection[1] = -0.004f; + hmdInfo->ChromaAbCorrection[2] = 1.014f; + hmdInfo->ChromaAbCorrection[3] = 0.0f; + } + + OVR_strcpy(hmdInfo->DisplayDeviceName, sizeof(hmdInfo->DisplayDeviceName), + DisplayDeviceName.ToCStr()); + } + + return true; +} + +//------------------------------------------------------------------------------------- +// ***** HMDDevice + +HMDDevice::HMDDevice(HMDDeviceCreateDesc* createDesc) + : OVR::DeviceImpl(createDesc, 0) +{ +} +HMDDevice::~HMDDevice() +{ +} + +bool HMDDevice::Initialize(DeviceBase* parent) +{ + pParent = parent; + + // Initialize user profile to default for device. + ProfileManager* profileManager = GetManager()->GetProfileManager(); + ProfileName = profileManager->GetDefaultProfileName(getDesc()->GetProfileType()); + + return true; +} +void HMDDevice::Shutdown() +{ + ProfileName.Clear(); + pCachedProfile.Clear(); + pParent.Clear(); +} + +Profile* HMDDevice::GetProfile() const +{ + if (!pCachedProfile) + pCachedProfile = *getDesc()->GetProfileAddRef(); + return pCachedProfile.GetPtr(); +} + +const char* HMDDevice::GetProfileName() const +{ + return ProfileName.ToCStr(); +} + +bool HMDDevice::SetProfileName(const char* name) +{ + pCachedProfile.Clear(); + if (!name) + { + ProfileName.Clear(); + return 0; + } + if (GetManager()->GetProfileManager()->HasProfile(getDesc()->GetProfileType(), name)) + { + ProfileName = name; + return true; + } + return false; +} + +OVR::SensorDevice* HMDDevice::GetSensor() +{ + // Just return first sensor found since we have no way to match it yet. + OVR::SensorDevice* sensor = GetManager()->EnumerateDevices().CreateDevice(); + if (sensor) + sensor->SetCoordinateFrame(SensorDevice::Coord_HMD); + return sensor; +} + +}} // namespace OVR::Win32 + + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/win32/OVR_Win32_HMDDevice.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/win32/OVR_Win32_HMDDevice.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,157 @@ +/************************************************************************************ + +Filename : OVR_Win32_HMDDevice.h +Content : Win32 HMDDevice implementation +Created : September 21, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Win32_HMDDevice_h +#define OVR_Win32_HMDDevice_h + +#include "OVR_Win32_DeviceManager.h" +#include "OVR_Profile.h" + +namespace OVR { namespace Win32 { + +class HMDDevice; + + +//------------------------------------------------------------------------------------- + +// HMDDeviceFactory enumerates attached Oculus HMD devices. +// +// This is currently done by matching monitor device strings. + +class HMDDeviceFactory : public DeviceFactory +{ +public: + static HMDDeviceFactory Instance; + + // Enumerates devices, creating and destroying relevant objects in manager. + virtual void EnumerateDevices(EnumerateVisitor& visitor); + +protected: + DeviceManager* getManager() const { return (DeviceManager*) pManager; } +}; + + +class HMDDeviceCreateDesc : public DeviceCreateDesc +{ + friend class HMDDevice; + +protected: + enum + { + Contents_Screen = 1, + Contents_Distortion = 2, + Contents_7Inch = 4, + }; + String DeviceId; + String DisplayDeviceName; + int DesktopX, DesktopY; + unsigned Contents; + unsigned HResolution, VResolution; + float HScreenSize, VScreenSize; + float DistortionK[4]; + +public: + HMDDeviceCreateDesc(DeviceFactory* factory, + const String& deviceId, const String& displayDeviceName); + HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other); + + virtual DeviceCreateDesc* Clone() const + { + return new HMDDeviceCreateDesc(*this); + } + + virtual DeviceBase* NewDeviceInstance(); + + virtual MatchResult MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc**) const; + + // Matches device by path. + virtual bool MatchDevice(const String& path); + + virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL); + + virtual bool GetDeviceInfo(DeviceInfo* info) const; + + // Requests the currently used default profile. This profile affects the + // settings reported by HMDInfo. + Profile* GetProfileAddRef() const; + + ProfileType GetProfileType() const + { + return (HResolution >= 1920) ? Profile_RiftDKHD : Profile_RiftDK1; + } + + + void SetScreenParameters(int x, int y, unsigned hres, unsigned vres, float hsize, float vsize) + { + DesktopX = x; + DesktopY = y; + HResolution = hres; + VResolution = vres; + HScreenSize = hsize; + VScreenSize = vsize; + Contents |= Contents_Screen; + } + void SetDistortion(const float* dks) + { + for (int i = 0; i < 4; i++) + DistortionK[i] = dks[i]; + Contents |= Contents_Distortion; + } + + void Set7Inch() { Contents |= Contents_7Inch; } + + bool Is7Inch() const; +}; + + +//------------------------------------------------------------------------------------- + +// HMDDevice represents an Oculus HMD device unit. An instance of this class +// is typically created from the DeviceManager. +// After HMD device is created, we its sensor data can be obtained by +// first creating a Sensor object and then wrappig it in SensorFusion. + +class HMDDevice : public DeviceImpl +{ +public: + HMDDevice(HMDDeviceCreateDesc* createDesc); + ~HMDDevice(); + + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + // Requests the currently used default profile. This profile affects the + // settings reported by HMDInfo. + virtual Profile* GetProfile() const; + virtual const char* GetProfileName() const; + virtual bool SetProfileName(const char* name); + + // Query associated sensor. + virtual OVR::SensorDevice* GetSensor(); + +protected: + HMDDeviceCreateDesc* getDesc() const { return (HMDDeviceCreateDesc*)pCreateDesc.GetPtr(); } + + // User name for the profile used with this device. + String ProfileName; + mutable Ptr pCachedProfile; +}; + + +}} // namespace OVR::Win32 + +#endif // OVR_Win32_HMDDevice_h + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/win32/OVR_Win32_SensorDevice.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/win32/OVR_Win32_SensorDevice.cpp Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,47 @@ +/************************************************************************************ + +Filename : OVR_Win32_SensorDevice.cpp +Content : Win32 SensorDevice implementation +Created : March 12, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Win32_SensorDevice.h" + +#include "OVR_Win32_HMDDevice.h" +#include "OVR_SensorImpl.h" +#include "OVR_DeviceImpl.h" + +namespace OVR { namespace Win32 { + +} // namespace Win32 + +//------------------------------------------------------------------------------------- +void SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo + (const SensorDisplayInfoImpl& displayInfo, + DeviceFactory::EnumerateVisitor& visitor) +{ + + Win32::HMDDeviceCreateDesc hmdCreateDesc(&Win32::HMDDeviceFactory::Instance, String(), String()); + hmdCreateDesc.SetScreenParameters( 0, 0, + displayInfo.HResolution, displayInfo.VResolution, + displayInfo.HScreenSize, displayInfo.VScreenSize); + + if ((displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) == SensorDisplayInfoImpl::Base_Distortion) + hmdCreateDesc.SetDistortion(displayInfo.DistortionK); + if (displayInfo.HScreenSize > 0.14f) + hmdCreateDesc.Set7Inch(); + + visitor.Visit(hmdCreateDesc); +} + +} // namespace OVR + + diff -r c7b50cd7184c -r e2f9e4603129 libovr/Src/win32/OVR_Win32_SensorDevice.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libovr/Src/win32/OVR_Win32_SensorDevice.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,24 @@ +/************************************************************************************ + +Filename : OVR_Win32_SensorDevice.h +Content : Win32 SensorDevice implementation +Created : March 12, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Win32_SensorDevice_h +#define OVR_Win32_SensorDevice_h + +namespace OVR { namespace Win32 { + +}} // namespace OVR::Win32 + +#endif // OVR_Win32_SensorDevice_h + diff -r c7b50cd7184c -r e2f9e4603129 src/main.cc --- a/src/main.cc Fri Aug 30 06:08:34 2013 +0300 +++ b/src/main.cc Sat Sep 14 16:14:59 2013 +0300 @@ -1,5 +1,6 @@ #include #include +#include #include #include #ifdef __APPLE__ @@ -7,7 +8,7 @@ #else #include #endif -#include +#include "vr.h" #include "camera.h" static bool init(); @@ -17,14 +18,20 @@ static void reshape(int x, int y); static void keyb(unsigned char key, int x, int y); static void sball_rotate(int rx, int ry, int rz); +static bool parse_args(int argc, char **argv); static Camera cam; static int width, height; -static OVR::DeviceManager *ovr_devman; +static bool use_vr = false; int main(int argc, char **argv) { glutInit(&argc, argv); + + if(!parse_args(argc, argv)) { + return 1; + } + glutInitWindowSize(1280, 800); glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); glutCreateWindow("oculus test 01"); @@ -55,18 +62,15 @@ glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); - // initialize Oculus SDK - OVR::System::Init(); - if(!(ovr_devman = OVR::DeviceManager::Create())) { - fprintf(stderr, "failed to create OVR device manager\n"); + if(vr_init(VR_INIT_OCULUS) == -1) { return false; } - return true; } static void cleanup() { + vr_shutdown(); } static void disp() @@ -115,3 +119,23 @@ static void sball_rotate(int rx, int ry, int rz) { } + +static bool parse_args(int argc, char **argv) +{ + for(int i=1; i +#include "vr.h" +#include "vr_impl.h" + +static bool init_ovr(); + +VRData vr_data; + +extern "C" int vr_init(enum vr_init_mode mode) +{ + if(!init_ovr()) { + return -1; + } + return 0; +} + +extern "C" void vr_shutdown(void) +{ + System::Destroy(); +} + + + +static bool init_ovr() +{ + // initialize Oculus SDK + System::Init(); + if(!(vr_data.ovr_devman = DeviceManager::Create())) { + fprintf(stderr, "failed to create OVR device manager\n"); + return false; + } + + // create the display device + if(!(vr_data.ovr_hmd_dev = vr_data.ovr_devman->EnumerateDevices().CreateDevice())) { + fprintf(stderr, "no oculus rift devices found\n"); + return false; + } + + HMDInfo info; + if(vr_data.ovr_hmd_dev->GetDeviceInfo(&info)) { + printf("oculus device info:\n"); + printf(" name: %s\n", info.DisplayDeviceName); + printf(" ipd: %f\n", info.InterpupillaryDistance); + printf(" distortion: %f %f %f %f\n", info.DistortionK[0], + info.DistortionK[1], info.DistortionK[2], info.DistortionK[3]); + } + + // get the sensor device + if(!(vr_data.ovr_sensor_dev = vr_data.ovr_hmd_dev->GetSensor())) { + fprintf(stderr, "failed to get oculus sensor device\n"); + return false; + } + + SensorInfo sinfo; + if(vr_data.ovr_sensor_dev->GetDeviceInfo(&sinfo)) { + printf("oculus sensor device info:\n"); + printf(" name: %s\n", sinfo.ProductName); + } + + return true; +} diff -r c7b50cd7184c -r e2f9e4603129 src/vr.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vr.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,57 @@ +#ifndef VR_H_ +#define VR_H_ + +/* VR mode init options */ +enum vr_init_mode { + VR_INIT_NONE, + VR_INIT_OCULUS, + VR_INIT_STEREO +}; + +/* possible eye values */ +enum { + VR_EYE_CENTER, + VR_EYE_LEFT, + VR_EYE_RIGHT +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int vr_init(enum vr_init_mode mode); +void vr_shutdown(void); + +void vr_set_fov(float fov); +float vr_get_fov(void); + +void vr_set_aspect(float aspect); +float vr_get_aspect(void); + +void vr_set_eyedist(float ipd); +float vr_get_eyedist(void); + +/* expects an array of 4 barrel distortion coefficients: + * polar scale: k_0 + k_1 r^2 + k_2 r^4 + k_3 r^6 + */ +void vr_set_distort(const float *coef); +void vr_get_distort(float *coef); + +void vr_get_view_matrix(float *res, int eye); +void vr_get_proj_matrix(float *res, int eye); + +/* expects an array of at least 3 floats (x, y, z, offset). */ +void vr_get_translation(float *offs); +/* expects an array of at least 4 floats (x, y, z, w, quaternion). */ +void vr_get_rotation(float *quat); +/* expects an array of at least 3 floats (pitch, yaw, roll, angles). */ +void vr_get_rotation_euler(float *euler); + +/* OpenGL stuff */ +void vr_draw_eye(unsigned int tex, int eye); + +#ifdef __cplusplus +} +#endif + +#endif /* VR_H_ */ diff -r c7b50cd7184c -r e2f9e4603129 src/vr_impl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vr_impl.h Sat Sep 14 16:14:59 2013 +0300 @@ -0,0 +1,21 @@ +#ifndef VR_IMPL_H_ +#define VR_IMPL_H_ + +#include + +using namespace OVR; + +struct VRData { + DeviceManager *ovr_devman; + HMDDevice *ovr_hmd_dev; + SensorDevice *ovr_sensor_dev; + + float fov; + float aspect; + float ipd; + float distort[4]; +}; + +extern VRData vr_data; + +#endif // VR_IMPL_H_