From 5e067e0f4c4d4b5005346c4d5ea206e5bfcad8cd Mon Sep 17 00:00:00 2001 From: Dimitri Diakopoulos Date: Sat, 18 Jul 2015 21:32:10 -0700 Subject: [PATCH] initial with project --- .gitignore | 41 + COPYING | 0 LICENSE | 0 .../project.pbxproj | 285 + PointcloudViewer/PointcloudViewer/GfxUtil.cpp | 209 + PointcloudViewer/PointcloudViewer/GfxUtil.h | 89 + PointcloudViewer/PointcloudViewer/main.cpp | 362 + .../contents.xcworkspacedata | 10 + .../librealsense.xcodeproj/project.pbxproj | 347 + librealsense/librealsense/CameraContext.cpp | 5 + librealsense/librealsense/CameraContext.h | 69 + librealsense/librealsense/CameraHeader.h | 79 + librealsense/librealsense/CameraSPI.cpp | 138 + librealsense/librealsense/CameraSPI.h | 80 + librealsense/librealsense/CameraXU.cpp | 48 + librealsense/librealsense/CameraXU.h | 107 + readme.md | 0 third_party/catch/catch.hpp | 8997 +++++++++++++++++ third_party/glfw/bin/osx/libglfw3.a | Bin 0 -> 140640 bytes third_party/glfw/glfw3.h | 2631 +++++ third_party/glfw/glfw3native.h | 202 + third_party/libuvc/include/libuvc/libuvc.h | 718 ++ .../libuvc/include/libuvc/libuvc_config.h | 22 + .../libuvc/include/libuvc/libuvc_config.h.in | 22 + .../libuvc/include/libuvc/libuvc_internal.h | 296 + third_party/libuvc/include/utlist.h | 490 + third_party/libuvc/src/ctrl.c | 165 + third_party/libuvc/src/device.c | 1621 +++ third_party/libuvc/src/diag.c | 262 + third_party/libuvc/src/example.c | 215 + third_party/libuvc/src/frame-mjpeg.c | 187 + third_party/libuvc/src/frame.c | 371 + third_party/libuvc/src/init.c | 163 + third_party/libuvc/src/misc.c | 58 + third_party/libuvc/src/stream.c | 1249 +++ third_party/libuvc/src/test.c | 153 + 36 files changed, 19691 insertions(+) create mode 100644 .gitignore create mode 100644 COPYING create mode 100644 LICENSE create mode 100644 PointcloudViewer/PointcloudViewer.xcodeproj/project.pbxproj create mode 100644 PointcloudViewer/PointcloudViewer/GfxUtil.cpp create mode 100644 PointcloudViewer/PointcloudViewer/GfxUtil.h create mode 100644 PointcloudViewer/PointcloudViewer/main.cpp create mode 100644 librealsense.xcworkspace/contents.xcworkspacedata create mode 100644 librealsense/librealsense.xcodeproj/project.pbxproj create mode 100644 librealsense/librealsense/CameraContext.cpp create mode 100644 librealsense/librealsense/CameraContext.h create mode 100644 librealsense/librealsense/CameraHeader.h create mode 100644 librealsense/librealsense/CameraSPI.cpp create mode 100644 librealsense/librealsense/CameraSPI.h create mode 100644 librealsense/librealsense/CameraXU.cpp create mode 100644 librealsense/librealsense/CameraXU.h create mode 100644 readme.md create mode 100644 third_party/catch/catch.hpp create mode 100644 third_party/glfw/bin/osx/libglfw3.a create mode 100644 third_party/glfw/glfw3.h create mode 100644 third_party/glfw/glfw3native.h create mode 100644 third_party/libuvc/include/libuvc/libuvc.h create mode 100644 third_party/libuvc/include/libuvc/libuvc_config.h create mode 100644 third_party/libuvc/include/libuvc/libuvc_config.h.in create mode 100644 third_party/libuvc/include/libuvc/libuvc_internal.h create mode 100644 third_party/libuvc/include/utlist.h create mode 100644 third_party/libuvc/src/ctrl.c create mode 100644 third_party/libuvc/src/device.c create mode 100644 third_party/libuvc/src/diag.c create mode 100644 third_party/libuvc/src/example.c create mode 100644 third_party/libuvc/src/frame-mjpeg.c create mode 100644 third_party/libuvc/src/frame.c create mode 100644 third_party/libuvc/src/init.c create mode 100644 third_party/libuvc/src/misc.c create mode 100644 third_party/libuvc/src/stream.c create mode 100644 third_party/libuvc/src/test.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..d151f5e081 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# User-specific files +*.suo +*.user + +# Build results +[Dd]ebug/ +[Rr]elease/ +[Oo]bj/ + +*.ilk +*.obj +*.pch +*.pdb +*.log + +# Visual C++ cache files +ipch/ +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# XCode +.DS_Store +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.xcuserstate diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000..e69de29bb2 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..e69de29bb2 diff --git a/PointcloudViewer/PointcloudViewer.xcodeproj/project.pbxproj b/PointcloudViewer/PointcloudViewer.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..923b4ed929 --- /dev/null +++ b/PointcloudViewer/PointcloudViewer.xcodeproj/project.pbxproj @@ -0,0 +1,285 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 08567BB51B5B59E800EB6C0D /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 08567BB41B5B59E800EB6C0D /* main.cpp */; }; + 08567BD31B5B5A4A00EB6C0D /* librealsense.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 08567BD21B5B5A4A00EB6C0D /* librealsense.a */; }; + 08567BD71B5B5AA300EB6C0D /* GfxUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 08567BD51B5B5AA300EB6C0D /* GfxUtil.cpp */; }; + 08567C041B5B5D6B00EB6C0D /* libglfw3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 08567C031B5B5D6B00EB6C0D /* libglfw3.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 08567BAF1B5B59E800EB6C0D /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 08567BB11B5B59E800EB6C0D /* PointcloudViewer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = PointcloudViewer; sourceTree = BUILT_PRODUCTS_DIR; }; + 08567BB41B5B59E800EB6C0D /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; + 08567BD21B5B5A4A00EB6C0D /* librealsense.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = librealsense.a; path = ../librealsense/build/Debug/librealsense.a; sourceTree = ""; }; + 08567BD51B5B5AA300EB6C0D /* GfxUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GfxUtil.cpp; sourceTree = ""; }; + 08567BD61B5B5AA300EB6C0D /* GfxUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GfxUtil.h; sourceTree = ""; }; + 08567C031B5B5D6B00EB6C0D /* libglfw3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libglfw3.a; path = ../third_party/glfw/bin/osx/libglfw3.a; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 08567BAE1B5B59E800EB6C0D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 08567BD31B5B5A4A00EB6C0D /* librealsense.a in Frameworks */, + 08567C041B5B5D6B00EB6C0D /* libglfw3.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08567BA81B5B59E800EB6C0D = { + isa = PBXGroup; + children = ( + 08567BD21B5B5A4A00EB6C0D /* librealsense.a */, + 08567C031B5B5D6B00EB6C0D /* libglfw3.a */, + 08567BD41B5B5A5400EB6C0D /* third_party */, + 08567BB31B5B59E800EB6C0D /* PointcloudViewer */, + 08567BB21B5B59E800EB6C0D /* Products */, + ); + sourceTree = ""; + }; + 08567BB21B5B59E800EB6C0D /* Products */ = { + isa = PBXGroup; + children = ( + 08567BB11B5B59E800EB6C0D /* PointcloudViewer */, + ); + name = Products; + sourceTree = ""; + }; + 08567BB31B5B59E800EB6C0D /* PointcloudViewer */ = { + isa = PBXGroup; + children = ( + 08567BD51B5B5AA300EB6C0D /* GfxUtil.cpp */, + 08567BD61B5B5AA300EB6C0D /* GfxUtil.h */, + 08567BB41B5B59E800EB6C0D /* main.cpp */, + ); + path = PointcloudViewer; + sourceTree = ""; + }; + 08567BD41B5B5A5400EB6C0D /* third_party */ = { + isa = PBXGroup; + children = ( + ); + name = third_party; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 08567BB01B5B59E800EB6C0D /* PointcloudViewer */ = { + isa = PBXNativeTarget; + buildConfigurationList = 08567BB81B5B59E800EB6C0D /* Build configuration list for PBXNativeTarget "PointcloudViewer" */; + buildPhases = ( + 08567BAD1B5B59E800EB6C0D /* Sources */, + 08567BAE1B5B59E800EB6C0D /* Frameworks */, + 08567BAF1B5B59E800EB6C0D /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PointcloudViewer; + productName = PointcloudViewer; + productReference = 08567BB11B5B59E800EB6C0D /* PointcloudViewer */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08567BA91B5B59E800EB6C0D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0620; + ORGANIZATIONNAME = "Dimitri Diakopoulos"; + TargetAttributes = { + 08567BB01B5B59E800EB6C0D = { + CreatedOnToolsVersion = 6.2; + }; + }; + }; + buildConfigurationList = 08567BAC1B5B59E800EB6C0D /* Build configuration list for PBXProject "PointcloudViewer" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 08567BA81B5B59E800EB6C0D; + productRefGroup = 08567BB21B5B59E800EB6C0D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 08567BB01B5B59E800EB6C0D /* PointcloudViewer */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 08567BAD1B5B59E800EB6C0D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 08567BB51B5B59E800EB6C0D /* main.cpp in Sources */, + 08567BD71B5B5AA300EB6C0D /* GfxUtil.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 08567BB61B5B59E800EB6C0D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../third_party/libuvc/include", + "$(SRCROOT)/../third_party/glfw", + "$(SRCROOT)/../third_party/catch", + ); + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 08567BB71B5B59E800EB6C0D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../third_party/libuvc/include", + "$(SRCROOT)/../third_party/glfw", + "$(SRCROOT)/../third_party/catch", + ); + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 08567BB91B5B59E800EB6C0D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + /Users/ddiakopoulos/Desktop/uvcsense/librealsense/build/Debug, + /Users/ddiakopoulos/Desktop/uvcsense/third_party/glfw/bin/osx, + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 08567BBA1B5B59E800EB6C0D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + /Users/ddiakopoulos/Desktop/uvcsense/librealsense/build/Debug, + /Users/ddiakopoulos/Desktop/uvcsense/third_party/glfw/bin/osx, + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 08567BAC1B5B59E800EB6C0D /* Build configuration list for PBXProject "PointcloudViewer" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 08567BB61B5B59E800EB6C0D /* Debug */, + 08567BB71B5B59E800EB6C0D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 08567BB81B5B59E800EB6C0D /* Build configuration list for PBXNativeTarget "PointcloudViewer" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 08567BB91B5B59E800EB6C0D /* Debug */, + 08567BBA1B5B59E800EB6C0D /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08567BA91B5B59E800EB6C0D /* Project object */; +} diff --git a/PointcloudViewer/PointcloudViewer/GfxUtil.cpp b/PointcloudViewer/PointcloudViewer/GfxUtil.cpp new file mode 100644 index 0000000000..c53af4e349 --- /dev/null +++ b/PointcloudViewer/PointcloudViewer/GfxUtil.cpp @@ -0,0 +1,209 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "GfxUtil.h" + +void convert_yuyv_rgb(const uint8_t *src, int width, int height, uint8_t *dst) +{ + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; x += 2) + { + int y0 = src[0], u0 = src[1], y1 = src[2], v0 = src[3]; + + int c = y0 - 16, d = u0 - 128, e = v0 - 128; + dst[0] = clampbyte((298 * c + 409 * e + 128) >> 8); // r + dst[1] = clampbyte((298 * c - 100 * d - 208 * e + 128) >> 8); // g + dst[2] = clampbyte((298 * c + 516 * d + 128) >> 8); // b + + c = y1 - 16; + dst[3] = clampbyte((298 * c + 409 * e + 128) >> 8); // r + dst[4] = clampbyte((298 * c - 100 * d - 208 * e + 128) >> 8); // g + dst[5] = clampbyte((298 * c + 516 * d + 128) >> 8); // b + + src += 4; + dst += 6; + } + } +} + +void CheckGLError(const char* file, int32_t line) +{ + GLint error = glGetError(); + if (error) + { + const char* errorString = 0; + switch (error) + { + case GL_INVALID_ENUM: errorString = "GL_INVALID_ENUM"; break; + case GL_INVALID_VALUE: errorString = "GL_INVALID_VALUE"; break; + case GL_INVALID_OPERATION: errorString = "GL_INVALID_OPERATION"; break; + case GL_OUT_OF_MEMORY: errorString = "GL_OUT_OF_MEMORY"; break; + default: errorString = "unknown error"; break; + } + printf("GL error : %s, line %d : %s\n", file, line, errorString); + error = 0; + } +} + +void AddShader(GLuint program, GLenum type, const char * source) +{ + auto shader = glCreateShader(type); + glShaderSource(shader, 1, &source, nullptr); + glCompileShader(shader); + GLint status, length; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if(status == GL_FALSE) + { + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); + std::vector buffer(length); + glGetShaderInfoLog(shader, buffer.size(), nullptr, buffer.data()); + glDeleteShader(shader); + throw std::runtime_error(std::string("GLSL compile error: ") + buffer.data()); + } + glAttachShader(program, shader); + glDeleteShader(shader); +} + +GLuint CreateGLProgram(const std::string & vertSource, const std::string & fragSource) +{ + GLuint program = glCreateProgram(); + AddShader(program, GL_VERTEX_SHADER, vertSource.c_str()); + AddShader(program, GL_FRAGMENT_SHADER, fragSource.c_str()); + + glLinkProgram(program); + GLint status, length; + + glGetProgramiv(program, GL_LINK_STATUS, &status); + + if(status == GL_FALSE) + { + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length); + std::vector buffer(length); + glGetProgramInfoLog(program, buffer.size(), nullptr, buffer.data()); + throw std::runtime_error(std::string("GLSL link error: ") + buffer.data()); + } + + return program; + +} + +GLuint CreateTexture(int width, int height, GLint internalFmt) +{ + GLuint newTexId; + + glGenTextures(1, &newTexId); + + glBindTexture(GL_TEXTURE_2D, newTexId); + + glTexImage2D(GL_TEXTURE_2D, 0, internalFmt, width, height, 0, internalFmt, GL_UNSIGNED_BYTE, 0); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + return newTexId; +} + +void ConvertDepthToRGBUsingHistogram(uint8_t img[], const uint16_t depthImage[], int width, int height, const float nearHue, const float farHue) +{ + int histogram[256 * 256] = { 1 }; + + for (int i = 0; i < width * height; ++i) + if (auto d = depthImage[i]) ++histogram[d]; + + for (int i = 1; i < 256 * 256; i++) + histogram[i] += histogram[i - 1]; + + for (int i = 1; i < 256 * 256; i++) + histogram[i] = (histogram[i] << 8) / histogram[256 * 256 - 1]; + + auto rgb = img; + for (int i = 0; i < width * height; i++) + { + if (uint16_t d = depthImage[i]) + { + auto t = histogram[d]; // Use the histogram entry (in the range of [0-256]) to interpolate between nearColor and farColor + std::array returnRGB = { 0, 0, 0 }; + returnRGB = hsvToRgb(remapInt(t,nearHue, farHue), 1, 1); + *rgb++ = returnRGB[0]; + *rgb++ = returnRGB[1]; + *rgb++ = returnRGB[2]; + } + else + { + *rgb++ = 0; + *rgb++ = 0; + *rgb++ = 0; + } + } +} + +std::array rgbToHsv(uint8_t r, uint8_t g, uint8_t b) +{ + std::array hsv; + double rd = (double)r / 255; + double gd = (double)g / 255; + double bd = (double)b / 255; + + double max = util::max(rd, gd, bd), min = util::min(rd, gd, bd); + double h, s, v = max; + double d = max - min; + + s = max == 0 ? 0 : d / max; + + if (max == min) + { + h = 0; // achromatic + } + else + { + if (max == rd) + { + h = (gd - bd) / d + (gd < bd ? 6 : 0); + } + else if (max == gd) + { + h = (bd - rd) / d + 2; + } + else if (max == bd) + { + h = (rd - gd) / d + 4; + } + h /= 6; + } + + hsv[0] = h; + hsv[1] = s; + hsv[2] = v; + return hsv; +} + +std::array hsvToRgb(double h, double s, double v) { + + std::array rgb; + double r, g, b; + int i = int(h * 6); + double f = h * 6 - i; + double p = v * (1 - s); + double q = v * (1 - f * s); + double t = v * (1 - (1 - f) * s); + + switch (i % 6) + { + case 0: r = v, g = t, b = p; break; + case 1: r = q, g = v, b = p; break; + case 2: r = p, g = v, b = t; break; + case 3: r = p, g = q, b = v; break; + case 4: r = t, g = p, b = v; break; + case 5: r = v, g = p, b = q; break; + } + rgb[0] = uint8_t(clamp((float) r * 255.0f, 0.0f, 255.0f)); + rgb[1] = uint8_t(clamp((float) g * 255.0f, 0.0f, 255.0f)); + rgb[2] = uint8_t(clamp((float) b * 255.0f, 0.0f, 255.0f)); + return rgb; +} \ No newline at end of file diff --git a/PointcloudViewer/PointcloudViewer/GfxUtil.h b/PointcloudViewer/PointcloudViewer/GfxUtil.h new file mode 100644 index 0000000000..926476707a --- /dev/null +++ b/PointcloudViewer/PointcloudViewer/GfxUtil.h @@ -0,0 +1,89 @@ +#ifndef DS4OSX_Util_h +#define DS4OSX_Util_h + +#include +#include + +#ifndef CHECK_GL_ERROR +#define CHECK_GL_ERROR() CheckGLError(__FILE__, __LINE__) +#endif + +template +T max(T a, T b, T c) +{ + return std::max(a, std::max(b, c)); +} + +template +T min(T a, T b, T c) +{ + return std::min(a, std::min(b, c)); +} + +template T +clamp(T a, T mn, T mx) +{ + return std::max(std::min(a, mx), mn); +} + +template +inline T remapInt(T value, float outputMin, float outputMax) +{ + T invVal = 1.0f / (inputMax - inputMin); + T outVal = (invVal*(value - inputMin) * (outputMax - outputMin) + outputMin); + if (clamp) + { + if (outputMax < outputMin) + { + if (outVal < outputMax) outVal = outputMax; + else if (outVal > outputMin) outVal = outputMin; + } + else + { + if (outVal > outputMax) outVal = outputMax; + else if (outVal < outputMin) outVal = outputMin; + } + } + return outVal; +} + +template +inline T remap(T value, T inputMin, T inputMax, T outputMin, T outputMax, bool clamp) +{ + T outVal = ((value - inputMin) / (inputMax - inputMin) * (outputMax - outputMin) + outputMin); + if (clamp) + { + if (outputMax < outputMin) + { + if (outVal < outputMax) outVal = outputMax; + else if (outVal > outputMin) outVal = outputMin; + } + else + { + if (outVal > outputMax) outVal = outputMax; + else if (outVal < outputMin) outVal = outputMin; + } + } + return outVal; +} + +inline uint8_t clampbyte(int v) +{ + return v < 0 ? 0 : v > 255 ? 255 : v; +} + +void convert_yuyv_rgb(const uint8_t *src, int width, int height, uint8_t *dst); + +void CheckGLError(const char* file, int32_t line); + +GLuint CreateGLProgram(const std::string & vertSource, const std::string & fragSource); + +GLuint CreateTexture(int width, int height, GLint internalFmt); + +void ConvertDepthToRGBUsingHistogram(uint8_t img[], const uint16_t depthImage[], int width, int height, const float nearHue, const float farHue); + +std::array rgbToHsv(uint8_t r, uint8_t g, uint8_t b); + +std::array hsvToRgb(double h, double s, double v); + +#endif diff --git a/PointcloudViewer/PointcloudViewer/main.cpp b/PointcloudViewer/PointcloudViewer/main.cpp new file mode 100644 index 0000000000..54a9b6f7fe --- /dev/null +++ b/PointcloudViewer/PointcloudViewer/main.cpp @@ -0,0 +1,362 @@ +#include +#include +#include +#include +#include +#include +#include "Util.h" + +#include "libuvc/libuvc.h" + +#include "glfw3.h" + +#define GLFW_EXPOSE_NATIVE_COCOA +#define GLFW_EXPOSE_NATIVE_NSGL +#include "glfw3native.h" + +#include + +#include "CameraContext.h" +#include "CameraHeader.h" +#include "CameraSPI.h" +#include "CameraXU.h" +#include "GfxUtil.h" + +GLFWwindow * window; + +GLuint g_IvCamLocation; +GLuint g_DS4CamLocation; +GLuint textureHandle; + +uint16_t g_IvImg[640 * 480]; +uint16_t g_DsImg[628 * 469]; + +std::string vertShader = R"(#version 330 core +layout(location = 0) in vec3 position; +out vec2 texCoord; +void main() +{ + gl_Position = vec4(position, 1); + texCoord = (position.xy + vec2(1,1)) / 2.0; +} +)"; + +std::string fragShader = R"(#version 330 core +in vec2 texCoord; +out vec3 color; +uniform sampler2D u_image; +void main() +{ + color = texture(u_image, texCoord.st * vec2(1.0, -1.0)).rgb; +} +)"; + +/* +void rgbCallback(uvc_frame_t *frame, void *ptr) +{ + static int frameCount; + + std::cout << "rgb_frame_count: " << frameCount << std::endl; + + std::vector colorImg(frame->width * frame->height * 3); // YUY2 = 16 in -> 24 out + convert_yuyv_rgb((const uint8_t *)frame->data, frame->width, frame->height, colorImg.data()); + memcpy(g_rgbImage, colorImg.data(), 640 * 480 * 3); + + frameCount++; +} + */ + +void ivCallback(uvc_frame_t *frame, void *ptr) +{ + static int frameCount; + + std::cout << "iv framecount: " << frameCount << std::endl; + //printf("frame dims %i x %i \n", frame->width, frame->height); + + memcpy(g_IvImg, frame->data, (frame->width * frame->height) * 2); // w * h * 2 + + frameCount++; +} + +void dsCallback(uvc_frame_t *frame, void *ptr) +{ + static int frameCount; + + std::cout << "ds framecount: " << frameCount << std::endl; + //printf("frame dims %i x %i \n", frame->width, frame->height); + + memcpy(g_DsImg, frame->data, frame->width * (frame->height - 1) * 2); // w * h * 2 + + frameCount++; +} + +uvc_device_handle_t *g_IvHandle = nullptr; +uvc_device_handle_t *g_DsHandle = nullptr; + +uvc_stream_ctrl_t g_IvCtrl; +uvc_stream_ctrl_t g_DsCtrl; + +struct CamParams +{ + uvc_frame_format format; + int width; + int height; + int fps; + uvc_frame_callback_t * cb; +}; + +// ivcam = 0x0a66 +// UVC_FRAME_FORMAT_INVI, 640, 480, 60 +void CameraCaptureInit(uvc_context_t *ctx, uint16_t product_id, uvc_device_handle_t * devh, uvc_stream_ctrl_t &ctrl, CamParams params, int devNum = 1) +{ + uvc_device_t *uvcCameraDevice; + + uvc_error_t result; + + // Locates the first attached UVC device, stores in uvcCameraDevice + result = uvc_find_device(ctx, &uvcCameraDevice, 0, product_id, NULL); + + if (result < 0) + { + std::cout << "================================================================================= \n"; + std::cout << "Couldn't find camera with product id: " << product_id << std::endl; + std::cout << "================================================================================= \n"; + return; + //throw std::runtime_error("Find Device Failed"); + } + + result = uvc_open2(uvcCameraDevice, &devh, devNum); + + if (result < 0) + { + uvc_perror(result, "uvc_open"); + return; + //throw std::runtime_error("Open camera_handle Failed"); + } + + uvc_error_t status = uvc_get_stream_ctrl_format_size(devh, &ctrl, params.format, params.width, params.height, params.fps); + + if (status < 0) + { + uvc_perror(status, "set_format_size"); + //throw std::runtime_error("Open camera_handle Failed"); + } + + uvc_print_stream_ctrl(&ctrl, stderr); + + /* Print out a message containing all the information that libuvc + * knows about the device */ + uvc_print_diag(devh, stderr); + + for (int i = 0; i < 10; ++i) + { + std::cout << "Fuck" << std::endl; + SetStreamIntent(devh, 5); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + + for (int i = 0; i < 100; ++i) + { + + uvc_error_t startStreamResult = uvc_start_streaming(devh, &ctrl, params.cb, nullptr, 0); + + for (int i = 0; i < 10; ++i) + { + std::cout << "Fuck" << std::endl; + SetStreamIntent(devh, 5); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + if (startStreamResult < 0) + { + uvc_perror(startStreamResult, "start_stream"); + continue; + } + else { + break; + } + + } + +} + +int main(int argc, const char * argv[]) +{ + static int frameCount = 0; + + if (!glfwInit()) + { + fprintf( stderr, "Failed to initialize GLFW\n" ); + return -1; + } + + glfwWindowHint(GLFW_SAMPLES, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + window = glfwCreateWindow(1280, 480, "DS4 & OSX", NULL, NULL); + + if (!window) + { + std::cout << "Failed to open GLFW window\n" << std::endl; + glfwTerminate(); + std::exit( EXIT_FAILURE ); + } + + glfwMakeContextCurrent(window); + + glfwSetWindowSizeCallback(window, + [](GLFWwindow* window, int width, int height) + { + glViewport(0, 0, width, height); + }); + + // Remapped colors... + g_IvCamLocation = CreateTexture(640, 480, GL_RGB); + g_DS4CamLocation = CreateTexture(628, 468, GL_RGB); + + // glfwSetWindowShouldClose(window, 1); + + // The fullscreen quad's FBO + GLuint quad_VertexArrayID; + glGenVertexArrays(1, &quad_VertexArrayID); + glBindVertexArray(quad_VertexArrayID); + + static const GLfloat g_quad_vertex_buffer_data[] = + { + 1.0f, 1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 0.0f, + }; + + GLuint quad_vertexbuffer; + glGenBuffers(1, &quad_vertexbuffer); + glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(g_quad_vertex_buffer_data), g_quad_vertex_buffer_data, GL_STATIC_DRAW); + + // Create and compile our GLSL program from the shaders + GLuint fullscreenTextureProg = CreateGLProgram(vertShader, fragShader); + textureHandle = glGetUniformLocation(fullscreenTextureProg, "u_image"); + + // Initialize overall context... + uvc_error_t contextInitStatus; + + uvc_context_t *ctx; + + contextInitStatus = uvc_init(&ctx, NULL); + + if (contextInitStatus < 0) + { + uvc_perror(contextInitStatus, "uvc_init"); + throw std::runtime_error("UVC Init Failed"); + } + + // SPARK UP THE CAMERAS! + // --------------------- + + //CamParams ivParams = {UVC_FRAME_FORMAT_INVR, 640, 480, 60, ivCallback}; + + CamParams ivParams = {UVC_FRAME_FORMAT_YUYV, 640, 480, 0, ivCallback}; + CamParams dsParams = {UVC_FRAME_FORMAT_Z16, 628, 469, 0, dsCallback}; + + //CameraCaptureInit(ctx, 0x0a66, g_IvHandle, g_IvCtrl, ivParams); // This is depth for ivCam + + CameraCaptureInit(ctx, 0x0a80, g_DsHandle, g_DsCtrl, dsParams, 1); // This is Depth for DS + CameraCaptureInit(ctx, 0x0a80, g_IvHandle, g_IvCtrl, ivParams, 2); // This is RGB for DS + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + //std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + //SetStreamIntent(g_DsHandle, g_streamIntent); + + while(!glfwWindowShouldClose(window)) + { + glfwPollEvents(); + + glClearColor(0.15, 0.15, 0.15, 1); + glClear(GL_COLOR_BUFFER_BIT); + + glfwMakeContextCurrent(window); + + //std::cout << "depth_stream_enabled? " << getStatus(g_depthHandle) << std::endl; + //std::cout << "color_stream_enabled? " << getStatus(g_colorHandle) << std::endl; + + //if (getStatus(g_DsHandle) == 4) + //{ + // //SetStreamIntent(g_DsHandle, g_streamIntent); + //} + + auto drawTex = [fullscreenTextureProg, quad_vertexbuffer](GLuint texId, void * pixels, int width, int height, GLint fmt, GLenum type) + { + + CHECK_GL_ERROR(); + + glUseProgram(fullscreenTextureProg); + + // Bind our texture in Texture Unit 0 + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texId); + glTexImage2D(GL_TEXTURE_2D, 0, fmt, width, height, 0, fmt, type, pixels); + glUniform1i(textureHandle, 0); // Set our "u_image" sampler to user Texture Unit 0 + + CHECK_GL_ERROR(); + + // 1st attribute buffer : vertices + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer); + glVertexAttribPointer( + 0, // attribute 0 (layout location 1) + 3, // size + GL_FLOAT, // type + GL_FALSE, // normalized? + 0, // stride + (void*)0 // array buffer offset + ); + + CHECK_GL_ERROR(); + + // Draw the triangles ! + glDrawArrays(GL_TRIANGLES, 0, 6); // 2*3 indices starting at 0 -> 2 triangles + + glDisableVertexAttribArray(0); + + glUseProgram(0); + + }; + + int width, height; + glfwGetWindowSize(window, &width, &height); + + + glViewport(0, 0, width, height); + + static uint8_t remappedIv[640 * 480 * 3]; + ConvertDepthToRGBUsingHistogram(remappedIv, g_IvImg, 640, 480, 0.3f, 0.825f); + drawTex(g_IvCamLocation, remappedIv, 640, 480, GL_RGB, GL_UNSIGNED_BYTE); + + + glViewport(width / 2, 0, width, height); + static uint8_t remappedDS[628 * 469 * 3]; + ConvertDepthToRGBUsingHistogram(remappedDS, g_DsImg, 628, 469, 0.1f, 0.625f); + drawTex(g_DS4CamLocation, remappedDS, 628, 469, GL_RGB, GL_UNSIGNED_BYTE); + + frameCount++; + + glfwSwapBuffers(window); + CHECK_GL_ERROR(); + + std::this_thread::sleep_for(std::chrono::milliseconds(60)); + + } + + glfwTerminate(); + return 0; + +} \ No newline at end of file diff --git a/librealsense.xcworkspace/contents.xcworkspacedata b/librealsense.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..84a55db50d --- /dev/null +++ b/librealsense.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/librealsense/librealsense.xcodeproj/project.pbxproj b/librealsense/librealsense.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..dcfc5c6fdb --- /dev/null +++ b/librealsense/librealsense.xcodeproj/project.pbxproj @@ -0,0 +1,347 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 08567BDF1B5B5ABF00EB6C0D /* CameraContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 08567BD81B5B5ABF00EB6C0D /* CameraContext.cpp */; }; + 08567BE01B5B5ABF00EB6C0D /* CameraContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 08567BD91B5B5ABF00EB6C0D /* CameraContext.h */; }; + 08567BE11B5B5ABF00EB6C0D /* CameraHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 08567BDA1B5B5ABF00EB6C0D /* CameraHeader.h */; }; + 08567BE21B5B5ABF00EB6C0D /* CameraSPI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 08567BDB1B5B5ABF00EB6C0D /* CameraSPI.cpp */; }; + 08567BE31B5B5ABF00EB6C0D /* CameraSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 08567BDC1B5B5ABF00EB6C0D /* CameraSPI.h */; }; + 08567BE41B5B5ABF00EB6C0D /* CameraXU.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 08567BDD1B5B5ABF00EB6C0D /* CameraXU.cpp */; }; + 08567BE51B5B5ABF00EB6C0D /* CameraXU.h in Headers */ = {isa = PBXBuildFile; fileRef = 08567BDE1B5B5ABF00EB6C0D /* CameraXU.h */; }; + 08567BEB1B5B5B4E00EB6C0D /* libuvc_config.h in Headers */ = {isa = PBXBuildFile; fileRef = 08567BE81B5B5B4E00EB6C0D /* libuvc_config.h */; }; + 08567BEC1B5B5B4E00EB6C0D /* libuvc_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 08567BE91B5B5B4E00EB6C0D /* libuvc_internal.h */; }; + 08567BED1B5B5B4E00EB6C0D /* libuvc.h in Headers */ = {isa = PBXBuildFile; fileRef = 08567BEA1B5B5B4E00EB6C0D /* libuvc.h */; }; + 08567BF91B5B5B5D00EB6C0D /* ctrl.c in Sources */ = {isa = PBXBuildFile; fileRef = 08567BEF1B5B5B5D00EB6C0D /* ctrl.c */; }; + 08567BFA1B5B5B5D00EB6C0D /* device.c in Sources */ = {isa = PBXBuildFile; fileRef = 08567BF01B5B5B5D00EB6C0D /* device.c */; }; + 08567BFB1B5B5B5D00EB6C0D /* diag.c in Sources */ = {isa = PBXBuildFile; fileRef = 08567BF11B5B5B5D00EB6C0D /* diag.c */; }; + 08567BFE1B5B5B5D00EB6C0D /* frame.c in Sources */ = {isa = PBXBuildFile; fileRef = 08567BF41B5B5B5D00EB6C0D /* frame.c */; }; + 08567BFF1B5B5B5D00EB6C0D /* init.c in Sources */ = {isa = PBXBuildFile; fileRef = 08567BF51B5B5B5D00EB6C0D /* init.c */; }; + 08567C001B5B5B5D00EB6C0D /* misc.c in Sources */ = {isa = PBXBuildFile; fileRef = 08567BF61B5B5B5D00EB6C0D /* misc.c */; }; + 08567C011B5B5B5D00EB6C0D /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = 08567BF71B5B5B5D00EB6C0D /* stream.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 08567BC41B5B5A0200EB6C0D /* librealsense.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = librealsense.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 08567BD81B5B5ABF00EB6C0D /* CameraContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CameraContext.cpp; sourceTree = ""; }; + 08567BD91B5B5ABF00EB6C0D /* CameraContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CameraContext.h; sourceTree = ""; }; + 08567BDA1B5B5ABF00EB6C0D /* CameraHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CameraHeader.h; sourceTree = ""; }; + 08567BDB1B5B5ABF00EB6C0D /* CameraSPI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CameraSPI.cpp; sourceTree = ""; }; + 08567BDC1B5B5ABF00EB6C0D /* CameraSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CameraSPI.h; sourceTree = ""; }; + 08567BDD1B5B5ABF00EB6C0D /* CameraXU.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CameraXU.cpp; sourceTree = ""; }; + 08567BDE1B5B5ABF00EB6C0D /* CameraXU.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CameraXU.h; sourceTree = ""; }; + 08567BE81B5B5B4E00EB6C0D /* libuvc_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libuvc_config.h; path = ../third_party/libuvc/include/libuvc/libuvc_config.h; sourceTree = ""; }; + 08567BE91B5B5B4E00EB6C0D /* libuvc_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libuvc_internal.h; path = ../third_party/libuvc/include/libuvc/libuvc_internal.h; sourceTree = ""; }; + 08567BEA1B5B5B4E00EB6C0D /* libuvc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libuvc.h; path = ../third_party/libuvc/include/libuvc/libuvc.h; sourceTree = ""; }; + 08567BEF1B5B5B5D00EB6C0D /* ctrl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ctrl.c; path = ../third_party/libuvc/src/ctrl.c; sourceTree = ""; }; + 08567BF01B5B5B5D00EB6C0D /* device.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = device.c; path = ../third_party/libuvc/src/device.c; sourceTree = ""; }; + 08567BF11B5B5B5D00EB6C0D /* diag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = diag.c; path = ../third_party/libuvc/src/diag.c; sourceTree = ""; }; + 08567BF41B5B5B5D00EB6C0D /* frame.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = frame.c; path = ../third_party/libuvc/src/frame.c; sourceTree = ""; }; + 08567BF51B5B5B5D00EB6C0D /* init.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = init.c; path = ../third_party/libuvc/src/init.c; sourceTree = ""; }; + 08567BF61B5B5B5D00EB6C0D /* misc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = misc.c; path = ../third_party/libuvc/src/misc.c; sourceTree = ""; }; + 08567BF71B5B5B5D00EB6C0D /* stream.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = stream.c; path = ../third_party/libuvc/src/stream.c; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 08567BC11B5B5A0200EB6C0D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08567BBB1B5B5A0200EB6C0D = { + isa = PBXGroup; + children = ( + 08567BE61B5B5B2800EB6C0D /* libuvc */, + 08567BC61B5B5A0200EB6C0D /* librealsense */, + 08567BC51B5B5A0200EB6C0D /* Products */, + ); + sourceTree = ""; + }; + 08567BC51B5B5A0200EB6C0D /* Products */ = { + isa = PBXGroup; + children = ( + 08567BC41B5B5A0200EB6C0D /* librealsense.a */, + ); + name = Products; + sourceTree = ""; + }; + 08567BC61B5B5A0200EB6C0D /* librealsense */ = { + isa = PBXGroup; + children = ( + 08567BD81B5B5ABF00EB6C0D /* CameraContext.cpp */, + 08567BD91B5B5ABF00EB6C0D /* CameraContext.h */, + 08567BDA1B5B5ABF00EB6C0D /* CameraHeader.h */, + 08567BDB1B5B5ABF00EB6C0D /* CameraSPI.cpp */, + 08567BDC1B5B5ABF00EB6C0D /* CameraSPI.h */, + 08567BDD1B5B5ABF00EB6C0D /* CameraXU.cpp */, + 08567BDE1B5B5ABF00EB6C0D /* CameraXU.h */, + ); + path = librealsense; + sourceTree = ""; + }; + 08567BE61B5B5B2800EB6C0D /* libuvc */ = { + isa = PBXGroup; + children = ( + 08567BEE1B5B5B5100EB6C0D /* src */, + 08567BE71B5B5B4100EB6C0D /* include */, + ); + name = libuvc; + sourceTree = ""; + }; + 08567BE71B5B5B4100EB6C0D /* include */ = { + isa = PBXGroup; + children = ( + 08567BE81B5B5B4E00EB6C0D /* libuvc_config.h */, + 08567BE91B5B5B4E00EB6C0D /* libuvc_internal.h */, + 08567BEA1B5B5B4E00EB6C0D /* libuvc.h */, + ); + name = include; + sourceTree = ""; + }; + 08567BEE1B5B5B5100EB6C0D /* src */ = { + isa = PBXGroup; + children = ( + 08567BEF1B5B5B5D00EB6C0D /* ctrl.c */, + 08567BF01B5B5B5D00EB6C0D /* device.c */, + 08567BF11B5B5B5D00EB6C0D /* diag.c */, + 08567BF41B5B5B5D00EB6C0D /* frame.c */, + 08567BF51B5B5B5D00EB6C0D /* init.c */, + 08567BF61B5B5B5D00EB6C0D /* misc.c */, + 08567BF71B5B5B5D00EB6C0D /* stream.c */, + ); + name = src; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 08567BC21B5B5A0200EB6C0D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 08567BEC1B5B5B4E00EB6C0D /* libuvc_internal.h in Headers */, + 08567BE11B5B5ABF00EB6C0D /* CameraHeader.h in Headers */, + 08567BEB1B5B5B4E00EB6C0D /* libuvc_config.h in Headers */, + 08567BE31B5B5ABF00EB6C0D /* CameraSPI.h in Headers */, + 08567BED1B5B5B4E00EB6C0D /* libuvc.h in Headers */, + 08567BE51B5B5ABF00EB6C0D /* CameraXU.h in Headers */, + 08567BE01B5B5ABF00EB6C0D /* CameraContext.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 08567BC31B5B5A0200EB6C0D /* librealsense */ = { + isa = PBXNativeTarget; + buildConfigurationList = 08567BCF1B5B5A0200EB6C0D /* Build configuration list for PBXNativeTarget "librealsense" */; + buildPhases = ( + 08567BC01B5B5A0200EB6C0D /* Sources */, + 08567BC11B5B5A0200EB6C0D /* Frameworks */, + 08567BC21B5B5A0200EB6C0D /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = librealsense; + productName = librealsense; + productReference = 08567BC41B5B5A0200EB6C0D /* librealsense.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08567BBC1B5B5A0200EB6C0D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0620; + ORGANIZATIONNAME = "Dimitri Diakopoulos"; + TargetAttributes = { + 08567BC31B5B5A0200EB6C0D = { + CreatedOnToolsVersion = 6.2; + }; + }; + }; + buildConfigurationList = 08567BBF1B5B5A0200EB6C0D /* Build configuration list for PBXProject "librealsense" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 08567BBB1B5B5A0200EB6C0D; + productRefGroup = 08567BC51B5B5A0200EB6C0D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 08567BC31B5B5A0200EB6C0D /* librealsense */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 08567BC01B5B5A0200EB6C0D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 08567BF91B5B5B5D00EB6C0D /* ctrl.c in Sources */, + 08567BE41B5B5ABF00EB6C0D /* CameraXU.cpp in Sources */, + 08567BFE1B5B5B5D00EB6C0D /* frame.c in Sources */, + 08567C001B5B5B5D00EB6C0D /* misc.c in Sources */, + 08567C011B5B5B5D00EB6C0D /* stream.c in Sources */, + 08567BFB1B5B5B5D00EB6C0D /* diag.c in Sources */, + 08567BFF1B5B5B5D00EB6C0D /* init.c in Sources */, + 08567BFA1B5B5B5D00EB6C0D /* device.c in Sources */, + 08567BDF1B5B5ABF00EB6C0D /* CameraContext.cpp in Sources */, + 08567BE21B5B5ABF00EB6C0D /* CameraSPI.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 08567BCD1B5B5A0200EB6C0D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../third_party/libuvc/include", + /usr/local/Cellar/libusb/1.0.19/include/, + ); + LIBRARY_SEARCH_PATHS = /usr/local/Cellar/libusb/1.0.19/lib; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 08567BCE1B5B5A0200EB6C0D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../third_party/libuvc/include", + /usr/local/Cellar/libusb/1.0.19/include/, + ); + LIBRARY_SEARCH_PATHS = /usr/local/Cellar/libusb/1.0.19/lib; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 08567BD01B5B5A0200EB6C0D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + EXECUTABLE_PREFIX = ""; + GCC_ENABLE_CPP_EXCEPTIONS = YES; + GCC_ENABLE_CPP_RTTI = YES; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 08567BD11B5B5A0200EB6C0D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + EXECUTABLE_PREFIX = ""; + GCC_ENABLE_CPP_EXCEPTIONS = YES; + GCC_ENABLE_CPP_RTTI = YES; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 08567BBF1B5B5A0200EB6C0D /* Build configuration list for PBXProject "librealsense" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 08567BCD1B5B5A0200EB6C0D /* Debug */, + 08567BCE1B5B5A0200EB6C0D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 08567BCF1B5B5A0200EB6C0D /* Build configuration list for PBXNativeTarget "librealsense" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 08567BD01B5B5A0200EB6C0D /* Debug */, + 08567BD11B5B5A0200EB6C0D /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08567BBC1B5B5A0200EB6C0D /* Project object */; +} diff --git a/librealsense/librealsense/CameraContext.cpp b/librealsense/librealsense/CameraContext.cpp new file mode 100644 index 0000000000..57873b4f6c --- /dev/null +++ b/librealsense/librealsense/CameraContext.cpp @@ -0,0 +1,5 @@ +#include "CameraContext.h" + +#include +#include +#include \ No newline at end of file diff --git a/librealsense/librealsense/CameraContext.h b/librealsense/librealsense/CameraContext.h new file mode 100644 index 0000000000..7213fc3c05 --- /dev/null +++ b/librealsense/librealsense/CameraContext.h @@ -0,0 +1,69 @@ +#ifndef CAMERA_CONTEXT_H +#define CAMERA_CONTEXT_H + +#include +#include +#include + +enum PixelFormat +{ + // ... +}; + +struct StreamInfo +{ + int width; + int height; + int fps; + PixelFormat fmt; +}; + +struct StreamHandle +{ + // ... +}; + +class CameraContext +{ + // Initialize libusb context + CameraContext(); + + // Stops open streams, closes devices, closes libusb + ~CameraContext(); + + // Probes currently connected USB3 cameras and returns an array of device ids + static std::vector QueryDeviceList(); + + // Gets informational data about a device idx in QueryDeviceList(). F200 / R200. + void GetDeviceInfo(int dev); + + // Opens the device from idx. Returns true if the device was opened. + bool OpenDevice(int dev); + + // Finds supported info + std::vector QuerySupportedStreams(int dev); + + // Configure stream in a specific mode. Returns true if the stream was configured. + bool ConfigureStream(int dev, StreamInfo inf); + + // Starts the stream. + const StreamHandle & StartStream(int dev, StreamInfo inf); + + // Stops the stream + void StopStream(int dev); + +}; + +bool QueryCalibrationExtrinsics(const StreamHandle & h); + +bool QueryCalibrationIntrinsics(const StreamHandle & h); + +int GetStreamStatus(const StreamHandle & h); + +int GetCameraSerial(const StreamHandle & h); + +// Etc +float GetStreamGain(const StreamHandle & h); +bool SetStreamGain(float gain, const StreamHandle & h); + +#endif diff --git a/librealsense/librealsense/CameraHeader.h b/librealsense/librealsense/CameraHeader.h new file mode 100644 index 0000000000..b01f804100 --- /dev/null +++ b/librealsense/librealsense/CameraHeader.h @@ -0,0 +1,79 @@ +#ifndef DS4_CAMERA_HEADER_H +#define DS4_CAMERA_HEADER_H + +#include + +#pragma pack(1) + +#define CURRENT_CAMERA_CONTENTS_VERSION_NUMBER 10 + +struct CameraHeaderInfo +{ + uint32_t serialNumber; + uint32_t modelNumber; + uint32_t revisionNumber; + uint8_t modelData[64]; + double buildDate; + double firstProgramDate; + double focusAndAlignmentDate; + uint32_t nominalBaselineThird; + uint8_t moduleVersion; + uint8_t moduleMajorVersion; + uint8_t moduleMinorVersion; + uint8_t moduleSkew; + uint32_t lensTypeThird; + uint32_t OEMID; + uint32_t lensCoatingTypeThird; + uint8_t reserved2[4]; + uint32_t emitterType; + uint8_t reserved3[4]; + uint32_t cameraFPGAVersion; + uint8_t reserved4[4]; + double calibrationDate; + uint32_t calibrationType; + double calibrationXError; + double calibrationYError; + double rectificationDataQres[54]; + double rectificationDataPadding[26]; + double CxQres; + double CyQres; + double CzQres; + double KxQres; + double KyQres; + uint32_t cameraHeadContentsVersion; + uint32_t cameraHeadContentsSizeInBytes; + double CxBig; + double CyBig; + double CzBig; + double KxBig; + double KyBig; + double CxSpecial; + double CySpecial; + double CzSpecial; + double KxSpecial; + double KySpecial; + uint8_t cameraHeadDataLittleEndian; + double rectificationDataBig[54]; + double rectificationDataSpecial[54]; + uint8_t cameraOptions1; + uint8_t cameraOptions2; + uint8_t bodySerialNumber[20]; + double Dx; + double Dy; + double Dz; + double ThetaX; + double ThetaY; + double ThetaZ; + double registrationDate; + double registrationRotation[9]; + double registrationTranslation[3]; + uint32_t nominalBaseline; + uint32_t lensType; + uint32_t lensCoating; + uint32_t theLastWord; + uint8_t reserved5[57]; +} __attribute__((aligned(1))); + +#pragma pack() + +#endif diff --git a/librealsense/librealsense/CameraSPI.cpp b/librealsense/librealsense/CameraSPI.cpp new file mode 100644 index 0000000000..7c0450e106 --- /dev/null +++ b/librealsense/librealsense/CameraSPI.cpp @@ -0,0 +1,138 @@ +#include "CameraSPI.h" +#include + +using namespace XUControl; +using namespace std; + +bool readPages(uvc_device_handle_t *devh, uint32_t address, unsigned char * buffer, uint32_t nPages) +{ + int addressTest = SPI_FLASH_TOTAL_SIZE_IN_BYTES - address - nPages * SPI_FLASH_PAGE_SIZE_IN_BYTES; + + if (!nPages || addressTest < 0) + return false; + + // SendCommand() + + CommandPacket command; + command.code = COMMAND_DOWNLOAD_SPI_FLASH; + command.modifier = COMMAND_MODIFIER_DIRECT; + command.tag = 12; + command.address = address; + command.value = nPages * SPI_FLASH_PAGE_SIZE_IN_BYTES; + + unsigned int cmdLength = sizeof(CommandPacket); + auto XUWriteCmdStatus = uvc_set_ctrl(devh, CAMERA_XU_UNIT_ID, CONTROL_COMMAND_RESPONSE, &command, cmdLength); + + ResponsePacket response; + unsigned int resLength = sizeof(ResponsePacket); + auto XUReadCmdStatus = uvc_get_ctrl(devh, CAMERA_XU_UNIT_ID, CONTROL_COMMAND_RESPONSE, &response, resLength, UVC_GET_CUR); + + std::cout << "Read SPI Command Status: " << ResponseCodeToString(response.responseCode) << std::endl; + + // End SendCommand() + + bool returnStatus = true; + + if (XUReadCmdStatus > 0) + { + uint8_t *p = buffer; + uint16_t spiLength = SPI_FLASH_PAGE_SIZE_IN_BYTES; + for (unsigned int i = 0; i < nPages; ++i) + { + int bytesReturned = uvc_get_ctrl(devh, CAMERA_XU_UNIT_ID, CONTROL_COMMAND_RESPONSE, p, spiLength, UVC_GET_CUR); + p += SPI_FLASH_PAGE_SIZE_IN_BYTES; + } + } + + return returnStatus; +} + +void readArbitraryChunk(uvc_device_handle_t *devh, uint32_t address, void * dataIn, int lengthInBytesIn) +{ + unsigned char * data = (unsigned char *)dataIn; + int lengthInBytes = lengthInBytesIn; + unsigned char page[SPI_FLASH_PAGE_SIZE_IN_BYTES]; + int nPagesToRead; + uint32_t startAddress = address; + if (startAddress & 0xff) + { + // we are not on a page boundary + startAddress = startAddress & ~0xff; + uint32_t startInPage = address - startAddress; + uint32_t lengthToCopy = SPI_FLASH_PAGE_SIZE_IN_BYTES - startInPage; + if (lengthToCopy > (uint32_t)lengthInBytes) + lengthToCopy = lengthInBytes; + readPages(devh, startAddress, page, 1); + memcpy(data, page + startInPage, lengthToCopy); + lengthInBytes -= lengthToCopy; + data += lengthToCopy; + startAddress += SPI_FLASH_PAGE_SIZE_IN_BYTES; + } + nPagesToRead = lengthInBytes / SPI_FLASH_PAGE_SIZE_IN_BYTES; + if (nPagesToRead > 0) + readPages(devh, startAddress, data, nPagesToRead); + lengthInBytes -= (nPagesToRead * SPI_FLASH_PAGE_SIZE_IN_BYTES); + + if (lengthInBytes) + { + // means we still have a remainder + data += (nPagesToRead * SPI_FLASH_PAGE_SIZE_IN_BYTES); + startAddress += (nPagesToRead * SPI_FLASH_PAGE_SIZE_IN_BYTES); + readPages(devh, startAddress, page, 1); + memcpy(data, page, lengthInBytes); + } +} + + +bool readAdminSector(uvc_device_handle_t *devh, unsigned char data[SPI_FLASH_SECTOR_SIZE_IN_BYTES], int whichAdminSector) +{ + readArbitraryChunk(devh, NV_NON_FIRMWARE_ROOT_ADDRESS, adminSectorAddresses, NV_ADMIN_DATA_N_ENTRIES * sizeof(adminSectorAddresses[0])); + + if (whichAdminSector >= 0 && whichAdminSector < NV_ADMIN_DATA_N_ENTRIES) + { + uint32_t pageAddressInBytes = adminSectorAddresses[whichAdminSector]; + return readPages(devh, pageAddressInBytes, data, SPI_FLASH_PAGES_PER_SECTOR); + } + + return false; +} + +int getAdminSectorUsedCopies(uvc_device_handle_t *devh, uint32_t sectorAddress, uint32_t * unusedCopiesPtr) +{ + const uint32_t UNUSED_BITS_OFFSET = SPI_FLASH_SECTOR_SIZE_IN_BYTES - sizeof(uint32_t) - sizeof(uint32_t); + + uint32_t unusedCopies; + uint32_t usedCopies; + int usedCopiesCount = 0; + uint32_t addressOfUnusedBits = sectorAddress + UNUSED_BITS_OFFSET; + unsigned int i; + + readArbitraryChunk(devh, addressOfUnusedBits, &unusedCopies, sizeof(uint32_t)); + usedCopies = ~unusedCopies; + + for (i = 0; i < (sizeof(uint32_t) * 8); i++) + { + if (usedCopies & (1 << i)) usedCopiesCount++; + } + + *unusedCopiesPtr = unusedCopies; + return usedCopiesCount; +} + +void readAdminTable(uvc_device_handle_t *devh, int whichAdminSector, int blockLength, void * data, int offset, int lengthToRead) +{ + uint32_t address = adminSectorAddresses[whichAdminSector]; + uint32_t dummy; + int usedCopiesCount = getAdminSectorUsedCopies(devh, address, &dummy); + uint32_t addressInSector = address + (usedCopiesCount - 1) * blockLength + offset; + readArbitraryChunk(devh, addressInSector, data, lengthToRead); +} + +void readCameraModuleInfoBlock(uvc_device_handle_t *devh, uint8_t *data, int size) +{ + RoutineStorageTables rst; + readAdminTable(devh, NV_IFFLEY_ROUTINE_TABLE_ADDRESS_INDEX, SIZEOF_ROUTINE_DESCRIPTION_ERASED_AND_PRESERVE_TABLE, rst.rd, ROUTINE_DESCRIPTION_OFFSET, SIZEOF_ROUTINE_DESCRIPTION_TABLE); + unsigned char block[SPI_FLASH_SECTOR_SIZE_IN_BYTES]; + readAdminSector(devh, block, NV_CALIBRATION_DATA_ADDRESS_INDEX); // readCalibrationDataSector + memmove(data, block + 2048, (size < CAM_INFO_BLOCK_LEN ? size : CAM_INFO_BLOCK_LEN)); +} \ No newline at end of file diff --git a/librealsense/librealsense/CameraSPI.h b/librealsense/librealsense/CameraSPI.h new file mode 100644 index 0000000000..820df82960 --- /dev/null +++ b/librealsense/librealsense/CameraSPI.h @@ -0,0 +1,80 @@ +#ifndef DS4_CAMERA_SPI_H +#define DS4_CAMERA_SPI_H + +#include "CameraXU.h" +#include "libuvc/libuvc.h" + +// SPI +#define SPI_FLASH_PAGE_SIZE_IN_BYTES 0x100 +#define SPI_FLASH_SECTOR_SIZE_IN_BYTES 0x1000 +#define SPI_FLASH_SIZE_IN_SECTORS 256 +#define SPI_FLASH_TOTAL_SIZE_IN_BYTES (SPI_FLASH_SIZE_IN_SECTORS * SPI_FLASH_SECTOR_SIZE_IN_BYTES) +#define SPI_FLASH_PAGES_PER_SECTOR (SPI_FLASH_SECTOR_SIZE_IN_BYTES / SPI_FLASH_PAGE_SIZE_IN_BYTES) +#define SPI_FLASH_LENGTH_IN_PAGES(N_BYTES) ((N_BYTES + 0xFF) / SPI_FLASH_PAGE_SIZE_IN_BYTES) + +#define SPI_FLASH_SECTOR_INDEX(BYTE_ADDRESS) (BYTE_ADDRESS >> 12) +#define SPI_FLASH_ADDRESS_WITHIN_SECTOR(BYTE_ADDRESS) (BYTE_ADDRESS & 0xFFF) +#define SPI_FLASH_SECTOR_ADDRESS(BYTE_ADDRESS) (BYTE_ADDRESS & ~0xFFF) +#define SPI_FLASH_SECTOR_ADDRESS_FROM_INDEX(SECTOR_INDEX) (SECTOR_INDEX << 12) + +#define SPI_FLASH_SECTORS_RESERVED_FOR_FIRMWARE 160 +#define SPI_FLASH_START_OF_SECTORS_NOT_FOR_FIRMWARE (SPI_FLASH_SECTORS_RESERVED_FOR_FIRMWARE * SPI_FLASH_SECTOR_SIZE_IN_BYTES) + +#define SPI_FLASH_SECTORS_RESERVED_FOR_ROUTINES 64 +#define SPI_FLASH_FIRST_ROUTINE_SECTOR (SPI_FLASH_SIZE_IN_SECTORS - SPI_FLASH_SECTORS_RESERVED_FOR_ROUTINES) + +// 1 Mb total +#define NV_STORAGE_IN_BYTES (SPI_FLASH_SECTOR_SIZE_IN_BYTES * SPI_FLASH_SIZE_IN_SECTORS) +#define NV_NON_FIRMWARE_START (SPI_FLASH_SECTORS_RESERVED_FOR_FIRMWARE * SPI_FLASH_SECTOR_SIZE_IN_BYTES) + +#define NV_ADMIN_DATA_N_ENTRIES 9 +#define NV_CALIBRATION_DATA_ADDRESS_INDEX 0 +#define NV_IFFLEY_ROUTINE_TABLE_ADDRESS_INDEX 1 +#define NV_IFFLEY_CONSTANTS_TABLE_ADDRESS_INDEX 2 +#define NV_ENUMERATION_INFORMATION_ADDRESS_INDEX 3 +#define NV_DESCRIPTOR_INFORMATION_ADDRESS_INDEX 4 +#define IFFLEY_VERSION_NUMBER_INDEX 5 +#define FIRMWARE_VERSION_NUMBER_INDEX 6 +#define NV_CALIBRATION_DATA_BACKUP_ADDRESS_INDEX 7 +#define NV_ENCRYPTED_HASH_DATA_INDEX 8 + +#define NV_NON_FIRMWARE_ROOT_ADDRESS NV_NON_FIRMWARE_START +#define NV_INITIAL_CALIBRATION_DATA_ADDRESS (NV_NON_FIRMWARE_ROOT_ADDRESS + (NV_CALIBRATION_DATA_ADDRESS_INDEX + 1) * SPI_FLASH_SECTOR_SIZE_IN_BYTES) +#define NV_INITIAL_IFFLEY_ROUTINE_TABLE_ADDRESS (NV_NON_FIRMWARE_ROOT_ADDRESS + (NV_IFFLEY_ROUTINE_TABLE_ADDRESS_INDEX + 1) * SPI_FLASH_SECTOR_SIZE_IN_BYTES) +#define NV_INITIAL_IFFLEY_CONSTANTS_TABLE_ADDRESS (NV_NON_FIRMWARE_ROOT_ADDRESS + (NV_IFFLEY_CONSTANTS_TABLE_ADDRESS_INDEX + 1) * SPI_FLASH_SECTOR_SIZE_IN_BYTES) +#define NV_INITIAL_ENUMERATION_INFORMATION_TABLE_ADDRESS (NV_NON_FIRMWARE_ROOT_ADDRESS + (NV_ENUMERATION_INFORMATION_ADDRESS_INDEX + 1) * SPI_FLASH_SECTOR_SIZE_IN_BYTES) +#define NV_INITIAL_DESCRIPTOR_INFORMATION_TABLE_ADDRESS (NV_NON_FIRMWARE_ROOT_ADDRESS + (NV_DESCRIPTOR_INFORMATION_ADDRESS_INDEX + 1) * SPI_FLASH_SECTOR_SIZE_IN_BYTES) +#define NV_INITIAL_CALIBRATION_DATA_BACKUP_ADDRESS (NV_INITIAL_DESCRIPTOR_INFORMATION_TABLE_ADDRESS + SPI_FLASH_SECTOR_SIZE_IN_BYTES) +#define NV_INITIAL_ENCRYPTED_HASH_DATA_ADDRESS (NV_INITIAL_CALIBRATION_DATA_BACKUP_ADDRESS + SPI_FLASH_SECTOR_SIZE_IN_BYTES) + +#define UNUSED_ROUTINE(ENTRY) (ENTRY == UNINITIALIZED_ROUTINE_ENTRY || ENTRY == DELETED_ROUTINE_ENTRY) +typedef unsigned short RoutineDescription; + +#define MAX_ROUTINES 256 + +#define SIZEOF_ROUTINE_DESCRIPTION_TABLE (MAX_ROUTINES * sizeof(RoutineDescription)) +#define SIZEOF_ERASED_TABLE (SPI_FLASH_SECTORS_RESERVED_FOR_ROUTINES * sizeof(unsigned short)) +#define SIZEOF_PRESERVE_TABLE (SPI_FLASH_SECTORS_RESERVED_FOR_ROUTINES * sizeof(unsigned short)) + +#define SIZEOF_ROUTINE_DESCRIPTION_ERASED_AND_PRESERVE_TABLE (SIZEOF_ROUTINE_DESCRIPTION_TABLE + SIZEOF_ERASED_TABLE + SIZEOF_PRESERVE_TABLE) + +#define ROUTINE_DESCRIPTION_OFFSET 0 +#define ERASED_TABLE_OFFSET SIZEOF_ROUTINE_DESCRIPTION_TABLE +#define PRESERVE_TABLE_OFFSET (ERASED_TABLE_OFFSET + SIZEOF_ERASED_TABLE) + +typedef struct +{ + RoutineDescription rd[MAX_ROUTINES]; // Partition Table + unsigned short erasedTable[SPI_FLASH_SECTORS_RESERVED_FOR_ROUTINES]; + unsigned short preserveTable[SPI_FLASH_SECTORS_RESERVED_FOR_ROUTINES]; +} RoutineStorageTables; + +// Bus group +#define COMMAND_MODIFIER_DIRECT 0x00000010 + +// Global... +uint32_t adminSectorAddresses[NV_ADMIN_DATA_N_ENTRIES]; + +#define CAM_INFO_BLOCK_LEN 2048 + +#endif diff --git a/librealsense/librealsense/CameraXU.cpp b/librealsense/librealsense/CameraXU.cpp new file mode 100644 index 0000000000..0a0ee9bdc3 --- /dev/null +++ b/librealsense/librealsense/CameraXU.cpp @@ -0,0 +1,48 @@ +#include "CameraXU.h" + +#include +#include +#include + +namespace XUControl +{ + +int GetStreamStatus(uvc_device_handle_t *devh) +{ + uint32_t status = 0xffffffff; + + size_t length = sizeof(uint32_t); + + // XU Read + auto s = uvc_get_ctrl(devh, CAMERA_XU_UNIT_ID, CONTROL_STATUS, &status, int(length), UVC_GET_CUR); + + std::cout << "Actual Status " << s << std::endl; + + if (s > 0) + { + return s; + } + + std::cerr << "XURead failed for DSDevice::GetStreamStatus" << std::endl; + return -1; +} + +bool SetStreamIntent(uvc_device_handle_t *devh, uint8_t intent) +{ + size_t length = sizeof(uint8_t); + + // XU Write + auto s = uvc_set_ctrl(devh, CAMERA_XU_UNIT_ID, CONTROL_STREAM_INTENT, &intent, int(length)); + + if (s > 0) + { + return true; + } + + std::cerr << "XUWrite failed for DSDevice::SetStreamIntent" << std::endl; + return false; +} + +} + +//@todo implement generic xu read / write + response \ No newline at end of file diff --git a/librealsense/librealsense/CameraXU.h b/librealsense/librealsense/CameraXU.h new file mode 100644 index 0000000000..a0bc361a4e --- /dev/null +++ b/librealsense/librealsense/CameraXU.h @@ -0,0 +1,107 @@ +#ifndef DS4_CAMERA_XU_H +#define DS4_CAMERA_XU_H + +#include +#include + +#include "CameraSPI.h" +#include "libuvc/libuvc.h" + +#define STATUS_BIT_Z_STREAMING (1 << 0) +#define STATUS_BIT_LR_STREAMING (1 << 1) +#define STATUS_BIT_WEB_STREAMING (1 << 2) +#define STATUS_BIT_BOOT_DIAGNOSTIC_FAULT (1 << 3) +#define STATUS_BIT_IFFLEY_CONSTANTS_VALID (1 << 4) +#define STATUS_BIT_WATCHDOG_TIMER_RESET (1 << 5) +#define STATUS_BIT_REC_BUFFER_OVERRUN (1 << 6) +#define STATUS_BIT_CAM_DATA_FORMAT_ERROR (1 << 7) +#define STATUS_BIT_CAM_FIFO_OVERFLOW (1 << 8) +#define STATUS_BIT_REC_DIVIDED_BY_ZERO_ERROR (1 << 9) +#define STATUS_BIT_UVC_HEADER_ERROR (1 << 10) +#define STATUS_BIT_EMITTER_FAULT (1 << 11) +#define STATUS_BIT_THERMAL_FAULT (1 << 12) +#define STATUS_BIT_REC_RUN_ENABLED (1 << 13) +#define STATUS_BIT_VDF_DEPTH_POINTER_STREAMING (1 << 14) +#define STATUS_BIT_VDF_LR_POINTER_STREAMING (1 << 15) +#define STATUS_BIT_VDF_WEBCAM_POINTER_STREAMING (1 << 16) +#define STATUS_BIT_STREAMING_STATE (1 << 27) | (1 << 28) | (1 << 29) | (1 << 30) +#define STATUS_BIT_BUSY (1 << 31) + +#define CAMERA_XU_UNIT_ID 2 + +#define CONTROL_COMMAND_RESPONSE 1 +#define CONTROL_IFFLEY 2 +#define CONTROL_STREAM_INTENT 3 +#define CONTROL_STATUS 20 + +#define COMMAND_DOWNLOAD_SPI_FLASH 0x1A +#define COMMAND_PROTECT_FLASH 0x1C +#define COMMAND_LED_ON 0x14 +#define COMMAND_LED_OFF 0x15 +#define COMMAND_GET_FWREVISION 0x21 +#define COMMAND_GET_SPI_PROTECT 0x23 + +#define INTENT_ENABLE_DEPTH 0x1 +#define INTENT_ENABLE_IR 0x2 +#define INTENT_ENABLE_RGB 0x4 + +namespace XUControl +{ + +struct CommandPacket +{ + CommandPacket(uint32_t code_ = 0, uint32_t modifier_ = 0, uint32_t tag_ = 0, uint32_t address_ = 0, uint32_t value_ = 0) + : code(code_), modifier(modifier_), tag(tag_), address(address_), value(value_) + { + } + + uint32_t code = 0; + uint32_t modifier = 0; + uint32_t tag = 0; + uint32_t address = 0; + uint32_t value = 0; + uint32_t reserved[59]{}; +}; + +struct ResponsePacket +{ + ResponsePacket(uint32_t code_ = 0, uint32_t modifier_ = 0, uint32_t tag_ = 0, uint32_t responseCode_ = 0, uint32_t value_ = 0) + : code(code_), modifier(modifier_), tag(tag_), responseCode(responseCode_), value(value_) + { + } + + uint32_t code = 0; + uint32_t modifier = 0; + uint32_t tag = 0; + uint32_t responseCode = 0; + uint32_t value = 0; + uint32_t revision[4]{}; + uint32_t reserved[55]{}; +}; + +inline std::string ResponseCodeToString(uint32_t rc) +{ + switch (rc) + { + case 0x10: return std::string("RESPONSE_OK"); break; + case 0x11: return std::string("RESPONSE_TIMEOUT"); break; + case 0x12: return std::string("RESPONSE_ACQUIRING_IMAGE"); break; + case 0x13: return std::string("RESPONSE_IMAGE_BUSY"); break; + case 0x14: return std::string("RESPONSE_ACQUIRING_SPI"); break; + case 0x15: return std::string("RESPONSE_SENDING_SPI"); break; + case 0x16: return std::string("RESPONSE_SPI_BUSY"); break; + case 0x17: return std::string("RESPSONSE_UNAUTHORIZED"); break; + case 0x18: return std::string("RESPONSE_ERROR"); break; + case 0x19: return std::string("RESPONSE_CODE_END"); break; + default: return "RESPONSE_UNKNOWN"; + } +} + + +int GetStreamStatus(uvc_device_handle_t *devh); + +bool SetStreamIntent(uvc_device_handle_t *devh, uint8_t intent); + +} + +#endif diff --git a/readme.md b/readme.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/third_party/catch/catch.hpp b/third_party/catch/catch.hpp new file mode 100644 index 0000000000..6b8dfb5ebd --- /dev/null +++ b/third_party/catch/catch.hpp @@ -0,0 +1,8997 @@ +/* + * CATCH v1.0 build 53 (master branch) + * Generated: 2014-08-20 08:08:19.533804 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + +#define TWOBLUECUBES_CATCH_HPP_INCLUDED + +// #included from: internal/catch_suppress_warnings.h + +#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wvariadic-macros" +#pragma clang diagnostic ignored "-Wc99-extensions" +#pragma clang diagnostic ignored "-Wunused-variable" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#pragma clang diagnostic ignored "-Wc++98-compat" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#elif defined __GNUC__ +#pragma GCC diagnostic ignored "-Wvariadic-macros" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpadded" +#endif + +#ifdef CATCH_CONFIG_MAIN +# define CATCH_CONFIG_RUNNER +#endif + +#ifdef CATCH_CONFIG_RUNNER +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// #included from: internal/catch_notimplemented_exception.h +#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED + +// #included from: catch_common.h +#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED + +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) + +#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr +#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) + +#include +#include +#include + +// #included from: catch_compiler_capabilities.h +#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED + +// Much of the following code is based on Boost (1.53) + +#ifdef __clang__ + +# if __has_feature(cxx_nullptr) +# define CATCH_CONFIG_CPP11_NULLPTR +# endif + +# if __has_feature(cxx_noexcept) +# define CATCH_CONFIG_CPP11_NOEXCEPT +# endif + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Borland +#ifdef __BORLANDC__ + +#if (__BORLANDC__ > 0x582 ) +//#define CATCH_CONFIG_SFINAE // Not confirmed +#endif + +#endif // __BORLANDC__ + +//////////////////////////////////////////////////////////////////////////////// +// EDG +#ifdef __EDG_VERSION__ + +#if (__EDG_VERSION__ > 238 ) +//#define CATCH_CONFIG_SFINAE // Not confirmed +#endif + +#endif // __EDG_VERSION__ + +//////////////////////////////////////////////////////////////////////////////// +// Digital Mars +#ifdef __DMC__ + +#if (__DMC__ > 0x840 ) +//#define CATCH_CONFIG_SFINAE // Not confirmed +#endif + +#endif // __DMC__ + +//////////////////////////////////////////////////////////////////////////////// +// GCC +#ifdef __GNUC__ + +#if __GNUC__ < 3 + +#if (__GNUC_MINOR__ >= 96 ) +//#define CATCH_CONFIG_SFINAE +#endif + +#elif __GNUC__ >= 3 + +// #define CATCH_CONFIG_SFINAE // Taking this out completely for now + +#endif // __GNUC__ < 3 + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) ) + +#define CATCH_CONFIG_CPP11_NULLPTR +#endif + +#endif // __GNUC__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +#if (_MSC_VER >= 1310 ) // (VC++ 7.0+) +//#define CATCH_CONFIG_SFINAE // Not confirmed +#endif + +#endif // _MSC_VER + +// Use variadic macros if the compiler supports them +#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ + ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ + ( defined __GNUC__ && __GNUC__ >= 3 ) || \ + ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) + +#ifndef CATCH_CONFIG_NO_VARIADIC_MACROS +#define CATCH_CONFIG_VARIADIC_MACROS +#endif + +#endif + +//////////////////////////////////////////////////////////////////////////////// +// C++ language feature support + +// detect language version: +#if (__cplusplus == 201103L) +# define CATCH_CPP11 +# define CATCH_CPP11_OR_GREATER +#elif (__cplusplus >= 201103L) +# define CATCH_CPP11_OR_GREATER +#endif + +// noexcept support: +#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) +# define CATCH_NOEXCEPT noexcept +# define CATCH_NOEXCEPT_IS(x) noexcept(x) +#else +# define CATCH_NOEXCEPT throw() +# define CATCH_NOEXCEPT_IS(x) +#endif + +namespace Catch { + + class NonCopyable { + NonCopyable( NonCopyable const& ); + void operator = ( NonCopyable const& ); + protected: + NonCopyable() {} + virtual ~NonCopyable(); + }; + + class SafeBool { + public: + typedef void (SafeBool::*type)() const; + + static type makeSafe( bool value ) { + return value ? &SafeBool::trueValue : 0; + } + private: + void trueValue() const {} + }; + + template + inline void deleteAll( ContainerT& container ) { + typename ContainerT::const_iterator it = container.begin(); + typename ContainerT::const_iterator itEnd = container.end(); + for(; it != itEnd; ++it ) + delete *it; + } + template + inline void deleteAllValues( AssociativeContainerT& container ) { + typename AssociativeContainerT::const_iterator it = container.begin(); + typename AssociativeContainerT::const_iterator itEnd = container.end(); + for(; it != itEnd; ++it ) + delete it->second; + } + + bool startsWith( std::string const& s, std::string const& prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; + + struct SourceLineInfo { + + SourceLineInfo(); + SourceLineInfo( char const* _file, std::size_t _line ); + SourceLineInfo( SourceLineInfo const& other ); +# ifdef CATCH_CPP11_OR_GREATER + SourceLineInfo( SourceLineInfo && ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo& operator = ( SourceLineInfo && ) = default; +# endif + bool empty() const; + bool operator == ( SourceLineInfo const& other ) const; + + std::string file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // This is just here to avoid compiler warnings with macro constants and boolean literals + inline bool isTrue( bool value ){ return value; } + inline bool alwaysTrue() { return true; } + inline bool alwaysFalse() { return false; } + + void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() { + return std::string(); + } + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) +#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); + +#include + +namespace Catch { + + class NotImplementedException : public std::exception + { + public: + NotImplementedException( SourceLineInfo const& lineInfo ); + NotImplementedException( NotImplementedException const& ) {} + + virtual ~NotImplementedException() CATCH_NOEXCEPT {} + + virtual const char* what() const CATCH_NOEXCEPT; + + private: + std::string m_what; + SourceLineInfo m_lineInfo; + }; + +} // end namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) + +// #included from: internal/catch_context.h +#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED + +// #included from: catch_interfaces_generators.h +#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED + +#include + +namespace Catch { + + struct IGeneratorInfo { + virtual ~IGeneratorInfo(); + virtual bool moveNext() = 0; + virtual std::size_t getCurrentIndex() const = 0; + }; + + struct IGeneratorsForTest { + virtual ~IGeneratorsForTest(); + + virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; + virtual bool moveNext() = 0; + }; + + IGeneratorsForTest* createGeneratorsForTest(); + +} // end namespace Catch + +// #included from: catch_ptr.hpp +#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + // An intrusive reference counting smart pointer. + // T must implement addRef() and release() methods + // typically implementing the IShared interface + template + class Ptr { + public: + Ptr() : m_p( NULL ){} + Ptr( T* p ) : m_p( p ){ + if( m_p ) + m_p->addRef(); + } + Ptr( Ptr const& other ) : m_p( other.m_p ){ + if( m_p ) + m_p->addRef(); + } + ~Ptr(){ + if( m_p ) + m_p->release(); + } + void reset() { + if( m_p ) + m_p->release(); + m_p = NULL; + } + Ptr& operator = ( T* p ){ + Ptr temp( p ); + swap( temp ); + return *this; + } + Ptr& operator = ( Ptr const& other ){ + Ptr temp( other ); + swap( temp ); + return *this; + } + void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } + T* get() { return m_p; } + const T* get() const{ return m_p; } + T& operator*() const { return *m_p; } + T* operator->() const { return m_p; } + bool operator !() const { return m_p == NULL; } + operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); } + + private: + T* m_p; + }; + + struct IShared : NonCopyable { + virtual ~IShared(); + virtual void addRef() const = 0; + virtual void release() const = 0; + }; + + template + struct SharedImpl : T { + + SharedImpl() : m_rc( 0 ){} + + virtual void addRef() const { + ++m_rc; + } + virtual void release() const { + if( --m_rc == 0 ) + delete this; + } + + mutable unsigned int m_rc; + }; + +} // end namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#include +#include +#include + +namespace Catch { + + class TestCase; + class Stream; + struct IResultCapture; + struct IRunner; + struct IGeneratorsForTest; + struct IConfig; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; + virtual bool advanceGeneratorsForCurrentTest() = 0; + virtual Ptr getConfig() const = 0; + }; + + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( Ptr const& config ) = 0; + }; + + IContext& getCurrentContext(); + IMutableContext& getCurrentMutableContext(); + void cleanUpContext(); + Stream createStream( std::string const& streamName ); + +} + +// #included from: internal/catch_test_registry.hpp +#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED + +// #included from: catch_interfaces_testcase.h +#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED + +#include + +namespace Catch { + + class TestSpec; + + struct ITestCase : IShared { + virtual void invoke () const = 0; + protected: + virtual ~ITestCase(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector& matchingTestCases ) const = 0; + + }; +} + +namespace Catch { + +template +class MethodTestCase : public SharedImpl { + +public: + MethodTestCase( void (C::*method)() ) : m_method( method ) {} + + virtual void invoke() const { + C obj; + (obj.*m_method)(); + } + +private: + virtual ~MethodTestCase() {} + + void (C::*m_method)(); +}; + +typedef void(*TestFunction)(); + +struct NameAndDesc { + NameAndDesc( const char* _name = "", const char* _description= "" ) + : name( _name ), description( _description ) + {} + + const char* name; + const char* description; +}; + +struct AutoReg { + + AutoReg( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ); + + template + AutoReg( void (C::*method)(), + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ) { + registerTestCase( new MethodTestCase( method ), + className, + nameAndDesc, + lineInfo ); + } + + void registerTestCase( ITestCase* testCase, + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ); + + ~AutoReg(); + +private: + AutoReg( AutoReg const& ); + void operator= ( AutoReg const& ); +}; + +} // end namespace Catch + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE( ... ) \ + static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ + static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\ + namespace{ \ + struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ + } \ + void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() + +#else + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ + static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ + static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ + namespace{ \ + struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ + } \ + void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() + +#endif + +// #included from: internal/catch_capture.hpp +#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED + +// #included from: catch_result_builder.h +#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED + +// #included from: catch_result_type.h +#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED + +namespace Catch { + + // ResultWas::OfType enum + struct ResultWas { enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2 + + }; }; + + inline bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + inline bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } + + // ResultDisposition::Flags enum + struct ResultDisposition { enum Flags { + Normal = 0x00, + + ContinueOnFailure = 0x01, // Failures fail test, but execution continues + FalseTest = 0x02, // Prefix expression with ! + SuppressFail = 0x04 // Failures are reported but do not fail the test + }; }; + + inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | static_cast( rhs ) ); + } + + inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch + +// #included from: catch_assertionresult.h +#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED + +#include + +namespace Catch { + + struct AssertionInfo + { + AssertionInfo() {} + AssertionInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition ); + + std::string macroName; + SourceLineInfo lineInfo; + std::string capturedExpression; + ResultDisposition::Flags resultDisposition; + }; + + struct AssertionResultData + { + AssertionResultData() : resultType( ResultWas::Unknown ) {} + + std::string reconstructedExpression; + std::string message; + ResultWas::OfType resultType; + }; + + class AssertionResult { + public: + AssertionResult(); + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + ~AssertionResult(); +# ifdef CATCH_CPP11_OR_GREATER + AssertionResult( AssertionResult const& ) = default; + AssertionResult( AssertionResult && ) = default; + AssertionResult& operator = ( AssertionResult const& ) = default; + AssertionResult& operator = ( AssertionResult && ) = default; +# endif + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + std::string getTestMacroName() const; + + protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; + +} // end namespace Catch + +namespace Catch { + + struct TestFailureException{}; + + template class ExpressionLhs; + + struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; + + struct CopyableStream { + CopyableStream() {} + CopyableStream( CopyableStream const& other ) { + oss << other.oss.str(); + } + CopyableStream& operator=( CopyableStream const& other ) { + oss.str(""); + oss << other.oss.str(); + return *this; + } + std::ostringstream oss; + }; + + class ResultBuilder { + public: + ResultBuilder( char const* macroName, + SourceLineInfo const& lineInfo, + char const* capturedExpression, + ResultDisposition::Flags resultDisposition ); + + template + ExpressionLhs operator->* ( T const& operand ); + ExpressionLhs operator->* ( bool value ); + + template + ResultBuilder& operator << ( T const& value ) { + m_stream.oss << value; + return *this; + } + + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); + + ResultBuilder& setResultType( ResultWas::OfType result ); + ResultBuilder& setResultType( bool result ); + ResultBuilder& setLhs( std::string const& lhs ); + ResultBuilder& setRhs( std::string const& rhs ); + ResultBuilder& setOp( std::string const& op ); + + void endExpression(); + + std::string reconstructExpression() const; + AssertionResult build() const; + + void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); + void captureResult( ResultWas::OfType resultType ); + void captureExpression(); + void react(); + bool shouldDebugBreak() const; + bool allowThrows() const; + + private: + AssertionInfo m_assertionInfo; + AssertionResultData m_data; + struct ExprComponents { + ExprComponents() : testFalse( false ) {} + bool testFalse; + std::string lhs, rhs, op; + } m_exprComponents; + CopyableStream m_stream; + + bool m_shouldDebugBreak; + bool m_shouldThrow; + }; + +} // namespace Catch + +// Include after due to circular dependency: +// #included from: catch_expression_lhs.hpp +#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED + +// #included from: catch_evaluate.hpp +#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#endif + +#include + +namespace Catch { +namespace Internal { + + enum Operator { + IsEqualTo, + IsNotEqualTo, + IsLessThan, + IsGreaterThan, + IsLessThanOrEqualTo, + IsGreaterThanOrEqualTo + }; + + template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; + template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; + template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; + template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; + template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; + template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; + template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; + + template + inline T& opCast(T const& t) { return const_cast(t); } + +// nullptr_t support based on pull request #154 from Konstantin Baumann +#ifdef CATCH_CONFIG_CPP11_NULLPTR + inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } +#endif // CATCH_CONFIG_CPP11_NULLPTR + + // So the compare overloads can be operator agnostic we convey the operator as a template + // enum, which is used to specialise an Evaluator for doing the comparison. + template + class Evaluator{}; + + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs) { + return opCast( lhs ) == opCast( rhs ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) != opCast( rhs ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) < opCast( rhs ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) > opCast( rhs ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) >= opCast( rhs ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) <= opCast( rhs ); + } + }; + + template + bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { + return Evaluator::evaluate( lhs, rhs ); + } + + // This level of indirection allows us to specialise for integer types + // to avoid signed/ unsigned warnings + + // "base" overload + template + bool compare( T1 const& lhs, T2 const& rhs ) { + return Evaluator::evaluate( lhs, rhs ); + } + + // unsigned X to int + template bool compare( unsigned int lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned long lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned char lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + + // unsigned X to long + template bool compare( unsigned int lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned long lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned char lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + + // int to unsigned X + template bool compare( int lhs, unsigned int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( int lhs, unsigned long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( int lhs, unsigned char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + + // long to unsigned X + template bool compare( long lhs, unsigned int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long lhs, unsigned long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long lhs, unsigned char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + + // pointer to long (when comparing against NULL) + template bool compare( long lhs, T* rhs ) { + return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); + } + template bool compare( T* lhs, long rhs ) { + return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); + } + + // pointer to int (when comparing against NULL) + template bool compare( int lhs, T* rhs ) { + return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); + } + template bool compare( T* lhs, int rhs ) { + return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); + } + +#ifdef CATCH_CONFIG_CPP11_NULLPTR + // pointer to nullptr_t (when comparing against nullptr) + template bool compare( std::nullptr_t, T* rhs ) { + return Evaluator::evaluate( NULL, rhs ); + } + template bool compare( T* lhs, std::nullptr_t ) { + return Evaluator::evaluate( lhs, NULL ); + } +#endif // CATCH_CONFIG_CPP11_NULLPTR + +} // end of namespace Internal +} // end of namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// #included from: catch_tostring.h +#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED + +// #included from: catch_sfinae.hpp +#define TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED + +// Try to detect if the current compiler supports SFINAE + +namespace Catch { + + struct TrueType { + static const bool value = true; + typedef void Enable; + char sizer[1]; + }; + struct FalseType { + static const bool value = false; + typedef void Disable; + char sizer[2]; + }; + +#ifdef CATCH_CONFIG_SFINAE + + template struct NotABooleanExpression; + + template struct If : NotABooleanExpression {}; + template<> struct If : TrueType {}; + template<> struct If : FalseType {}; + + template struct SizedIf; + template<> struct SizedIf : TrueType {}; + template<> struct SizedIf : FalseType {}; + +#endif // CATCH_CONFIG_SFINAE + +} // end namespace Catch + +#include +#include +#include +#include +#include + +#ifdef __OBJC__ +// #included from: catch_objc_arc.hpp +#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED + +#import + +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease( NSObject* obj ) { + [obj release]; +} +inline id performOptionalSelector( id obj, SEL sel ) { + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; + return nil; +} +#define CATCH_UNSAFE_UNRETAINED +#define CATCH_ARC_STRONG +#else +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return nil; +} +#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained +#define CATCH_ARC_STRONG __strong +#endif + +#endif + +namespace Catch { +namespace Detail { + +// SFINAE is currently disabled by default for all compilers. +// If the non SFINAE version of IsStreamInsertable is ambiguous for you +// and your compiler supports SFINAE, try #defining CATCH_CONFIG_SFINAE +#ifdef CATCH_CONFIG_SFINAE + + template + class IsStreamInsertableHelper { + template struct TrueIfSizeable : TrueType {}; + + template + static TrueIfSizeable dummy(T2*); + static FalseType dummy(...); + + public: + typedef SizedIf type; + }; + + template + struct IsStreamInsertable : IsStreamInsertableHelper::type {}; + +#else + + struct BorgType { + template BorgType( T const& ); + }; + + TrueType& testStreamable( std::ostream& ); + FalseType testStreamable( FalseType ); + + FalseType operator<<( std::ostream const&, BorgType const& ); + + template + struct IsStreamInsertable { + static std::ostream &s; + static T const&t; + enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; + }; + +#endif + + template + struct StringMakerBase { + template + static std::string convert( T const& ) { return "{?}"; } + }; + + template<> + struct StringMakerBase { + template + static std::string convert( T const& _value ) { + std::ostringstream oss; + oss << _value; + return oss.str(); + } + }; + + std::string rawMemoryToString( const void *object, std::size_t size ); + + template + inline std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } + +} // end namespace Detail + +template +std::string toString( T const& value ); + +template +struct StringMaker : + Detail::StringMakerBase::value> {}; + +template +struct StringMaker { + template + static std::string convert( U* p ) { + if( !p ) + return INTERNAL_CATCH_STRINGIFY( NULL ); + else + return Detail::rawMemoryToString( p ); + } +}; + +template +struct StringMaker { + static std::string convert( R C::* p ) { + if( !p ) + return INTERNAL_CATCH_STRINGIFY( NULL ); + else + return Detail::rawMemoryToString( p ); + } +}; + +namespace Detail { + template + std::string rangeToString( InputIterator first, InputIterator last ); +} + +template +struct StringMaker > { + static std::string convert( std::vector const& v ) { + return Detail::rangeToString( v.begin(), v.end() ); + } +}; + +namespace Detail { + template + std::string makeString( T const& value ) { + return StringMaker::convert( value ); + } +} // end namespace Detail + +/// \brief converts any type to a string +/// +/// The default template forwards on to ostringstream - except when an +/// ostringstream overload does not exist - in which case it attempts to detect +/// that and writes {?}. +/// Overload (not specialise) this template for custom typs that you don't want +/// to provide an ostream overload for. +template +std::string toString( T const& value ) { + return StringMaker::convert( value ); +} + +// Built in overloads + +std::string toString( std::string const& value ); +std::string toString( std::wstring const& value ); +std::string toString( const char* const value ); +std::string toString( char* const value ); +std::string toString( const wchar_t* const value ); +std::string toString( wchar_t* const value ); +std::string toString( int value ); +std::string toString( unsigned long value ); +std::string toString( unsigned int value ); +std::string toString( const double value ); +std::string toString( const float value ); +std::string toString( bool value ); +std::string toString( char value ); +std::string toString( signed char value ); +std::string toString( unsigned char value ); + +#ifdef CATCH_CONFIG_CPP11_NULLPTR +std::string toString( std::nullptr_t ); +#endif + +#ifdef __OBJC__ + std::string toString( NSString const * const& nsstring ); + std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); + std::string toString( NSObject* const& nsObject ); +#endif + + namespace Detail { + template + std::string rangeToString( InputIterator first, InputIterator last ) { + std::ostringstream oss; + oss << "{ "; + if( first != last ) { + oss << toString( *first ); + for( ++first ; first != last ; ++first ) { + oss << ", " << toString( *first ); + } + } + oss << " }"; + return oss.str(); + } +} + +} // end namespace Catch + +namespace Catch { + +// Wraps the LHS of an expression and captures the operator and RHS (if any) - +// wrapping them all in a ResultBuilder object +template +class ExpressionLhs { + ExpressionLhs& operator = ( ExpressionLhs const& ); +# ifdef CATCH_CPP11_OR_GREATER + ExpressionLhs& operator = ( ExpressionLhs && ) = delete; +# endif + +public: + ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {} +# ifdef CATCH_CPP11_OR_GREATER + ExpressionLhs( ExpressionLhs const& ) = default; + ExpressionLhs( ExpressionLhs && ) = default; +# endif + + template + ResultBuilder& operator == ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator != ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator < ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator > ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator <= ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator >= ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + ResultBuilder& operator == ( bool rhs ) { + return captureExpression( rhs ); + } + + ResultBuilder& operator != ( bool rhs ) { + return captureExpression( rhs ); + } + + void endExpression() { + bool value = m_lhs ? true : false; + m_rb + .setLhs( Catch::toString( value ) ) + .setResultType( value ) + .endExpression(); + } + + // Only simple binary expressions are allowed on the LHS. + // If more complex compositions are required then place the sub expression in parentheses + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); + +private: + template + ResultBuilder& captureExpression( RhsT const& rhs ) { + return m_rb + .setResultType( Internal::compare( m_lhs, rhs ) ) + .setLhs( Catch::toString( m_lhs ) ) + .setRhs( Catch::toString( rhs ) ) + .setOp( Internal::OperatorTraits::getName() ); + } + +private: + ResultBuilder& m_rb; + T m_lhs; +}; + +} // end namespace Catch + + +namespace Catch { + + template + inline ExpressionLhs ResultBuilder::operator->* ( T const& operand ) { + return ExpressionLhs( *this, operand ); + } + + inline ExpressionLhs ResultBuilder::operator->* ( bool value ) { + return ExpressionLhs( *this, value ); + } + +} // namespace Catch + +// #included from: catch_message.h +#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED + +#include + +namespace Catch { + + struct MessageInfo { + MessageInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); + + std::string macroName; + SourceLineInfo lineInfo; + ResultWas::OfType type; + std::string message; + unsigned int sequence; + + bool operator == ( MessageInfo const& other ) const { + return sequence == other.sequence; + } + bool operator < ( MessageInfo const& other ) const { + return sequence < other.sequence; + } + private: + static unsigned int globalCount; + }; + + struct MessageBuilder { + MessageBuilder( std::string const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + : m_info( macroName, lineInfo, type ) + {} + + template + MessageBuilder& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + MessageInfo m_info; + std::ostringstream m_stream; + }; + + class ScopedMessage { + public: + ScopedMessage( MessageBuilder const& builder ); + ScopedMessage( ScopedMessage const& other ); + ~ScopedMessage(); + + MessageInfo m_info; + }; + +} // end namespace Catch + +// #included from: catch_interfaces_capture.h +#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED + +#include + +namespace Catch { + + class TestCase; + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct MessageInfo; + class ScopedMessageBuilder; + struct Counts; + + struct IResultCapture { + + virtual ~IResultCapture(); + + virtual void assertionEnded( AssertionResult const& result ) = 0; + virtual bool sectionStarted( SectionInfo const& sectionInfo, + Counts& assertions ) = 0; + virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0; + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 0; + + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + }; + + IResultCapture& getResultCapture(); +} + +// #included from: catch_debugger.h +#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED + +// #included from: catch_platform.h +#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED + +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#define CATCH_PLATFORM_MAC +#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#define CATCH_PLATFORM_IPHONE +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#define CATCH_PLATFORM_WINDOWS +#endif + +#include + +namespace Catch{ + + bool isDebuggerActive(); + void writeToDebugConsole( std::string const& text ); +} + +#ifdef CATCH_PLATFORM_MAC + + // The following code snippet based on: + // http://cocoawithlove.com/2008/03/break-into-debugger.html + #ifdef DEBUG + #if defined(__ppc64__) || defined(__ppc__) + #define CATCH_BREAK_INTO_DEBUGGER() \ + if( Catch::isDebuggerActive() ) { \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ + : : : "memory","r0","r3","r4" ); \ + } + #else + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );} + #endif + #endif + +#elif defined(_MSC_VER) + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) void __stdcall DebugBreak(); + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); } +#endif + +#ifndef CATCH_BREAK_INTO_DEBUGGER +#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); +#endif + +// #included from: catch_interfaces_runner.h +#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED + +namespace Catch { + class TestCase; + + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; +} + +/////////////////////////////////////////////////////////////////////////////// +// In the event of a failure works out if the debugger needs to be invoked +// and/or an exception thrown and takes appropriate action. +// This needs to be done as a macro so the debugger will stop in the user +// source code rather than in Catch library code +#define INTERNAL_CATCH_REACT( resultBuilder ) \ + if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ + resultBuilder.react(); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + try { \ + ( __catchResult->*expr ).endExpression(); \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ + INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ + if( Catch::getResultCapture().getLastResult()->succeeded() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ + INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ + if( !Catch::getResultCapture().getLastResult()->succeeded() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + try { \ + expr; \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( resultDisposition ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + if( __catchResult.allowThrows() ) \ + try { \ + expr; \ + __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + } \ + catch( ... ) { \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + } \ + else \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + if( __catchResult.allowThrows() ) \ + try { \ + expr; \ + __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + } \ + catch( exceptionType ) { \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( resultDisposition ); \ + } \ + else \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ + __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ + __catchResult.captureResult( messageType ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) +#else + #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ + __catchResult << log + ::Catch::StreamEndStop(); \ + __catchResult.captureResult( messageType ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) +#endif + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO( log, macroName ) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \ + try { \ + std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \ + __catchResult \ + .setLhs( Catch::toString( arg ) ) \ + .setRhs( matcherAsString == "{?}" ? #matcher : matcherAsString ) \ + .setOp( "matches" ) \ + .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \ + __catchResult.captureExpression(); \ + } catch( ... ) { \ + __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +// #included from: internal/catch_section.h +#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED + +// #included from: catch_section_info.h +#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED + +namespace Catch { + + struct SectionInfo { + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& _description = std::string() ); + + std::string name; + std::string description; + SourceLineInfo lineInfo; + }; + +} // end namespace Catch + +// #included from: catch_totals.hpp +#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED + +#include + +namespace Catch { + + struct Counts { + Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} + + Counts operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } + Counts& operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } + + std::size_t total() const { + return passed + failed + failedButOk; + } + bool allPassed() const { + return failed == 0 && failedButOk == 0; + } + + std::size_t passed; + std::size_t failed; + std::size_t failedButOk; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } + + Totals& operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Counts assertions; + Counts testCases; + }; +} + +// #included from: catch_timer.h +#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED + +#ifdef CATCH_PLATFORM_WINDOWS +typedef unsigned long long uint64_t; +#else +#include +#endif + +namespace Catch { + + class Timer { + public: + Timer() : m_ticks( 0 ) {} + void start(); + unsigned int getElapsedNanoseconds() const; + unsigned int getElapsedMilliseconds() const; + double getElapsedSeconds() const; + + private: + uint64_t m_ticks; + }; + +} // namespace Catch + +#include + +namespace Catch { + + class Section { + public: + Section( SectionInfo const& info ); + ~Section(); + + // This indicates whether the section should be executed or not + operator bool() const; + + private: +#ifdef CATCH_CPP11_OR_GREATER + Section( Section const& ) = delete; + Section( Section && ) = delete; + Section& operator = ( Section const& ) = delete; + Section& operator = ( Section && ) = delete; +#else + Section( Section const& info ); + Section& operator = ( Section const& ); +#endif + SectionInfo m_info; + + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; + +} // end namespace Catch + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define INTERNAL_CATCH_SECTION( ... ) \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) +#else + #define INTERNAL_CATCH_SECTION( name, desc ) \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) +#endif + +// #included from: internal/catch_generators.hpp +#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + +template +struct IGenerator { + virtual ~IGenerator() {} + virtual T getValue( std::size_t index ) const = 0; + virtual std::size_t size () const = 0; +}; + +template +class BetweenGenerator : public IGenerator { +public: + BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} + + virtual T getValue( std::size_t index ) const { + return m_from+static_cast( index ); + } + + virtual std::size_t size() const { + return static_cast( 1+m_to-m_from ); + } + +private: + + T m_from; + T m_to; +}; + +template +class ValuesGenerator : public IGenerator { +public: + ValuesGenerator(){} + + void add( T value ) { + m_values.push_back( value ); + } + + virtual T getValue( std::size_t index ) const { + return m_values[index]; + } + + virtual std::size_t size() const { + return m_values.size(); + } + +private: + std::vector m_values; +}; + +template +class CompositeGenerator { +public: + CompositeGenerator() : m_totalSize( 0 ) {} + + // *** Move semantics, similar to auto_ptr *** + CompositeGenerator( CompositeGenerator& other ) + : m_fileInfo( other.m_fileInfo ), + m_totalSize( 0 ) + { + move( other ); + } + + CompositeGenerator& setFileInfo( const char* fileInfo ) { + m_fileInfo = fileInfo; + return *this; + } + + ~CompositeGenerator() { + deleteAll( m_composed ); + } + + operator T () const { + size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); + + typename std::vector*>::const_iterator it = m_composed.begin(); + typename std::vector*>::const_iterator itEnd = m_composed.end(); + for( size_t index = 0; it != itEnd; ++it ) + { + const IGenerator* generator = *it; + if( overallIndex >= index && overallIndex < index + generator->size() ) + { + return generator->getValue( overallIndex-index ); + } + index += generator->size(); + } + CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); + return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so + } + + void add( const IGenerator* generator ) { + m_totalSize += generator->size(); + m_composed.push_back( generator ); + } + + CompositeGenerator& then( CompositeGenerator& other ) { + move( other ); + return *this; + } + + CompositeGenerator& then( T value ) { + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( value ); + add( valuesGen ); + return *this; + } + +private: + + void move( CompositeGenerator& other ) { + std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); + m_totalSize += other.m_totalSize; + other.m_composed.clear(); + } + + std::vector*> m_composed; + std::string m_fileInfo; + size_t m_totalSize; +}; + +namespace Generators +{ + template + CompositeGenerator between( T from, T to ) { + CompositeGenerator generators; + generators.add( new BetweenGenerator( from, to ) ); + return generators; + } + + template + CompositeGenerator values( T val1, T val2 ) { + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + generators.add( valuesGen ); + return generators; + } + + template + CompositeGenerator values( T val1, T val2, T val3 ){ + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + valuesGen->add( val3 ); + generators.add( valuesGen ); + return generators; + } + + template + CompositeGenerator values( T val1, T val2, T val3, T val4 ) { + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + valuesGen->add( val3 ); + valuesGen->add( val4 ); + generators.add( valuesGen ); + return generators; + } + +} // end namespace Generators + +using namespace Generators; + +} // end namespace Catch + +#define INTERNAL_CATCH_LINESTR2( line ) #line +#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) + +#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) + +// #included from: internal/catch_interfaces_exception.h +#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED + +#include +// #included from: catch_interfaces_registry_hub.h +#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED + +#include + +namespace Catch { + + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + + struct IRegistryHub { + virtual ~IRegistryHub(); + + virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; + }; + + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0; + virtual void registerTest( TestCase const& testInfo ) = 0; + virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + }; + + IRegistryHub& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); + +} + + +namespace Catch { + + typedef std::string(*exceptionTranslateFunction)(); + + struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string translate() const = 0; + }; + + struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); + + virtual std::string translateActiveException() const = 0; + }; + + class ExceptionTranslatorRegistrar { + template + class ExceptionTranslator : public IExceptionTranslator { + public: + + ExceptionTranslator( std::string(*translateFunction)( T& ) ) + : m_translateFunction( translateFunction ) + {} + + virtual std::string translate() const { + try { + throw; + } + catch( T& ex ) { + return m_translateFunction( ex ); + } + } + + protected: + std::string(*m_translateFunction)( T& ); + }; + + public: + template + ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { + getMutableRegistryHub().registerTranslator + ( new ExceptionTranslator( translateFunction ) ); + } + }; +} + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \ + static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\ + static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ) + +// #included from: internal/catch_approx.hpp +#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED + +#include +#include + +namespace Catch { +namespace Detail { + + class Approx { + public: + explicit Approx ( double value ) + : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_scale( 1.0 ), + m_value( value ) + {} + + Approx( Approx const& other ) + : m_epsilon( other.m_epsilon ), + m_scale( other.m_scale ), + m_value( other.m_value ) + {} + + static Approx custom() { + return Approx( 0 ); + } + + Approx operator()( double value ) { + Approx approx( value ); + approx.epsilon( m_epsilon ); + approx.scale( m_scale ); + return approx; + } + + friend bool operator == ( double lhs, Approx const& rhs ) { + // Thanks to Richard Harris for his help refining this formula + return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) ); + } + + friend bool operator == ( Approx const& lhs, double rhs ) { + return operator==( rhs, lhs ); + } + + friend bool operator != ( double lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + friend bool operator != ( Approx const& lhs, double rhs ) { + return !operator==( rhs, lhs ); + } + + Approx& epsilon( double newEpsilon ) { + m_epsilon = newEpsilon; + return *this; + } + + Approx& scale( double newScale ) { + m_scale = newScale; + return *this; + } + + std::string toString() const { + std::ostringstream oss; + oss << "Approx( " << Catch::toString( m_value ) << " )"; + return oss.str(); + } + + private: + double m_epsilon; + double m_scale; + double m_value; + }; +} + +template<> +inline std::string toString( Detail::Approx const& value ) { + return value.toString(); +} + +} // end namespace Catch + +// #included from: internal/catch_matchers.hpp +#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED + +namespace Catch { +namespace Matchers { + namespace Impl { + + template + struct Matcher : SharedImpl + { + typedef ExpressionT ExpressionType; + + virtual ~Matcher() {} + virtual Ptr clone() const = 0; + virtual bool match( ExpressionT const& expr ) const = 0; + virtual std::string toString() const = 0; + }; + + template + struct MatcherImpl : Matcher { + + virtual Ptr > clone() const { + return Ptr >( new DerivedT( static_cast( *this ) ) ); + } + }; + + namespace Generic { + + template + class AllOf : public MatcherImpl, ExpressionT> { + public: + + AllOf() {} + AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} + + AllOf& add( Matcher const& matcher ) { + m_matchers.push_back( matcher.clone() ); + return *this; + } + virtual bool match( ExpressionT const& expr ) const + { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) + if( !m_matchers[i]->match( expr ) ) + return false; + return true; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + oss << " and "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + private: + std::vector > > m_matchers; + }; + + template + class AnyOf : public MatcherImpl, ExpressionT> { + public: + + AnyOf() {} + AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} + + AnyOf& add( Matcher const& matcher ) { + m_matchers.push_back( matcher.clone() ); + return *this; + } + virtual bool match( ExpressionT const& expr ) const + { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) + if( m_matchers[i]->match( expr ) ) + return true; + return false; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + oss << " or "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + private: + std::vector > > m_matchers; + }; + + } + + namespace StdString { + + inline std::string makeString( std::string const& str ) { return str; } + inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } + + struct Equals : MatcherImpl { + Equals( std::string const& str ) : m_str( str ){} + Equals( Equals const& other ) : m_str( other.m_str ){} + + virtual ~Equals(); + + virtual bool match( std::string const& expr ) const { + return m_str == expr; + } + virtual std::string toString() const { + return "equals: \"" + m_str + "\""; + } + + std::string m_str; + }; + + struct Contains : MatcherImpl { + Contains( std::string const& substr ) : m_substr( substr ){} + Contains( Contains const& other ) : m_substr( other.m_substr ){} + + virtual ~Contains(); + + virtual bool match( std::string const& expr ) const { + return expr.find( m_substr ) != std::string::npos; + } + virtual std::string toString() const { + return "contains: \"" + m_substr + "\""; + } + + std::string m_substr; + }; + + struct StartsWith : MatcherImpl { + StartsWith( std::string const& substr ) : m_substr( substr ){} + StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){} + + virtual ~StartsWith(); + + virtual bool match( std::string const& expr ) const { + return expr.find( m_substr ) == 0; + } + virtual std::string toString() const { + return "starts with: \"" + m_substr + "\""; + } + + std::string m_substr; + }; + + struct EndsWith : MatcherImpl { + EndsWith( std::string const& substr ) : m_substr( substr ){} + EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){} + + virtual ~EndsWith(); + + virtual bool match( std::string const& expr ) const { + return expr.find( m_substr ) == expr.size() - m_substr.size(); + } + virtual std::string toString() const { + return "ends with: \"" + m_substr + "\""; + } + + std::string m_substr; + }; + } // namespace StdString + } // namespace Impl + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + template + inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, + Impl::Matcher const& m2 ) { + return Impl::Generic::AllOf().add( m1 ).add( m2 ); + } + template + inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, + Impl::Matcher const& m2, + Impl::Matcher const& m3 ) { + return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); + } + template + inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, + Impl::Matcher const& m2 ) { + return Impl::Generic::AnyOf().add( m1 ).add( m2 ); + } + template + inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, + Impl::Matcher const& m2, + Impl::Matcher const& m3 ) { + return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); + } + + inline Impl::StdString::Equals Equals( std::string const& str ) { + return Impl::StdString::Equals( str ); + } + inline Impl::StdString::Equals Equals( const char* str ) { + return Impl::StdString::Equals( Impl::StdString::makeString( str ) ); + } + inline Impl::StdString::Contains Contains( std::string const& substr ) { + return Impl::StdString::Contains( substr ); + } + inline Impl::StdString::Contains Contains( const char* substr ) { + return Impl::StdString::Contains( Impl::StdString::makeString( substr ) ); + } + inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { + return Impl::StdString::StartsWith( substr ); + } + inline Impl::StdString::StartsWith StartsWith( const char* substr ) { + return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); + } + inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { + return Impl::StdString::EndsWith( substr ); + } + inline Impl::StdString::EndsWith EndsWith( const char* substr ) { + return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); + } + +} // namespace Matchers + +using namespace Matchers; + +} // namespace Catch + +// #included from: internal/catch_interfaces_tag_alias_registry.h +#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED + +// #included from: catch_tag_alias.h +#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED + +#include + +namespace Catch { + + struct TagAlias { + TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} + + std::string tag; + SourceLineInfo lineInfo; + }; + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } +// #included from: catch_option.hpp +#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED + +namespace Catch { + + // An optional type + template + class Option { + public: + Option() : nullableValue( NULL ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : NULL ) + {} + + ~Option() { + reset(); + } + + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; + } + + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = NULL; + } + + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } + + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != NULL; } + bool none() const { return nullableValue == NULL; } + + bool operator !() const { return nullableValue == NULL; } + operator SafeBool::type() const { + return SafeBool::makeSafe( some() ); + } + + private: + T* nullableValue; + char storage[sizeof(T)]; + }; + +} // end namespace Catch + +namespace Catch { + + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + virtual Option find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + + static ITagAliasRegistry const& get(); + }; + +} // end namespace Catch + +// These files are included here so the single_include script doesn't put them +// in the conditionally compiled sections +// #included from: internal/catch_test_case_info.h +#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED + +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + struct ITestCase; + + struct TestCaseInfo { + enum SpecialProperties{ + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4 + }; + + TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set const& _tags, + SourceLineInfo const& _lineInfo ); + + TestCaseInfo( TestCaseInfo const& other ); + + bool isHidden() const; + bool throws() const; + bool okToFail() const; + bool expectedToFail() const; + + std::string name; + std::string className; + std::string description; + std::set tags; + std::set lcaseTags; + std::string tagsAsString; + SourceLineInfo lineInfo; + SpecialProperties properties; + }; + + class TestCase : public TestCaseInfo { + public: + + TestCase( ITestCase* testCase, TestCaseInfo const& info ); + TestCase( TestCase const& other ); + + TestCase withName( std::string const& _newName ) const; + + void invoke() const; + + TestCaseInfo const& getTestCaseInfo() const; + + void swap( TestCase& other ); + bool operator == ( TestCase const& other ) const; + bool operator < ( TestCase const& other ) const; + TestCase& operator = ( TestCase const& other ); + + private: + Ptr test; + }; + + TestCase makeTestCase( ITestCase* testCase, + std::string const& className, + std::string const& name, + std::string const& description, + SourceLineInfo const& lineInfo ); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + +#ifdef __OBJC__ +// #included from: internal/catch_objc.hpp +#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED + +#import + +#include + +// NB. Any general catch headers included here must be included +// in catch.hpp first to make sure they are included by the single +// header for non obj-usage + +/////////////////////////////////////////////////////////////////////////////// +// This protocol is really only here for (self) documenting purposes, since +// all its methods are optional. +@protocol OcFixture + +@optional + +-(void) setUp; +-(void) tearDown; + +@end + +namespace Catch { + + class OcMethod : public SharedImpl { + + public: + OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} + + virtual void invoke() const { + id obj = [[m_cls alloc] init]; + + performOptionalSelector( obj, @selector(setUp) ); + performOptionalSelector( obj, m_sel ); + performOptionalSelector( obj, @selector(tearDown) ); + + arcSafeRelease( obj ); + } + private: + virtual ~OcMethod() {} + + Class m_cls; + SEL m_sel; + }; + + namespace Detail{ + + inline std::string getAnnotation( Class cls, + std::string const& annotationName, + std::string const& testCaseName ) { + NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; + SEL sel = NSSelectorFromString( selStr ); + arcSafeRelease( selStr ); + id value = performOptionalSelector( cls, sel ); + if( value ) + return [(NSString*)value UTF8String]; + return ""; + } + } + + inline size_t registerTestMethods() { + size_t noTestMethods = 0; + int noClasses = objc_getClassList( NULL, 0 ); + + Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); + objc_getClassList( classes, noClasses ); + + for( int c = 0; c < noClasses; c++ ) { + Class cls = classes[c]; + { + u_int count; + Method* methods = class_copyMethodList( cls, &count ); + for( u_int m = 0; m < count ; m++ ) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if( startsWith( methodName, "Catch_TestCase_" ) ) { + std::string testCaseName = methodName.substr( 15 ); + std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); + std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); + const char* className = class_getName( cls ); + + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; + } + + namespace Matchers { + namespace Impl { + namespace NSStringMatchers { + + template + struct StringHolder : MatcherImpl{ + StringHolder( NSString* substr ) : m_substr( [substr copy] ){} + StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} + StringHolder() { + arcSafeRelease( m_substr ); + } + + NSString* m_substr; + }; + + struct Equals : StringHolder { + Equals( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str isEqualToString:m_substr]; + } + + virtual std::string toString() const { + return "equals string: " + Catch::toString( m_substr ); + } + }; + + struct Contains : StringHolder { + Contains( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location != NSNotFound; + } + + virtual std::string toString() const { + return "contains string: " + Catch::toString( m_substr ); + } + }; + + struct StartsWith : StringHolder { + StartsWith( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == 0; + } + + virtual std::string toString() const { + return "starts with: " + Catch::toString( m_substr ); + } + }; + struct EndsWith : StringHolder { + EndsWith( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == [str length] - [m_substr length]; + } + + virtual std::string toString() const { + return "ends with: " + Catch::toString( m_substr ); + } + }; + + } // namespace NSStringMatchers + } // namespace Impl + + inline Impl::NSStringMatchers::Equals + Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } + + inline Impl::NSStringMatchers::Contains + Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } + + inline Impl::NSStringMatchers::StartsWith + StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } + + inline Impl::NSStringMatchers::EndsWith + EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } + + } // namespace Matchers + + using namespace Matchers; + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define OC_TEST_CASE( name, desc )\ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ +{\ +return @ name; \ +}\ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ +{ \ +return @ desc; \ +} \ +-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) + +#endif + +#ifdef CATCH_CONFIG_RUNNER +// #included from: internal/catch_impl.hpp +#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED + +// Collect all the implementation files together here +// These are the equivalent of what would usually be cpp files + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// #included from: catch_runner.hpp +#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED + +// #included from: internal/catch_commandline.hpp +#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED + +// #included from: catch_config.hpp +#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED + +// #included from: catch_test_spec_parser.hpp +#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// #included from: catch_test_spec.hpp +#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +#include +#include + +namespace Catch { + + class TestSpec { + struct Pattern : SharedImpl<> { + virtual ~Pattern(); + virtual bool matches( TestCaseInfo const& testCase ) const = 0; + }; + class NamePattern : public Pattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) { + if( startsWith( m_name, "*" ) ) { + m_name = m_name.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_name, "*" ) ) { + m_name = m_name.substr( 0, m_name.size()-1 ); + m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); + } + } + virtual ~NamePattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_name == toLower( testCase.name ); + case WildcardAtStart: + return endsWith( toLower( testCase.name ), m_name ); + case WildcardAtEnd: + return startsWith( toLower( testCase.name ), m_name ); + case WildcardAtBothEnds: + return contains( toLower( testCase.name ), m_name ); + } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + throw std::logic_error( "Unknown enum" ); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } + private: + std::string m_name; + WildcardPosition m_wildcard; + }; + class TagPattern : public Pattern { + public: + TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + virtual ~TagPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { + return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); + } + private: + std::string m_tag; + }; + class ExcludedPattern : public Pattern { + public: + ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + virtual ~ExcludedPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + private: + Ptr m_underlyingPattern; + }; + + struct Filter { + std::vector > m_patterns; + + bool matches( TestCaseInfo const& testCase ) const { + // All patterns in a filter must match for the filter to be a match + for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) + if( !(*it)->matches( testCase ) ) + return false; + return true; + } + }; + + public: + bool hasFilters() const { + return !m_filters.empty(); + } + bool matches( TestCaseInfo const& testCase ) const { + // A TestSpec matches if any filter matches + for( std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) + if( it->matches( testCase ) ) + return true; + return false; + } + + private: + std::vector m_filters; + + friend class TestSpecParser; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +namespace Catch { + + class TestSpecParser { + enum Mode{ None, Name, QuotedName, Tag }; + Mode m_mode; + bool m_exclusion; + std::size_t m_start, m_pos; + std::string m_arg; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const* m_tagAliases; + + public: + TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + visitChar( m_arg[m_pos] ); + if( m_mode == Name ) + addPattern(); + return *this; + } + TestSpec testSpec() { + addFilter(); + return m_testSpec; + } + private: + void visitChar( char c ) { + if( m_mode == None ) { + switch( c ) { + case ' ': return; + case '~': m_exclusion = true; return; + case '[': return startNewMode( Tag, ++m_pos ); + case '"': return startNewMode( QuotedName, ++m_pos ); + default: startNewMode( Name, m_pos ); break; + } + } + if( m_mode == Name ) { + if( c == ',' ) { + addPattern(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern(); + startNewMode( Tag, ++m_pos ); + } + } + else if( m_mode == QuotedName && c == '"' ) + addPattern(); + else if( m_mode == Tag && c == ']' ) + addPattern(); + } + void startNewMode( Mode mode, std::size_t start ) { + m_mode = mode; + m_start = start; + } + std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + template + void addPattern() { + std::string token = subString(); + if( startsWith( token, "exclude:" ) ) { + m_exclusion = true; + token = token.substr( 8 ); + } + if( !token.empty() ) { + Ptr pattern = new T( token ); + if( m_exclusion ) + pattern = new TestSpec::ExcludedPattern( pattern ); + m_currentFilter.m_patterns.push_back( pattern ); + } + m_exclusion = false; + m_mode = None; + } + void addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); + } + } + }; + inline TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// #included from: catch_interfaces_config.h +#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED + +#include +#include +#include + +namespace Catch { + + struct Verbosity { enum Level { + NoOutput = 0, + Quiet, + Normal + }; }; + + struct WarnAbout { enum What { + Nothing = 0x00, + NoAssertions = 0x01 + }; }; + + struct ShowDurations { enum OrNot { + DefaultForReporter, + Always, + Never + }; }; + + class TestSpec; + + struct IConfig : IShared { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual TestSpec const& testSpec() const = 0; + }; +} + +// #included from: catch_stream.h +#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED + +#include + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + class Stream { + public: + Stream(); + Stream( std::streambuf* _streamBuf, bool _isOwned ); + void release(); + + std::streambuf* streamBuf; + + private: + bool isOwned; + }; +} + +#include +#include +#include +#include + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + + struct ConfigData { + + ConfigData() + : listTests( false ), + listTags( false ), + listReporters( false ), + listTestNamesOnly( false ), + showSuccessfulTests( false ), + shouldDebugBreak( false ), + noThrow( false ), + showHelp( false ), + showInvisibles( false ), + abortAfter( -1 ), + verbosity( Verbosity::Normal ), + warnings( WarnAbout::Nothing ), + showDurations( ShowDurations::DefaultForReporter ) + {} + + bool listTests; + bool listTags; + bool listReporters; + bool listTestNamesOnly; + + bool showSuccessfulTests; + bool shouldDebugBreak; + bool noThrow; + bool showHelp; + bool showInvisibles; + + int abortAfter; + + Verbosity::Level verbosity; + WarnAbout::What warnings; + ShowDurations::OrNot showDurations; + + std::string reporterName; + std::string outputFilename; + std::string name; + std::string processName; + + std::vector testsOrTags; + }; + + class Config : public SharedImpl { + private: + Config( Config const& other ); + Config& operator = ( Config const& other ); + virtual void dummy(); + public: + + Config() + : m_os( std::cout.rdbuf() ) + {} + + Config( ConfigData const& data ) + : m_data( data ), + m_os( std::cout.rdbuf() ) + { + if( !data.testsOrTags.empty() ) { + TestSpecParser parser( ITagAliasRegistry::get() ); + for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) + parser.parse( data.testsOrTags[i] ); + m_testSpec = parser.testSpec(); + } + } + + virtual ~Config() { + m_os.rdbuf( std::cout.rdbuf() ); + m_stream.release(); + } + + void setFilename( std::string const& filename ) { + m_data.outputFilename = filename; + } + + std::string const& getFilename() const { + return m_data.outputFilename ; + } + + bool listTests() const { return m_data.listTests; } + bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool listTags() const { return m_data.listTags; } + bool listReporters() const { return m_data.listReporters; } + + std::string getProcessName() const { return m_data.processName; } + + bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } + + void setStreamBuf( std::streambuf* buf ) { + m_os.rdbuf( buf ? buf : std::cout.rdbuf() ); + } + + void useStream( std::string const& streamName ) { + Stream stream = createStream( streamName ); + setStreamBuf( stream.streamBuf ); + m_stream.release(); + m_stream = stream; + } + + std::string getReporterName() const { return m_data.reporterName; } + + int abortAfter() const { return m_data.abortAfter; } + + TestSpec const& testSpec() const { return m_testSpec; } + + bool showHelp() const { return m_data.showHelp; } + bool showInvisibles() const { return m_data.showInvisibles; } + + // IConfig interface + virtual bool allowThrows() const { return !m_data.noThrow; } + virtual std::ostream& stream() const { return m_os; } + virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } + virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } + + private: + ConfigData m_data; + + Stream m_stream; + mutable std::ostream m_os; + TestSpec m_testSpec; + }; + +} // end namespace Catch + +// #included from: catch_clara.h +#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH +#undef CLARA_CONFIG_CONSOLE_WIDTH +#endif +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +// Declare Clara inside the Catch namespace +#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { +// #included from: ../external/clara.h + +// Only use header guard if we are not using an outer namespace +#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) + +#ifndef STITCH_CLARA_OPEN_NAMESPACE +#define TWOBLUECUBES_CLARA_H_INCLUDED +#define STITCH_CLARA_OPEN_NAMESPACE +#define STITCH_CLARA_CLOSE_NAMESPACE +#else +#define STITCH_CLARA_CLOSE_NAMESPACE } +#endif + +#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE + +// ----------- #included from tbc_text_format.h ----------- + +// Only use header guard if we are not using an outer namespace +#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) +#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +#define TBC_TEXT_FORMAT_H_INCLUDED +#endif + +#include +#include +#include + +// Use optional outer namespace +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + struct TextAttributes { + TextAttributes() + : initialIndent( std::string::npos ), + indent( 0 ), + width( consoleWidth-1 ), + tabChar( '\t' ) + {} + + TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } + TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } + TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } + TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } + + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + char tabChar; // If this char is seen the indent is changed to current pos + }; + + class Text { + public: + Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) + : attr( _attr ) + { + std::string wrappableChars = " [({.,/|\\-"; + std::size_t indent = _attr.initialIndent != std::string::npos + ? _attr.initialIndent + : _attr.indent; + std::string remainder = _str; + + while( !remainder.empty() ) { + if( lines.size() >= 1000 ) { + lines.push_back( "... message truncated due to excessive size" ); + return; + } + std::size_t tabPos = std::string::npos; + std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); + std::size_t pos = remainder.find_first_of( '\n' ); + if( pos <= width ) { + width = pos; + } + pos = remainder.find_last_of( _attr.tabChar, width ); + if( pos != std::string::npos ) { + tabPos = pos; + if( remainder[width] == '\n' ) + width--; + remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); + } + + if( width == remainder.size() ) { + spliceLine( indent, remainder, width ); + } + else if( remainder[width] == '\n' ) { + spliceLine( indent, remainder, width ); + if( width <= 1 || remainder.size() != 1 ) + remainder = remainder.substr( 1 ); + indent = _attr.indent; + } + else { + pos = remainder.find_last_of( wrappableChars, width ); + if( pos != std::string::npos && pos > 0 ) { + spliceLine( indent, remainder, pos ); + if( remainder[0] == ' ' ) + remainder = remainder.substr( 1 ); + } + else { + spliceLine( indent, remainder, width-1 ); + lines.back() += "-"; + } + if( lines.size() == 1 ) + indent = _attr.indent; + if( tabPos != std::string::npos ) + indent += tabPos; + } + } + } + + void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { + lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); + _remainder = _remainder.substr( _pos ); + } + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it ) { + if( it != _text.begin() ) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector lines; + }; + +} // end namespace Tbc + +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TBC_TEXT_FORMAT_H_INCLUDED + +// ----------- end of #include from tbc_text_format.h ----------- +// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h + +#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE + +#include +#include +#include +#include + +// Use optional outer namespace +#ifdef STITCH_CLARA_OPEN_NAMESPACE +STITCH_CLARA_OPEN_NAMESPACE +#endif + +namespace Clara { + + struct UnpositionalTag {}; + + extern UnpositionalTag _; + +#ifdef CLARA_CONFIG_MAIN + UnpositionalTag _; +#endif + + namespace Detail { + +#ifdef CLARA_CONSOLE_WIDTH + const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + using namespace Tbc; + + inline bool startsWith( std::string const& str, std::string const& prefix ) { + return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; + } + + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; + + template struct IsBool { static const bool value = false; }; + template<> struct IsBool { static const bool value = true; }; + + template + void convertInto( std::string const& _source, T& _dest ) { + std::stringstream ss; + ss << _source; + ss >> _dest; + if( ss.fail() ) + throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); + } + inline void convertInto( std::string const& _source, std::string& _dest ) { + _dest = _source; + } + inline void convertInto( std::string const& _source, bool& _dest ) { + std::string sourceLC = _source; + std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower ); + if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) + _dest = true; + else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) + _dest = false; + else + throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); + } + inline void convertInto( bool _source, bool& _dest ) { + _dest = _source; + } + template + inline void convertInto( bool, T& ) { + throw std::runtime_error( "Invalid conversion" ); + } + + template + struct IArgFunction { + virtual ~IArgFunction() {} +# ifdef CATCH_CPP11_OR_GREATER + IArgFunction() = default; + IArgFunction( IArgFunction const& ) = default; +# endif + virtual void set( ConfigT& config, std::string const& value ) const = 0; + virtual void setFlag( ConfigT& config ) const = 0; + virtual bool takesArg() const = 0; + virtual IArgFunction* clone() const = 0; + }; + + template + class BoundArgFunction { + public: + BoundArgFunction() : functionObj( NULL ) {} + BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} + BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {} + BoundArgFunction& operator = ( BoundArgFunction const& other ) { + IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL; + delete functionObj; + functionObj = newFunctionObj; + return *this; + } + ~BoundArgFunction() { delete functionObj; } + + void set( ConfigT& config, std::string const& value ) const { + functionObj->set( config, value ); + } + void setFlag( ConfigT& config ) const { + functionObj->setFlag( config ); + } + bool takesArg() const { return functionObj->takesArg(); } + + bool isSet() const { + return functionObj != NULL; + } + private: + IArgFunction* functionObj; + }; + + template + struct NullBinder : IArgFunction{ + virtual void set( C&, std::string const& ) const {} + virtual void setFlag( C& ) const {} + virtual bool takesArg() const { return true; } + virtual IArgFunction* clone() const { return new NullBinder( *this ); } + }; + + template + struct BoundDataMember : IArgFunction{ + BoundDataMember( M C::* _member ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + convertInto( stringValue, p.*member ); + } + virtual void setFlag( C& p ) const { + convertInto( true, p.*member ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } + M C::* member; + }; + template + struct BoundUnaryMethod : IArgFunction{ + BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + typename RemoveConstRef::type value; + convertInto( stringValue, value ); + (p.*member)( value ); + } + virtual void setFlag( C& p ) const { + typename RemoveConstRef::type value; + convertInto( true, value ); + (p.*member)( value ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } + void (C::*member)( M ); + }; + template + struct BoundNullaryMethod : IArgFunction{ + BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + bool value; + convertInto( stringValue, value ); + if( value ) + (p.*member)(); + } + virtual void setFlag( C& p ) const { + (p.*member)(); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } + void (C::*member)(); + }; + + template + struct BoundUnaryFunction : IArgFunction{ + BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} + virtual void set( C& obj, std::string const& stringValue ) const { + bool value; + convertInto( stringValue, value ); + if( value ) + function( obj ); + } + virtual void setFlag( C& p ) const { + function( p ); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } + void (*function)( C& ); + }; + + template + struct BoundBinaryFunction : IArgFunction{ + BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} + virtual void set( C& obj, std::string const& stringValue ) const { + typename RemoveConstRef::type value; + convertInto( stringValue, value ); + function( obj, value ); + } + virtual void setFlag( C& obj ) const { + typename RemoveConstRef::type value; + convertInto( true, value ); + function( obj, value ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } + void (*function)( C&, T ); + }; + + } // namespace Detail + + struct Parser { + Parser() : separators( " \t=:" ) {} + + struct Token { + enum Type { Positional, ShortOpt, LongOpt }; + Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} + Type type; + std::string data; + }; + + void parseIntoTokens( int argc, char const * const * argv, std::vector& tokens ) const { + const std::string doubleDash = "--"; + for( int i = 1; i < argc && argv[i] != doubleDash; ++i ) + parseIntoTokens( argv[i] , tokens); + } + void parseIntoTokens( std::string arg, std::vector& tokens ) const { + while( !arg.empty() ) { + Parser::Token token( Parser::Token::Positional, arg ); + arg = ""; + if( token.data[0] == '-' ) { + if( token.data.size() > 1 && token.data[1] == '-' ) { + token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) ); + } + else { + token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) ); + if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) { + arg = "-" + token.data.substr( 1 ); + token.data = token.data.substr( 0, 1 ); + } + } + } + if( token.type != Parser::Token::Positional ) { + std::size_t pos = token.data.find_first_of( separators ); + if( pos != std::string::npos ) { + arg = token.data.substr( pos+1 ); + token.data = token.data.substr( 0, pos ); + } + } + tokens.push_back( token ); + } + } + std::string separators; + }; + + template + struct CommonArgProperties { + CommonArgProperties() {} + CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} + + Detail::BoundArgFunction boundField; + std::string description; + std::string detail; + std::string placeholder; // Only value if boundField takes an arg + + bool takesArg() const { + return !placeholder.empty(); + } + void validate() const { + if( !boundField.isSet() ) + throw std::logic_error( "option not bound" ); + } + }; + struct OptionArgProperties { + std::vector shortNames; + std::string longName; + + bool hasShortName( std::string const& shortName ) const { + return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); + } + bool hasLongName( std::string const& _longName ) const { + return _longName == longName; + } + }; + struct PositionalArgProperties { + PositionalArgProperties() : position( -1 ) {} + int position; // -1 means non-positional (floating) + + bool isFixedPositional() const { + return position != -1; + } + }; + + template + class CommandLine { + + struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { + Arg() {} + Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} + + using CommonArgProperties::placeholder; // !TBD + + std::string dbgName() const { + if( !longName.empty() ) + return "--" + longName; + if( !shortNames.empty() ) + return "-" + shortNames[0]; + return "positional args"; + } + std::string commands() const { + std::ostringstream oss; + bool first = true; + std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); + for(; it != itEnd; ++it ) { + if( first ) + first = false; + else + oss << ", "; + oss << "-" << *it; + } + if( !longName.empty() ) { + if( !first ) + oss << ", "; + oss << "--" << longName; + } + if( !placeholder.empty() ) + oss << " <" << placeholder << ">"; + return oss.str(); + } + }; + + // NOTE: std::auto_ptr is deprecated in c++11/c++0x +#if defined(__cplusplus) && __cplusplus > 199711L + typedef std::unique_ptr ArgAutoPtr; +#else + typedef std::auto_ptr ArgAutoPtr; +#endif + + friend void addOptName( Arg& arg, std::string const& optName ) + { + if( optName.empty() ) + return; + if( Detail::startsWith( optName, "--" ) ) { + if( !arg.longName.empty() ) + throw std::logic_error( "Only one long opt may be specified. '" + + arg.longName + + "' already specified, now attempting to add '" + + optName + "'" ); + arg.longName = optName.substr( 2 ); + } + else if( Detail::startsWith( optName, "-" ) ) + arg.shortNames.push_back( optName.substr( 1 ) ); + else + throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); + } + friend void setPositionalArg( Arg& arg, int position ) + { + arg.position = position; + } + + class ArgBuilder { + public: + ArgBuilder( Arg* arg ) : m_arg( arg ) {} + + // Bind a non-boolean data member (requires placeholder string) + template + void bind( M C::* field, std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundDataMember( field ); + m_arg->placeholder = placeholder; + } + // Bind a boolean data member (no placeholder required) + template + void bind( bool C::* field ) { + m_arg->boundField = new Detail::BoundDataMember( field ); + } + + // Bind a method taking a single, non-boolean argument (requires a placeholder string) + template + void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); + m_arg->placeholder = placeholder; + } + + // Bind a method taking a single, boolean argument (no placeholder string required) + template + void bind( void (C::* unaryMethod)( bool ) ) { + m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); + } + + // Bind a method that takes no arguments (will be called if opt is present) + template + void bind( void (C::* nullaryMethod)() ) { + m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); + } + + // Bind a free function taking a single argument - the object to operate on (no placeholder string required) + template + void bind( void (* unaryFunction)( C& ) ) { + m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); + } + + // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) + template + void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); + m_arg->placeholder = placeholder; + } + + ArgBuilder& describe( std::string const& description ) { + m_arg->description = description; + return *this; + } + ArgBuilder& detail( std::string const& detail ) { + m_arg->detail = detail; + return *this; + } + + protected: + Arg* m_arg; + }; + + class OptBuilder : public ArgBuilder { + public: + OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} + OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} + + OptBuilder& operator[]( std::string const& optName ) { + addOptName( *ArgBuilder::m_arg, optName ); + return *this; + } + }; + + public: + + CommandLine() + : m_boundProcessName( new Detail::NullBinder() ), + m_highestSpecifiedArgPosition( 0 ), + m_throwOnUnrecognisedTokens( false ) + {} + CommandLine( CommandLine const& other ) + : m_boundProcessName( other.m_boundProcessName ), + m_options ( other.m_options ), + m_positionalArgs( other.m_positionalArgs ), + m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), + m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) + { + if( other.m_floatingArg.get() ) + m_floatingArg = ArgAutoPtr( new Arg( *other.m_floatingArg ) ); + } + + CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { + m_throwOnUnrecognisedTokens = shouldThrow; + return *this; + } + + OptBuilder operator[]( std::string const& optName ) { + m_options.push_back( Arg() ); + addOptName( m_options.back(), optName ); + OptBuilder builder( &m_options.back() ); + return builder; + } + + ArgBuilder operator[]( int position ) { + m_positionalArgs.insert( std::make_pair( position, Arg() ) ); + if( position > m_highestSpecifiedArgPosition ) + m_highestSpecifiedArgPosition = position; + setPositionalArg( m_positionalArgs[position], position ); + ArgBuilder builder( &m_positionalArgs[position] ); + return builder; + } + + // Invoke this with the _ instance + ArgBuilder operator[]( UnpositionalTag ) { + if( m_floatingArg.get() ) + throw std::logic_error( "Only one unpositional argument can be added" ); + m_floatingArg = ArgAutoPtr( new Arg() ); + ArgBuilder builder( m_floatingArg.get() ); + return builder; + } + + template + void bindProcessName( M C::* field ) { + m_boundProcessName = new Detail::BoundDataMember( field ); + } + template + void bindProcessName( void (C::*_unaryMethod)( M ) ) { + m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); + } + + void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { + typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; + std::size_t maxWidth = 0; + for( it = itBegin; it != itEnd; ++it ) + maxWidth = (std::max)( maxWidth, it->commands().size() ); + + for( it = itBegin; it != itEnd; ++it ) { + Detail::Text usage( it->commands(), Detail::TextAttributes() + .setWidth( maxWidth+indent ) + .setIndent( indent ) ); + Detail::Text desc( it->description, Detail::TextAttributes() + .setWidth( width - maxWidth - 3 ) ); + + for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { + std::string usageCol = i < usage.size() ? usage[i] : ""; + os << usageCol; + + if( i < desc.size() && !desc[i].empty() ) + os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) + << desc[i]; + os << "\n"; + } + } + } + std::string optUsage() const { + std::ostringstream oss; + optUsage( oss ); + return oss.str(); + } + + void argSynopsis( std::ostream& os ) const { + for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { + if( i > 1 ) + os << " "; + typename std::map::const_iterator it = m_positionalArgs.find( i ); + if( it != m_positionalArgs.end() ) + os << "<" << it->second.placeholder << ">"; + else if( m_floatingArg.get() ) + os << "<" << m_floatingArg->placeholder << ">"; + else + throw std::logic_error( "non consecutive positional arguments with no floating args" ); + } + // !TBD No indication of mandatory args + if( m_floatingArg.get() ) { + if( m_highestSpecifiedArgPosition > 1 ) + os << " "; + os << "[<" << m_floatingArg->placeholder << "> ...]"; + } + } + std::string argSynopsis() const { + std::ostringstream oss; + argSynopsis( oss ); + return oss.str(); + } + + void usage( std::ostream& os, std::string const& procName ) const { + validate(); + os << "usage:\n " << procName << " "; + argSynopsis( os ); + if( !m_options.empty() ) { + os << " [options]\n\nwhere options are: \n"; + optUsage( os, 2 ); + } + os << "\n"; + } + std::string usage( std::string const& procName ) const { + std::ostringstream oss; + usage( oss, procName ); + return oss.str(); + } + + ConfigT parse( int argc, char const * const * argv ) const { + ConfigT config; + parseInto( argc, argv, config ); + return config; + } + + std::vector parseInto( int argc, char const * const * argv, ConfigT& config ) const { + std::string processName = argv[0]; + std::size_t lastSlash = processName.find_last_of( "/\\" ); + if( lastSlash != std::string::npos ) + processName = processName.substr( lastSlash+1 ); + m_boundProcessName.set( config, processName ); + std::vector tokens; + Parser parser; + parser.parseIntoTokens( argc, argv, tokens ); + return populate( tokens, config ); + } + + std::vector populate( std::vector const& tokens, ConfigT& config ) const { + validate(); + std::vector unusedTokens = populateOptions( tokens, config ); + unusedTokens = populateFixedArgs( unusedTokens, config ); + unusedTokens = populateFloatingArgs( unusedTokens, config ); + return unusedTokens; + } + + std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { + std::vector unusedTokens; + std::vector errors; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); + for(; it != itEnd; ++it ) { + Arg const& arg = *it; + + try { + if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || + ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { + if( arg.takesArg() ) { + if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) + errors.push_back( "Expected argument to option: " + token.data ); + else + arg.boundField.set( config, tokens[++i].data ); + } + else { + arg.boundField.setFlag( config ); + } + break; + } + } + catch( std::exception& ex ) { + errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); + } + } + if( it == itEnd ) { + if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) + unusedTokens.push_back( token ); + else if( m_throwOnUnrecognisedTokens ) + errors.push_back( "unrecognised option: " + token.data ); + } + } + if( !errors.empty() ) { + std::ostringstream oss; + for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); + it != itEnd; + ++it ) { + if( it != errors.begin() ) + oss << "\n"; + oss << *it; + } + throw std::runtime_error( oss.str() ); + } + return unusedTokens; + } + std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { + std::vector unusedTokens; + int position = 1; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + typename std::map::const_iterator it = m_positionalArgs.find( position ); + if( it != m_positionalArgs.end() ) + it->second.boundField.set( config, token.data ); + else + unusedTokens.push_back( token ); + if( token.type == Parser::Token::Positional ) + position++; + } + return unusedTokens; + } + std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { + if( !m_floatingArg.get() ) + return tokens; + std::vector unusedTokens; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + if( token.type == Parser::Token::Positional ) + m_floatingArg->boundField.set( config, token.data ); + else + unusedTokens.push_back( token ); + } + return unusedTokens; + } + + void validate() const + { + if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) + throw std::logic_error( "No options or arguments specified" ); + + for( typename std::vector::const_iterator it = m_options.begin(), + itEnd = m_options.end(); + it != itEnd; ++it ) + it->validate(); + } + + private: + Detail::BoundArgFunction m_boundProcessName; + std::vector m_options; + std::map m_positionalArgs; + ArgAutoPtr m_floatingArg; + int m_highestSpecifiedArgPosition; + bool m_throwOnUnrecognisedTokens; + }; + +} // end namespace Clara + +STITCH_CLARA_CLOSE_NAMESPACE +#undef STITCH_CLARA_OPEN_NAMESPACE +#undef STITCH_CLARA_CLOSE_NAMESPACE + +#endif // TWOBLUECUBES_CLARA_H_INCLUDED +#undef STITCH_CLARA_OPEN_NAMESPACE + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#include + +namespace Catch { + + inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } + inline void abortAfterX( ConfigData& config, int x ) { + if( x < 1 ) + throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); + config.abortAfter = x; + } + inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } + + inline void addWarning( ConfigData& config, std::string const& _warning ) { + if( _warning == "NoAssertions" ) + config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); + else + throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" ); + + } + inline void setVerbosity( ConfigData& config, int level ) { + // !TBD: accept strings? + config.verbosity = static_cast( level ); + } + inline void setShowDurations( ConfigData& config, bool _showDurations ) { + config.showDurations = _showDurations + ? ShowDurations::Always + : ShowDurations::Never; + } + inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { + std::ifstream f( _filename.c_str() ); + if( !f.is_open() ) + throw std::domain_error( "Unable to load input file: " + _filename ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, "#" ) ) + addTestOrTags( config, "\"" + line + "\"," ); + } + } + + inline Clara::CommandLine makeCommandLineParser() { + + using namespace Clara; + CommandLine cli; + + cli.bindProcessName( &ConfigData::processName ); + + cli["-?"]["-h"]["--help"] + .describe( "display usage information" ) + .bind( &ConfigData::showHelp ); + + cli["-l"]["--list-tests"] + .describe( "list all/matching test cases" ) + .bind( &ConfigData::listTests ); + + cli["-t"]["--list-tags"] + .describe( "list all/matching tags" ) + .bind( &ConfigData::listTags ); + + cli["-s"]["--success"] + .describe( "include successful tests in output" ) + .bind( &ConfigData::showSuccessfulTests ); + + cli["-b"]["--break"] + .describe( "break into debugger on failure" ) + .bind( &ConfigData::shouldDebugBreak ); + + cli["-e"]["--nothrow"] + .describe( "skip exception tests" ) + .bind( &ConfigData::noThrow ); + + cli["-i"]["--invisibles"] + .describe( "show invisibles (tabs, newlines)" ) + .bind( &ConfigData::showInvisibles ); + + cli["-o"]["--out"] + .describe( "output filename" ) + .bind( &ConfigData::outputFilename, "filename" ); + + cli["-r"]["--reporter"] +// .placeholder( "name[:filename]" ) + .describe( "reporter to use (defaults to console)" ) + .bind( &ConfigData::reporterName, "name" ); + + cli["-n"]["--name"] + .describe( "suite name" ) + .bind( &ConfigData::name, "name" ); + + cli["-a"]["--abort"] + .describe( "abort at first failure" ) + .bind( &abortAfterFirst ); + + cli["-x"]["--abortx"] + .describe( "abort after x failures" ) + .bind( &abortAfterX, "no. failures" ); + + cli["-w"]["--warn"] + .describe( "enable warnings" ) + .bind( &addWarning, "warning name" ); + +// - needs updating if reinstated +// cli.into( &setVerbosity ) +// .describe( "level of verbosity (0=no output)" ) +// .shortOpt( "v") +// .longOpt( "verbosity" ) +// .placeholder( "level" ); + + cli[_] + .describe( "which test or tests to use" ) + .bind( &addTestOrTags, "test name, pattern or tags" ); + + cli["-d"]["--durations"] + .describe( "show test durations" ) + .bind( &setShowDurations, "yes/no" ); + + cli["-f"]["--input-file"] + .describe( "load test names to run from a file" ) + .bind( &loadTestNamesFromFile, "filename" ); + + // Less common commands which don't have a short form + cli["--list-test-names-only"] + .describe( "list all/matching test cases names only" ) + .bind( &ConfigData::listTestNamesOnly ); + + cli["--list-reporters"] + .describe( "list all reporters" ) + .bind( &ConfigData::listReporters ); + + return cli; + } + +} // end namespace Catch + +// #included from: internal/catch_list.hpp +#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED + +// #included from: catch_text.h +#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED + +#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch +// #included from: ../external/tbc_text_format.h +// Only use header guard if we are not using an outer namespace +#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +# endif +# else +# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +# endif +#endif +#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#include +#include +#include + +// Use optional outer namespace +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + struct TextAttributes { + TextAttributes() + : initialIndent( std::string::npos ), + indent( 0 ), + width( consoleWidth-1 ), + tabChar( '\t' ) + {} + + TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } + TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } + TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } + TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } + + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + char tabChar; // If this char is seen the indent is changed to current pos + }; + + class Text { + public: + Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) + : attr( _attr ) + { + std::string wrappableChars = " [({.,/|\\-"; + std::size_t indent = _attr.initialIndent != std::string::npos + ? _attr.initialIndent + : _attr.indent; + std::string remainder = _str; + + while( !remainder.empty() ) { + if( lines.size() >= 1000 ) { + lines.push_back( "... message truncated due to excessive size" ); + return; + } + std::size_t tabPos = std::string::npos; + std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); + std::size_t pos = remainder.find_first_of( '\n' ); + if( pos <= width ) { + width = pos; + } + pos = remainder.find_last_of( _attr.tabChar, width ); + if( pos != std::string::npos ) { + tabPos = pos; + if( remainder[width] == '\n' ) + width--; + remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); + } + + if( width == remainder.size() ) { + spliceLine( indent, remainder, width ); + } + else if( remainder[width] == '\n' ) { + spliceLine( indent, remainder, width ); + if( width <= 1 || remainder.size() != 1 ) + remainder = remainder.substr( 1 ); + indent = _attr.indent; + } + else { + pos = remainder.find_last_of( wrappableChars, width ); + if( pos != std::string::npos && pos > 0 ) { + spliceLine( indent, remainder, pos ); + if( remainder[0] == ' ' ) + remainder = remainder.substr( 1 ); + } + else { + spliceLine( indent, remainder, width-1 ); + lines.back() += "-"; + } + if( lines.size() == 1 ) + indent = _attr.indent; + if( tabPos != std::string::npos ) + indent += tabPos; + } + } + } + + void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { + lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); + _remainder = _remainder.substr( _pos ); + } + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it ) { + if( it != _text.begin() ) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector lines; + }; + +} // end namespace Tbc + +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE + +namespace Catch { + using Tbc::Text; + using Tbc::TextAttributes; +} + +// #included from: catch_console_colour.hpp +#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED + +namespace Catch { + + namespace Detail { + struct IColourImpl; + } + + struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + + // By intention + FileName = LightGrey, + Warning = Yellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = Yellow, + + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour( Code _colourCode ); + Colour( Colour const& other ); + ~Colour(); + + // Use static method for one-shot changes + static void use( Code _colourCode ); + + private: + static Detail::IColourImpl* impl(); + bool m_moved; + }; + + inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } + +} // end namespace Catch + +// #included from: catch_interfaces_reporter.h +#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED + +#include +#include +#include +#include + +namespace Catch +{ + struct ReporterConfig { + explicit ReporterConfig( Ptr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& stream() const { return *m_stream; } + Ptr fullConfig() const { return m_fullConfig; } + + private: + std::ostream* m_stream; + Ptr m_fullConfig; + }; + + struct ReporterPreferences { + ReporterPreferences() + : shouldRedirectStdOut( false ) + {} + + bool shouldRedirectStdOut; + }; + + template + struct LazyStat : Option { + LazyStat() : used( false ) {} + LazyStat& operator=( T const& _value ) { + Option::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option::reset(); + used = false; + } + bool used; + }; + + struct TestRunInfo { + TestRunInfo( std::string const& _name ) : name( _name ) {} + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); + } + } + virtual ~AssertionStats(); + +# ifdef CATCH_CPP11_OR_GREATER + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = default; + AssertionStats& operator = ( AssertionStats && ) = default; +# endif + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + virtual ~SectionStats(); +# ifdef CATCH_CPP11_OR_GREATER + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; +# endif + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + virtual ~TestCaseStats(); + +# ifdef CATCH_CPP11_OR_GREATER + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; +# endif + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + virtual ~TestGroupStats(); + +# ifdef CATCH_CPP11_OR_GREATER + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; +# endif + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + virtual ~TestRunStats(); + +# ifndef CATCH_CPP11_OR_GREATER + TestRunStats( TestRunStats const& _other ) + : runInfo( _other.runInfo ), + totals( _other.totals ), + aborting( _other.aborting ) + {} +# else + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; +# endif + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + struct IStreamingReporter : IShared { + virtual ~IStreamingReporter(); + + // Implementing class must also provide the following static method: + // static std::string getDescription(); + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases( std::string const& spec ) = 0; + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + }; + + struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + + struct IReporterRegistry { + typedef std::map FactoryMap; + + virtual ~IReporterRegistry(); + virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + }; + +} + +#include +#include + +namespace Catch { + + inline std::size_t listTests( Config const& config ) { + + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + std::cout << "Matching test cases:\n"; + else { + std::cout << "All available test cases:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + std::size_t matchedTests = 0; + TextAttributes nameAttr, tagsAttr; + nameAttr.setInitialIndent( 2 ).setIndent( 4 ); + tagsAttr.setIndent( 6 ); + + std::vector matchedTestCases; + getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + std::cout << Text( testCaseInfo.name, nameAttr ) << std::endl; + if( !testCaseInfo.tags.empty() ) + std::cout << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; + } + + if( !config.testSpec().hasFilters() ) + std::cout << pluralise( matchedTests, "test case" ) << "\n" << std::endl; + else + std::cout << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; + return matchedTests; + } + + inline std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( !config.testSpec().hasFilters() ) + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases; + getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + std::cout << testCaseInfo.name << std::endl; + } + return matchedTests; + } + + struct TagInfo { + TagInfo() : count ( 0 ) {} + void add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); + } + std::string all() const { + std::string out; + for( std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); + it != itEnd; + ++it ) + out += "[" + *it + "]"; + return out; + } + std::set spellings; + std::size_t count; + }; + + inline std::size_t listTags( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + std::cout << "Tags for matching test cases:\n"; + else { + std::cout << "All available tags:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + std::map tagCounts; + + std::vector matchedTestCases; + getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), + tagItEnd = it->getTestCaseInfo().tags.end(); + tagIt != tagItEnd; + ++tagIt ) { + std::string tagName = *tagIt; + std::string lcaseTagName = toLower( tagName ); + std::map::iterator countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( std::map::const_iterator countIt = tagCounts.begin(), + countItEnd = tagCounts.end(); + countIt != countItEnd; + ++countIt ) { + std::ostringstream oss; + oss << " " << std::setw(2) << countIt->second.count << " "; + Text wrapper( countIt->second.all(), TextAttributes() + .setInitialIndent( 0 ) + .setIndent( oss.str().size() ) + .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); + std::cout << oss.str() << wrapper << "\n"; + } + std::cout << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; + return tagCounts.size(); + } + + inline std::size_t listReporters( Config const& /*config*/ ) { + std::cout << "Available reports:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; + std::size_t maxNameLen = 0; + for(it = itBegin; it != itEnd; ++it ) + maxNameLen = (std::max)( maxNameLen, it->first.size() ); + + for(it = itBegin; it != itEnd; ++it ) { + Text wrapper( it->second->getDescription(), TextAttributes() + .setInitialIndent( 0 ) + .setIndent( 7+maxNameLen ) + .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); + std::cout << " " + << it->first + << ":" + << std::string( maxNameLen - it->first.size() + 2, ' ' ) + << wrapper << "\n"; + } + std::cout << std::endl; + return factories.size(); + } + + inline Option list( Config const& config ) { + Option listedCount; + if( config.listTests() ) + listedCount = listedCount.valueOr(0) + listTests( config ); + if( config.listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); + if( config.listTags() ) + listedCount = listedCount.valueOr(0) + listTags( config ); + if( config.listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters( config ); + return listedCount; + } + +} // end namespace Catch + +// #included from: internal/catch_runner_impl.hpp +#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED + +// #included from: catch_test_case_tracker.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED + +#include +#include +#include + +namespace Catch { +namespace SectionTracking { + + class TrackedSection { + + typedef std::map TrackedSections; + + public: + enum RunState { + NotStarted, + Executing, + ExecutingChildren, + Completed + }; + + TrackedSection( std::string const& name, TrackedSection* parent ) + : m_name( name ), m_runState( NotStarted ), m_parent( parent ) + {} + + RunState runState() const { return m_runState; } + + TrackedSection* findChild( std::string const& childName ) { + TrackedSections::iterator it = m_children.find( childName ); + return it != m_children.end() + ? &it->second + : NULL; + } + TrackedSection* acquireChild( std::string const& childName ) { + if( TrackedSection* child = findChild( childName ) ) + return child; + m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) ); + return findChild( childName ); + } + void enter() { + if( m_runState == NotStarted ) + m_runState = Executing; + } + void leave() { + for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end(); + it != itEnd; + ++it ) + if( it->second.runState() != Completed ) { + m_runState = ExecutingChildren; + return; + } + m_runState = Completed; + } + TrackedSection* getParent() { + return m_parent; + } + bool hasChildren() const { + return !m_children.empty(); + } + + private: + std::string m_name; + RunState m_runState; + TrackedSections m_children; + TrackedSection* m_parent; + + }; + + class TestCaseTracker { + public: + TestCaseTracker( std::string const& testCaseName ) + : m_testCase( testCaseName, NULL ), + m_currentSection( &m_testCase ), + m_completedASectionThisRun( false ) + {} + + bool enterSection( std::string const& name ) { + TrackedSection* child = m_currentSection->acquireChild( name ); + if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed ) + return false; + + m_currentSection = child; + m_currentSection->enter(); + return true; + } + void leaveSection() { + m_currentSection->leave(); + m_currentSection = m_currentSection->getParent(); + assert( m_currentSection != NULL ); + m_completedASectionThisRun = true; + } + + bool currentSectionHasChildren() const { + return m_currentSection->hasChildren(); + } + bool isCompleted() const { + return m_testCase.runState() == TrackedSection::Completed; + } + + class Guard { + public: + Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) { + m_tracker.enterTestCase(); + } + ~Guard() { + m_tracker.leaveTestCase(); + } + private: + Guard( Guard const& ); + void operator = ( Guard const& ); + TestCaseTracker& m_tracker; + }; + + private: + void enterTestCase() { + m_currentSection = &m_testCase; + m_completedASectionThisRun = false; + m_testCase.enter(); + } + void leaveTestCase() { + m_testCase.leave(); + } + + TrackedSection m_testCase; + TrackedSection* m_currentSection; + bool m_completedASectionThisRun; + }; + +} // namespace SectionTracking + +using SectionTracking::TestCaseTracker; + +} // namespace Catch + +#include +#include + +namespace Catch { + + class StreamRedirect { + + public: + StreamRedirect( std::ostream& stream, std::string& targetString ) + : m_stream( stream ), + m_prevBuf( stream.rdbuf() ), + m_targetString( targetString ) + { + stream.rdbuf( m_oss.rdbuf() ); + } + + ~StreamRedirect() { + m_targetString += m_oss.str(); + m_stream.rdbuf( m_prevBuf ); + } + + private: + std::ostream& m_stream; + std::streambuf* m_prevBuf; + std::ostringstream m_oss; + std::string& m_targetString; + }; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { + + RunContext( RunContext const& ); + void operator =( RunContext const& ); + + public: + + explicit RunContext( Ptr const& config, Ptr const& reporter ) + : m_runInfo( config->name() ), + m_context( getCurrentMutableContext() ), + m_activeTestCase( NULL ), + m_config( config ), + m_reporter( reporter ), + m_prevRunner( m_context.getRunner() ), + m_prevResultCapture( m_context.getResultCapture() ), + m_prevConfig( m_context.getConfig() ) + { + m_context.setRunner( this ); + m_context.setConfig( m_config ); + m_context.setResultCapture( this ); + m_reporter->testRunStarting( m_runInfo ); + } + + virtual ~RunContext() { + m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); + m_context.setRunner( m_prevRunner ); + m_context.setConfig( NULL ); + m_context.setResultCapture( m_prevResultCapture ); + m_context.setConfig( m_prevConfig ); + } + + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { + m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); + } + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { + m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); + } + + Totals runTest( TestCase const& testCase ) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + TestCaseInfo testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting( testInfo ); + + m_activeTestCase = &testCase; + m_testCaseTracker = TestCaseTracker( testInfo.name ); + + do { + do { + runCurrentTest( redirectedCout, redirectedCerr ); + } + while( !m_testCaseTracker->isCompleted() && !aborting() ); + } + while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); + + Totals deltaTotals = m_totals.delta( prevTotals ); + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded( TestCaseStats( testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting() ) ); + + m_activeTestCase = NULL; + m_testCaseTracker.reset(); + + return deltaTotals; + } + + Ptr config() const { + return m_config; + } + + private: // IResultCapture + + virtual void assertionEnded( AssertionResult const& result ) { + if( result.getResultType() == ResultWas::Ok ) { + m_totals.assertions.passed++; + } + else if( !result.isOk() ) { + m_totals.assertions.failed++; + } + + if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) + m_messages.clear(); + + // Reset working state + m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); + m_lastResult = result; + } + + virtual bool sectionStarted ( + SectionInfo const& sectionInfo, + Counts& assertions + ) + { + std::ostringstream oss; + oss << sectionInfo.name << "@" << sectionInfo.lineInfo; + + if( !m_testCaseTracker->enterSection( oss.str() ) ) + return false; + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting( sectionInfo ); + + assertions = m_totals.assertions; + + return true; + } + bool testForMissingAssertions( Counts& assertions ) { + if( assertions.total() != 0 || + !m_config->warnAboutMissingAssertions() || + m_testCaseTracker->currentSectionHasChildren() ) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) { + if( std::uncaught_exception() ) { + m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) ); + return; + } + + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions( assertions ); + + m_testCaseTracker->leaveSection(); + + m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) ); + m_messages.clear(); + } + + virtual void pushScopedMessage( MessageInfo const& message ) { + m_messages.push_back( message ); + } + + virtual void popScopedMessage( MessageInfo const& message ) { + m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); + } + + virtual std::string getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : ""; + } + + virtual const AssertionResult* getLastResult() const { + return &m_lastResult; + } + + public: + // !TBD We need to do this another way! + bool aborting() const { + return m_totals.assertions.failed == static_cast( m_config->abortAfter() ); + } + + private: + + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { + TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); + m_reporter->sectionStarting( testCaseSection ); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + try { + m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); + TestCaseTracker::Guard guard( *m_testCaseTracker ); + + Timer timer; + timer.start(); + if( m_reporter->getPreferences().shouldRedirectStdOut ) { + StreamRedirect coutRedir( std::cout, redirectedCout ); + StreamRedirect cerrRedir( std::cerr, redirectedCerr ); + m_activeTestCase->invoke(); + } + else { + m_activeTestCase->invoke(); + } + duration = timer.getElapsedSeconds(); + } + catch( TestFailureException& ) { + // This just means the test was aborted due to failure + } + catch(...) { + ResultBuilder exResult( m_lastAssertionInfo.macroName.c_str(), + m_lastAssertionInfo.lineInfo, + m_lastAssertionInfo.capturedExpression.c_str(), + m_lastAssertionInfo.resultDisposition ); + exResult.useActiveException(); + } + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it ) + sectionEnded( it->info, it->prevAssertions, it->durationInSeconds ); + m_unfinishedSections.clear(); + m_messages.clear(); + + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions( assertions ); + + if( testCaseInfo.okToFail() ) { + std::swap( assertions.failedButOk, assertions.failed ); + m_totals.assertions.failed -= assertions.failedButOk; + m_totals.assertions.failedButOk += assertions.failedButOk; + } + + SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); + m_reporter->sectionEnded( testCaseSectionStats ); + } + + private: + struct UnfinishedSections { + UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds ) + : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) + {} + + SectionInfo info; + Counts prevAssertions; + double durationInSeconds; + }; + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase; + Option m_testCaseTracker; + AssertionResult m_lastResult; + + Ptr m_config; + Totals m_totals; + Ptr m_reporter; + std::vector m_messages; + IRunner* m_prevRunner; + IResultCapture* m_prevResultCapture; + Ptr m_prevConfig; + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + }; + + IResultCapture& getResultCapture() { + if( IResultCapture* capture = getCurrentContext().getResultCapture() ) + return *capture; + else + throw std::logic_error( "No result capture instance" ); + } + +} // end namespace Catch + +// #included from: internal/catch_version.h +#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED + +namespace Catch { + + // Versioning information + struct Version { + Version( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _buildNumber, + char const* const _branchName ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + buildNumber( _buildNumber ), + branchName( _branchName ) + {} + + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const buildNumber; + char const* const branchName; + + private: + void operator=( Version const& ); + }; + + extern Version libraryVersion; +} + +#include +#include +#include + +namespace Catch { + + class Runner { + + public: + Runner( Ptr const& config ) + : m_config( config ) + { + openStream(); + makeReporter(); + } + + Totals runTests() { + + RunContext context( m_config.get(), m_reporter ); + + Totals totals; + + context.testGroupStarting( "", 1, 1 ); // deprecated? + + TestSpec testSpec = m_config->testSpec(); + if( !testSpec.hasFilters() ) + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests + + std::vector testCases; + getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases ); + + int testsRunForGroup = 0; + for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); + it != itEnd; + ++it ) { + testsRunForGroup++; + if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) { + + if( context.aborting() ) + break; + + totals += context.runTest( *it ); + m_testsAlreadyRun.insert( *it ); + } + } + context.testGroupEnded( "", totals, 1, 1 ); + return totals; + } + + private: + void openStream() { + // Open output file, if specified + if( !m_config->getFilename().empty() ) { + m_ofs.open( m_config->getFilename().c_str() ); + if( m_ofs.fail() ) { + std::ostringstream oss; + oss << "Unable to open file: '" << m_config->getFilename() << "'"; + throw std::domain_error( oss.str() ); + } + m_config->setStreamBuf( m_ofs.rdbuf() ); + } + } + void makeReporter() { + std::string reporterName = m_config->getReporterName().empty() + ? "console" + : m_config->getReporterName(); + + m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() ); + if( !m_reporter ) { + std::ostringstream oss; + oss << "No reporter registered with name: '" << reporterName << "'"; + throw std::domain_error( oss.str() ); + } + } + + private: + Ptr m_config; + std::ofstream m_ofs; + Ptr m_reporter; + std::set m_testsAlreadyRun; + }; + + class Session { + static bool alreadyInstantiated; + + public: + + struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; + + Session() + : m_cli( makeCommandLineParser() ) { + if( alreadyInstantiated ) { + std::string msg = "Only one instance of Catch::Session can ever be used"; + std::cerr << msg << std::endl; + throw std::logic_error( msg ); + } + alreadyInstantiated = true; + } + ~Session() { + Catch::cleanUp(); + } + + void showHelp( std::string const& processName ) { + std::cout << "\nCatch v" << libraryVersion.majorVersion << "." + << libraryVersion.minorVersion << " build " + << libraryVersion.buildNumber; + if( libraryVersion.branchName != std::string( "master" ) ) + std::cout << " (" << libraryVersion.branchName << " branch)"; + std::cout << "\n"; + + m_cli.usage( std::cout, processName ); + std::cout << "For more detail usage please see the project docs\n" << std::endl; + } + + int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { + try { + m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); + m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); + if( m_configData.showHelp ) + showHelp( m_configData.processName ); + m_config.reset(); + } + catch( std::exception& ex ) { + { + Colour colourGuard( Colour::Red ); + std::cerr << "\nError(s) in input:\n" + << Text( ex.what(), TextAttributes().setIndent(2) ) + << "\n\n"; + } + m_cli.usage( std::cout, m_configData.processName ); + return (std::numeric_limits::max)(); + } + return 0; + } + + void useConfigData( ConfigData const& _configData ) { + m_configData = _configData; + m_config.reset(); + } + + int run( int argc, char* const argv[] ) { + + int returnCode = applyCommandLine( argc, argv ); + if( returnCode == 0 ) + returnCode = run(); + return returnCode; + } + + int run() { + if( m_configData.showHelp ) + return 0; + + try + { + config(); // Force config to be constructed + Runner runner( m_config ); + + // Handle list request + if( Option listed = list( config() ) ) + return static_cast( *listed ); + + return static_cast( runner.runTests().assertions.failed ); + } + catch( std::exception& ex ) { + std::cerr << ex.what() << std::endl; + return (std::numeric_limits::max)(); + } + } + + Clara::CommandLine const& cli() const { + return m_cli; + } + std::vector const& unusedTokens() const { + return m_unusedTokens; + } + ConfigData& configData() { + return m_configData; + } + Config& config() { + if( !m_config ) + m_config = new Config( m_configData ); + return *m_config; + } + + private: + Clara::CommandLine m_cli; + std::vector m_unusedTokens; + ConfigData m_configData; + Ptr m_config; + }; + + bool Session::alreadyInstantiated = false; + +} // end namespace Catch + +// #included from: catch_registry_hub.hpp +#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED + +// #included from: catch_test_case_registry_impl.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + + class TestRegistry : public ITestCaseRegistry { + public: + TestRegistry() : m_unnamedCount( 0 ) {} + virtual ~TestRegistry(); + + virtual void registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name == "" ) { + std::ostringstream oss; + oss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( oss.str() ) ); + } + + if( m_functions.find( testCase ) == m_functions.end() ) { + m_functions.insert( testCase ); + m_functionsInOrder.push_back( testCase ); + if( !testCase.isHidden() ) + m_nonHiddenFunctions.push_back( testCase ); + } + else { + TestCase const& prev = *m_functions.find( testCase ); + { + Colour colourGuard( Colour::Red ); + std::cerr << "error: TEST_CASE( \"" << name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl; + } + exit(1); + } + } + + virtual std::vector const& getAllTests() const { + return m_functionsInOrder; + } + + virtual std::vector const& getAllNonHiddenTests() const { + return m_nonHiddenFunctions; + } + + virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector& matchingTestCases ) const { + for( std::vector::const_iterator it = m_functionsInOrder.begin(), + itEnd = m_functionsInOrder.end(); + it != itEnd; + ++it ) { + if( testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() ) ) + matchingTestCases.push_back( *it ); + } + } + + private: + + std::set m_functions; + std::vector m_functionsInOrder; + std::vector m_nonHiddenFunctions; + size_t m_unnamedCount; + }; + + /////////////////////////////////////////////////////////////////////////// + + class FreeFunctionTestCase : public SharedImpl { + public: + + FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} + + virtual void invoke() const { + m_fun(); + } + + private: + virtual ~FreeFunctionTestCase(); + + TestFunction m_fun; + }; + + inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, "&" ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; + } + + /////////////////////////////////////////////////////////////////////////// + + AutoReg::AutoReg( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ) { + registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); + } + + AutoReg::~AutoReg() {} + + void AutoReg::registerTestCase( ITestCase* testCase, + char const* classOrQualifiedMethodName, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ) { + + getMutableRegistryHub().registerTest + ( makeTestCase( testCase, + extractClassName( classOrQualifiedMethodName ), + nameAndDesc.name, + nameAndDesc.description, + lineInfo ) ); + } + +} // end namespace Catch + +// #included from: catch_reporter_registry.hpp +#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED + +#include + +namespace Catch { + + class ReporterRegistry : public IReporterRegistry { + + public: + + virtual ~ReporterRegistry() { + deleteAllValues( m_factories ); + } + + virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const { + FactoryMap::const_iterator it = m_factories.find( name ); + if( it == m_factories.end() ) + return NULL; + return it->second->create( ReporterConfig( config ) ); + } + + void registerReporter( std::string const& name, IReporterFactory* factory ) { + m_factories.insert( std::make_pair( name, factory ) ); + } + + FactoryMap const& getFactories() const { + return m_factories; + } + + private: + FactoryMap m_factories; + }; +} + +// #included from: catch_exception_translator_registry.hpp +#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED + +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry() { + deleteAll( m_translators ); + } + + virtual void registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( translator ); + } + + virtual std::string translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + throw; + } + @catch (NSException *exception) { + return toString( [exception description] ); + } +#else + throw; +#endif + } + catch( TestFailureException& ) { + throw; + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return tryTranslators( m_translators.begin() ); + } + } + + std::string tryTranslators( std::vector::const_iterator it ) const { + if( it == m_translators.end() ) + return "Unknown exception"; + + try { + return (*it)->translate(); + } + catch(...) { + return tryTranslators( it+1 ); + } + } + + private: + std::vector m_translators; + }; +} + +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub { + + RegistryHub( RegistryHub const& ); + void operator=( RegistryHub const& ); + + public: // IRegistryHub + RegistryHub() { + } + virtual IReporterRegistry const& getReporterRegistry() const { + return m_reporterRegistry; + } + virtual ITestCaseRegistry const& getTestCaseRegistry() const { + return m_testCaseRegistry; + } + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() { + return m_exceptionTranslatorRegistry; + } + + public: // IMutableRegistryHub + virtual void registerReporter( std::string const& name, IReporterFactory* factory ) { + m_reporterRegistry.registerReporter( name, factory ); + } + virtual void registerTest( TestCase const& testInfo ) { + m_testCaseRegistry.registerTest( testInfo ); + } + virtual void registerTranslator( const IExceptionTranslator* translator ) { + m_exceptionTranslatorRegistry.registerTranslator( translator ); + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + }; + + // Single, global, instance + inline RegistryHub*& getTheRegistryHub() { + static RegistryHub* theRegistryHub = NULL; + if( !theRegistryHub ) + theRegistryHub = new RegistryHub(); + return theRegistryHub; + } + } + + IRegistryHub& getRegistryHub() { + return *getTheRegistryHub(); + } + IMutableRegistryHub& getMutableRegistryHub() { + return *getTheRegistryHub(); + } + void cleanUp() { + delete getTheRegistryHub(); + getTheRegistryHub() = NULL; + cleanUpContext(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } + +} // end namespace Catch + +// #included from: catch_notimplemented_exception.hpp +#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED + +#include + +namespace Catch { + + NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) + : m_lineInfo( lineInfo ) { + std::ostringstream oss; + oss << lineInfo << ": function "; + oss << "not implemented"; + m_what = oss.str(); + } + + const char* NotImplementedException::what() const CATCH_NOEXCEPT { + return m_what.c_str(); + } + +} // end namespace Catch + +// #included from: catch_context_impl.hpp +#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED + +// #included from: catch_stream.hpp +#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED + +// #included from: catch_streambuf.h +#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED + +#include + +namespace Catch { + + class StreamBufBase : public std::streambuf { + public: + virtual ~StreamBufBase() CATCH_NOEXCEPT; + }; +} + +#include +#include + +namespace Catch { + + template + class StreamBufImpl : public StreamBufBase { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() CATCH_NOEXCEPT { + sync(); + } + + private: + int overflow( int c ) { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast( c ) ) ); + else + sputc( static_cast( c ) ); + } + return 0; + } + + int sync() { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + Stream::Stream() + : streamBuf( NULL ), isOwned( false ) + {} + + Stream::Stream( std::streambuf* _streamBuf, bool _isOwned ) + : streamBuf( _streamBuf ), isOwned( _isOwned ) + {} + + void Stream::release() { + if( isOwned ) { + delete streamBuf; + streamBuf = NULL; + isOwned = false; + } + } +} + +namespace Catch { + + class Context : public IMutableContext { + + Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {} + Context( Context const& ); + void operator=( Context const& ); + + public: // IContext + virtual IResultCapture* getResultCapture() { + return m_resultCapture; + } + virtual IRunner* getRunner() { + return m_runner; + } + virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { + return getGeneratorsForCurrentTest() + .getGeneratorInfo( fileInfo, totalSize ) + .getCurrentIndex(); + } + virtual bool advanceGeneratorsForCurrentTest() { + IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); + return generators && generators->moveNext(); + } + + virtual Ptr getConfig() const { + return m_config; + } + + public: // IMutableContext + virtual void setResultCapture( IResultCapture* resultCapture ) { + m_resultCapture = resultCapture; + } + virtual void setRunner( IRunner* runner ) { + m_runner = runner; + } + virtual void setConfig( Ptr const& config ) { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IGeneratorsForTest* findGeneratorsForCurrentTest() { + std::string testName = getResultCapture()->getCurrentTestName(); + + std::map::const_iterator it = + m_generatorsByTestName.find( testName ); + return it != m_generatorsByTestName.end() + ? it->second + : NULL; + } + + IGeneratorsForTest& getGeneratorsForCurrentTest() { + IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); + if( !generators ) { + std::string testName = getResultCapture()->getCurrentTestName(); + generators = createGeneratorsForTest(); + m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); + } + return *generators; + } + + private: + Ptr m_config; + IRunner* m_runner; + IResultCapture* m_resultCapture; + std::map m_generatorsByTestName; + }; + + namespace { + Context* currentContext = NULL; + } + IMutableContext& getCurrentMutableContext() { + if( !currentContext ) + currentContext = new Context(); + return *currentContext; + } + IContext& getCurrentContext() { + return getCurrentMutableContext(); + } + + Stream createStream( std::string const& streamName ) { + if( streamName == "stdout" ) return Stream( std::cout.rdbuf(), false ); + if( streamName == "stderr" ) return Stream( std::cerr.rdbuf(), false ); + if( streamName == "debug" ) return Stream( new StreamBufImpl, true ); + + throw std::domain_error( "Unknown stream: " + streamName ); + } + + void cleanUpContext() { + delete currentContext; + currentContext = NULL; + } +} + +// #included from: catch_console_colour_impl.hpp +#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED + +namespace Catch { namespace Detail { + struct IColourImpl { + virtual ~IColourImpl() {} + virtual void use( Colour::Code _colourCode ) = 0; + }; +}} + +#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +namespace Catch { +namespace { + + class Win32ColourImpl : public Detail::IColourImpl { + public: + Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) + { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); + originalAttributes = csbiInfo.wAttributes; + } + + virtual void use( Colour::Code _colourCode ) { + switch( _colourCode ) { + case Colour::None: return setTextAttribute( originalAttributes ); + case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::Red: return setTextAttribute( FOREGROUND_RED ); + case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); + case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); + case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); + case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::Grey: return setTextAttribute( 0 ); + + case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); + case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); + case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); + case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + + case Colour::Bright: throw std::logic_error( "not a colour" ); + } + } + + private: + void setTextAttribute( WORD _textAttribute ) { + SetConsoleTextAttribute( stdoutHandle, _textAttribute ); + } + HANDLE stdoutHandle; + WORD originalAttributes; + }; + + inline bool shouldUseColourForPlatform() { + return true; + } + + static Detail::IColourImpl* platformColourInstance() { + static Win32ColourImpl s_instance; + return &s_instance; + } + +} // end anon namespace +} // end namespace Catch + +#else // Not Windows - assumed to be POSIX compatible ////////////////////////// + +#include + +namespace Catch { +namespace { + + // use POSIX/ ANSI console terminal codes + // Thanks to Adam Strzelecki for original contribution + // (http://github.com/nanoant) + // https://github.com/philsquared/Catch/pull/131 + class PosixColourImpl : public Detail::IColourImpl { + public: + virtual void use( Colour::Code _colourCode ) { + switch( _colourCode ) { + case Colour::None: + case Colour::White: return setColour( "[0m" ); + case Colour::Red: return setColour( "[0;31m" ); + case Colour::Green: return setColour( "[0;32m" ); + case Colour::Blue: return setColour( "[0:34m" ); + case Colour::Cyan: return setColour( "[0;36m" ); + case Colour::Yellow: return setColour( "[0;33m" ); + case Colour::Grey: return setColour( "[1;30m" ); + + case Colour::LightGrey: return setColour( "[0;37m" ); + case Colour::BrightRed: return setColour( "[1;31m" ); + case Colour::BrightGreen: return setColour( "[1;32m" ); + case Colour::BrightWhite: return setColour( "[1;37m" ); + + case Colour::Bright: throw std::logic_error( "not a colour" ); + } + } + private: + void setColour( const char* _escapeCode ) { + std::cout << '\033' << _escapeCode; + } + }; + + inline bool shouldUseColourForPlatform() { + return isatty(STDOUT_FILENO); + } + + static Detail::IColourImpl* platformColourInstance() { + static PosixColourImpl s_instance; + return &s_instance; + } + +} // end anon namespace +} // end namespace Catch + +#endif // not Windows + +namespace Catch { + + namespace { + struct NoColourImpl : Detail::IColourImpl { + void use( Colour::Code ) {} + + static IColourImpl* instance() { + static NoColourImpl s_instance; + return &s_instance; + } + }; + static bool shouldUseColour() { + return shouldUseColourForPlatform() && !isDebuggerActive(); + } + } + + Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } + Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast( _other ).m_moved = true; } + Colour::~Colour(){ if( !m_moved ) use( None ); } + void Colour::use( Code _colourCode ) { + impl()->use( _colourCode ); + } + + Detail::IColourImpl* Colour::impl() { + return shouldUseColour() + ? platformColourInstance() + : NoColourImpl::instance(); + } + +} // end namespace Catch + +// #included from: catch_generators_impl.hpp +#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED + +#include +#include +#include + +namespace Catch { + + struct GeneratorInfo : IGeneratorInfo { + + GeneratorInfo( std::size_t size ) + : m_size( size ), + m_currentIndex( 0 ) + {} + + bool moveNext() { + if( ++m_currentIndex == m_size ) { + m_currentIndex = 0; + return false; + } + return true; + } + + std::size_t getCurrentIndex() const { + return m_currentIndex; + } + + std::size_t m_size; + std::size_t m_currentIndex; + }; + + /////////////////////////////////////////////////////////////////////////// + + class GeneratorsForTest : public IGeneratorsForTest { + + public: + ~GeneratorsForTest() { + deleteAll( m_generatorsInOrder ); + } + + IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { + std::map::const_iterator it = m_generatorsByName.find( fileInfo ); + if( it == m_generatorsByName.end() ) { + IGeneratorInfo* info = new GeneratorInfo( size ); + m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); + m_generatorsInOrder.push_back( info ); + return *info; + } + return *it->second; + } + + bool moveNext() { + std::vector::const_iterator it = m_generatorsInOrder.begin(); + std::vector::const_iterator itEnd = m_generatorsInOrder.end(); + for(; it != itEnd; ++it ) { + if( (*it)->moveNext() ) + return true; + } + return false; + } + + private: + std::map m_generatorsByName; + std::vector m_generatorsInOrder; + }; + + IGeneratorsForTest* createGeneratorsForTest() + { + return new GeneratorsForTest(); + } + +} // end namespace Catch + +// #included from: catch_assertionresult.hpp +#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED + +namespace Catch { + + AssertionInfo::AssertionInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + capturedExpression( _capturedExpression ), + resultDisposition( _resultDisposition ) + {} + + AssertionResult::AssertionResult() {} + + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} + + AssertionResult::~AssertionResult() {} + + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } + + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } + + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } + + bool AssertionResult::hasExpression() const { + return !m_info.capturedExpression.empty(); + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + if( isFalseTest( m_info.resultDisposition ) ) + return "!" + m_info.capturedExpression; + else + return m_info.capturedExpression; + } + std::string AssertionResult::getExpressionInMacro() const { + if( m_info.macroName.empty() ) + return m_info.capturedExpression; + else + return m_info.macroName + "( " + m_info.capturedExpression + " )"; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + return m_resultData.reconstructedExpression; + } + + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + std::string AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + +} // end namespace Catch + +// #included from: catch_test_case_info.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED + +namespace Catch { + + inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( tag == "." || + tag == "hide" || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else + return TestCaseInfo::None; + } + inline bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] ); + } + inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + if( isReservedTag( tag ) ) { + { + Colour colourGuard( Colour::Red ); + std::cerr + << "Tag name [" << tag << "] not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n"; + } + { + Colour colourGuard( Colour::FileName ); + std::cerr << _lineInfo << std::endl; + } + exit(1); + } + } + + TestCase makeTestCase( ITestCase* _testCase, + std::string const& _className, + std::string const& _name, + std::string const& _descOrTags, + SourceLineInfo const& _lineInfo ) + { + bool isHidden( startsWith( _name, "./" ) ); // Legacy support + + // Parse out tags + std::set tags; + std::string desc, tag; + bool inTag = false; + for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { + char c = _descOrTags[i]; + if( !inTag ) { + if( c == '[' ) + inTag = true; + else + desc += c; + } + else { + if( c == ']' ) { + enforceNotReservedTag( tag, _lineInfo ); + + inTag = false; + if( tag == "hide" || tag == "." ) + isHidden = true; + else + tags.insert( tag ); + tag.clear(); + } + else + tag += c; + } + } + if( isHidden ) { + tags.insert( "hide" ); + tags.insert( "." ); + } + + TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); + return TestCase( _testCase, info ); + } + + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set const& _tags, + SourceLineInfo const& _lineInfo ) + : name( _name ), + className( _className ), + description( _description ), + tags( _tags ), + lineInfo( _lineInfo ), + properties( None ) + { + std::ostringstream oss; + for( std::set::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) { + oss << "[" << *it << "]"; + std::string lcaseTag = toLower( *it ); + properties = static_cast( properties | parseSpecialTag( lcaseTag ) ); + lcaseTags.insert( lcaseTag ); + } + tagsAsString = oss.str(); + } + + TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) + : name( other.name ), + className( other.className ), + description( other.description ), + tags( other.tags ), + lcaseTags( other.lcaseTags ), + tagsAsString( other.tagsAsString ), + lineInfo( other.lineInfo ), + properties( other.properties ) + {} + + bool TestCaseInfo::isHidden() const { + return ( properties & IsHidden ) != 0; + } + bool TestCaseInfo::throws() const { + return ( properties & Throws ) != 0; + } + bool TestCaseInfo::okToFail() const { + return ( properties & (ShouldFail | MayFail ) ) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return ( properties & (ShouldFail ) ) != 0; + } + + TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} + + TestCase::TestCase( TestCase const& other ) + : TestCaseInfo( other ), + test( other.test ) + {} + + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } + + void TestCase::swap( TestCase& other ) { + test.swap( other.test ); + name.swap( other.name ); + className.swap( other.className ); + description.swap( other.description ); + tags.swap( other.tags ); + lcaseTags.swap( other.lcaseTags ); + tagsAsString.swap( other.tagsAsString ); + std::swap( TestCaseInfo::properties, static_cast( other ).properties ); + std::swap( lineInfo, other.lineInfo ); + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + TestCase& TestCase::operator = ( TestCase const& other ) { + TestCase temp( other ); + swap( temp ); + return *this; + } + + TestCaseInfo const& TestCase::getTestCaseInfo() const + { + return *this; + } + +} // end namespace Catch + +// #included from: catch_version.hpp +#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED + +namespace Catch { + + // These numbers are maintained by a script + Version libraryVersion( 1, 0, 53, "master" ); +} + +// #included from: catch_message.hpp +#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED + +namespace Catch { + + MessageInfo::MessageInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} + + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; + + //////////////////////////////////////////////////////////////////////////// + + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) + : m_info( builder.m_info ) + { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage( m_info ); + } + ScopedMessage::ScopedMessage( ScopedMessage const& other ) + : m_info( other.m_info ) + {} + + ScopedMessage::~ScopedMessage() { + getResultCapture().popScopedMessage( m_info ); + } + +} // end namespace Catch + +// #included from: catch_legacy_reporter_adapter.hpp +#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED + +// #included from: catch_legacy_reporter_adapter.h +#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED + +namespace Catch +{ + // Deprecated + struct IReporter : IShared { + virtual ~IReporter(); + + virtual bool shouldRedirectStdout() const = 0; + + virtual void StartTesting() = 0; + virtual void EndTesting( Totals const& totals ) = 0; + virtual void StartGroup( std::string const& groupName ) = 0; + virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; + virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; + virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; + virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; + virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; + virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; + virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; + virtual void Aborted() = 0; + virtual void Result( AssertionResult const& result ) = 0; + }; + + class LegacyReporterAdapter : public SharedImpl + { + public: + LegacyReporterAdapter( Ptr const& legacyReporter ); + virtual ~LegacyReporterAdapter(); + + virtual ReporterPreferences getPreferences() const; + virtual void noMatchingTestCases( std::string const& ); + virtual void testRunStarting( TestRunInfo const& ); + virtual void testGroupStarting( GroupInfo const& groupInfo ); + virtual void testCaseStarting( TestCaseInfo const& testInfo ); + virtual void sectionStarting( SectionInfo const& sectionInfo ); + virtual void assertionStarting( AssertionInfo const& ); + virtual bool assertionEnded( AssertionStats const& assertionStats ); + virtual void sectionEnded( SectionStats const& sectionStats ); + virtual void testCaseEnded( TestCaseStats const& testCaseStats ); + virtual void testGroupEnded( TestGroupStats const& testGroupStats ); + virtual void testRunEnded( TestRunStats const& testRunStats ); + + private: + Ptr m_legacyReporter; + }; +} + +namespace Catch +{ + LegacyReporterAdapter::LegacyReporterAdapter( Ptr const& legacyReporter ) + : m_legacyReporter( legacyReporter ) + {} + LegacyReporterAdapter::~LegacyReporterAdapter() {} + + ReporterPreferences LegacyReporterAdapter::getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); + return prefs; + } + + void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} + void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { + m_legacyReporter->StartTesting(); + } + void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { + m_legacyReporter->StartGroup( groupInfo.name ); + } + void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { + m_legacyReporter->StartTestCase( testInfo ); + } + void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { + m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); + } + void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { + // Not on legacy interface + } + + bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { + for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); + it != itEnd; + ++it ) { + if( it->type == ResultWas::Info ) { + ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal ); + rb << it->message; + rb.setResultType( ResultWas::Info ); + AssertionResult result = rb.build(); + m_legacyReporter->Result( result ); + } + } + } + m_legacyReporter->Result( assertionStats.assertionResult ); + return true; + } + void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { + if( sectionStats.missingAssertions ) + m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); + m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); + } + void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { + m_legacyReporter->EndTestCase + ( testCaseStats.testInfo, + testCaseStats.totals, + testCaseStats.stdOut, + testCaseStats.stdErr ); + } + void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { + if( testGroupStats.aborting ) + m_legacyReporter->Aborted(); + m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); + } + void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { + m_legacyReporter->EndTesting( testRunStats.totals ); + } +} + +// #included from: catch_timer.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++11-long-long" +#endif + +#ifdef CATCH_PLATFORM_WINDOWS +#include +#else +#include +#endif + +namespace Catch { + + namespace { +#ifdef CATCH_PLATFORM_WINDOWS + uint64_t getCurrentTicks() { + static uint64_t hz=0, hzo=0; + if (!hz) { + QueryPerformanceFrequency((LARGE_INTEGER*)&hz); + QueryPerformanceCounter((LARGE_INTEGER*)&hzo); + } + uint64_t t; + QueryPerformanceCounter((LARGE_INTEGER*)&t); + return ((t-hzo)*1000000)/hz; + } +#else + uint64_t getCurrentTicks() { + timeval t; + gettimeofday(&t,NULL); + return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); + } +#endif + } + + void Timer::start() { + m_ticks = getCurrentTicks(); + } + unsigned int Timer::getElapsedNanoseconds() const { + return static_cast(getCurrentTicks() - m_ticks); + } + unsigned int Timer::getElapsedMilliseconds() const { + return static_cast((getCurrentTicks() - m_ticks)/1000); + } + double Timer::getElapsedSeconds() const { + return (getCurrentTicks() - m_ticks)/1000000.0; + } + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +// #included from: catch_common.hpp +#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ) { + return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; + } + bool endsWith( std::string const& s, std::string const& suffix ) { + return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; + } + bool contains( std::string const& s, std::string const& infix ) { + return s.find( infix ) != std::string::npos; + } + void toLowerInPlace( std::string& s ) { + std::transform( s.begin(), s.end(), s.begin(), ::tolower ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + std::string trim( std::string const& str ) { + static char const* whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of( whitespaceChars ); + std::string::size_type end = str.find_last_not_of( whitespaceChars ); + + return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; + } + + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) + {} + + std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { + os << pluraliser.m_count << " " << pluraliser.m_label; + if( pluraliser.m_count != 1 ) + os << "s"; + return os; + } + + SourceLineInfo::SourceLineInfo() : line( 0 ){} + SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) + : file( _file ), + line( _line ) + {} + SourceLineInfo::SourceLineInfo( SourceLineInfo const& other ) + : file( other.file ), + line( other.line ) + {} + bool SourceLineInfo::empty() const { + return file.empty(); + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { + return line == other.line && file == other.file; + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << "(" << info.line << ")"; +#else + os << info.file << ":" << info.line; +#endif + return os; + } + + void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { + std::ostringstream oss; + oss << locationInfo << ": Internal Catch error: '" << message << "'"; + if( alwaysTrue() ) + throw std::logic_error( oss.str() ); + } +} + +// #included from: catch_section.hpp +#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED + +namespace Catch { + + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& _description ) + : name( _name ), + description( _description ), + lineInfo( _lineInfo ) + {} + + Section::Section( SectionInfo const& info ) + : m_info( info ), + m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + m_timer.start(); + } + + Section::~Section() { + if( m_sectionIncluded ) + getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() ); + } + + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } + +} // end namespace Catch + +// #included from: catch_debugger.hpp +#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED + +#include + +#ifdef CATCH_PLATFORM_MAC + + #include + #include + #include + #include + #include + + namespace Catch{ + + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ + + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) { + std::cerr << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + } // namespace Catch + +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + inline bool isDebuggerActive() { return false; } + } +#endif // Platform + +#ifdef CATCH_PLATFORM_WINDOWS + extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* ); + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } +#else + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + std::cout << text; + } + } +#endif // Platform + +// #included from: catch_tostring.hpp +#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED + +namespace Catch { + +namespace Detail { + + namespace { + struct Endianness { + enum Arch { Big, Little }; + + static Arch which() { + union _{ + int asInt; + char asChar[sizeof (int)]; + } u; + + u.asInt = 1; + return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; + } + }; + } + + std::string rawMemoryToString( const void *object, std::size_t size ) + { + // Reverse order for little endian architectures + int i = 0, end = static_cast( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + std::ostringstream os; + os << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + os << std::setw(2) << static_cast(bytes[i]); + return os.str(); + } +} + +std::string toString( std::string const& value ) { + std::string s = value; + if( getCurrentContext().getConfig()->showInvisibles() ) { + for(size_t i = 0; i < s.size(); ++i ) { + std::string subs; + switch( s[i] ) { + case '\n': subs = "\\n"; break; + case '\t': subs = "\\t"; break; + default: break; + } + if( !subs.empty() ) { + s = s.substr( 0, i ) + subs + s.substr( i+1 ); + ++i; + } + } + } + return "\"" + s + "\""; +} +std::string toString( std::wstring const& value ) { + + std::string s; + s.reserve( value.size() ); + for(size_t i = 0; i < value.size(); ++i ) + s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; + return toString( s ); +} + +std::string toString( const char* const value ) { + return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); +} + +std::string toString( char* const value ) { + return Catch::toString( static_cast( value ) ); +} + +std::string toString( const wchar_t* const value ) +{ + return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); +} + +std::string toString( wchar_t* const value ) +{ + return Catch::toString( static_cast( value ) ); +} + +std::string toString( int value ) { + std::ostringstream oss; + oss << value; + return oss.str(); +} + +std::string toString( unsigned long value ) { + std::ostringstream oss; + if( value > 8192 ) + oss << "0x" << std::hex << value; + else + oss << value; + return oss.str(); +} + +std::string toString( unsigned int value ) { + return toString( static_cast( value ) ); +} + +template +std::string fpToString( T value, int precision ) { + std::ostringstream oss; + oss << std::setprecision( precision ) + << std::fixed + << value; + std::string d = oss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; +} + +std::string toString( const double value ) { + return fpToString( value, 10 ); +} +std::string toString( const float value ) { + return fpToString( value, 5 ) + "f"; +} + +std::string toString( bool value ) { + return value ? "true" : "false"; +} + +std::string toString( char value ) { + return value < ' ' + ? toString( static_cast( value ) ) + : Detail::makeString( value ); +} + +std::string toString( signed char value ) { + return toString( static_cast( value ) ); +} + +std::string toString( unsigned char value ) { + return toString( static_cast( value ) ); +} + +#ifdef CATCH_CONFIG_CPP11_NULLPTR +std::string toString( std::nullptr_t ) { + return "nullptr"; +} +#endif + +#ifdef __OBJC__ + std::string toString( NSString const * const& nsstring ) { + if( !nsstring ) + return "nil"; + return "@" + toString([nsstring UTF8String]); + } + std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { + if( !nsstring ) + return "nil"; + return "@" + toString([nsstring UTF8String]); + } + std::string toString( NSObject* const& nsObject ) { + return toString( [nsObject description] ); + } +#endif + +} // end namespace Catch + +// #included from: catch_result_builder.hpp +#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED + +namespace Catch { + + ResultBuilder::ResultBuilder( char const* macroName, + SourceLineInfo const& lineInfo, + char const* capturedExpression, + ResultDisposition::Flags resultDisposition ) + : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ), + m_shouldDebugBreak( false ), + m_shouldThrow( false ) + {} + + ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { + m_data.resultType = result; + return *this; + } + ResultBuilder& ResultBuilder::setResultType( bool result ) { + m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; + return *this; + } + ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) { + m_exprComponents.lhs = lhs; + return *this; + } + ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) { + m_exprComponents.rhs = rhs; + return *this; + } + ResultBuilder& ResultBuilder::setOp( std::string const& op ) { + m_exprComponents.op = op; + return *this; + } + + void ResultBuilder::endExpression() { + m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition ); + captureExpression(); + } + + void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { + m_assertionInfo.resultDisposition = resultDisposition; + m_stream.oss << Catch::translateActiveException(); + captureResult( ResultWas::ThrewException ); + } + + void ResultBuilder::captureResult( ResultWas::OfType resultType ) { + setResultType( resultType ); + captureExpression(); + } + + void ResultBuilder::captureExpression() { + AssertionResult result = build(); + getResultCapture().assertionEnded( result ); + + if( !result.isOk() ) { + if( getCurrentContext().getConfig()->shouldDebugBreak() ) + m_shouldDebugBreak = true; + if( getCurrentContext().getRunner()->aborting() || m_assertionInfo.resultDisposition == ResultDisposition::Normal ) + m_shouldThrow = true; + } + } + void ResultBuilder::react() { + if( m_shouldThrow ) + throw Catch::TestFailureException(); + } + + bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } + bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } + + AssertionResult ResultBuilder::build() const + { + assert( m_data.resultType != ResultWas::Unknown ); + + AssertionResultData data = m_data; + + // Flip bool results if testFalse is set + if( m_exprComponents.testFalse ) { + if( data.resultType == ResultWas::Ok ) + data.resultType = ResultWas::ExpressionFailed; + else if( data.resultType == ResultWas::ExpressionFailed ) + data.resultType = ResultWas::Ok; + } + + data.message = m_stream.oss.str(); + data.reconstructedExpression = reconstructExpression(); + if( m_exprComponents.testFalse ) { + if( m_exprComponents.op == "" ) + data.reconstructedExpression = "!" + data.reconstructedExpression; + else + data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; + } + return AssertionResult( m_assertionInfo, data ); + } + std::string ResultBuilder::reconstructExpression() const { + if( m_exprComponents.op == "" ) + return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs; + else if( m_exprComponents.op == "matches" ) + return m_exprComponents.lhs + " " + m_exprComponents.rhs; + else if( m_exprComponents.op != "!" ) { + if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && + m_exprComponents.lhs.find("\n") == std::string::npos && + m_exprComponents.rhs.find("\n") == std::string::npos ) + return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; + else + return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; + } + else + return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}"; + } + +} // end namespace Catch + +// #included from: catch_tag_alias_registry.hpp +#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED + +// #included from: catch_tag_alias_registry.h +#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED + +#include + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + virtual ~TagAliasRegistry(); + virtual Option find( std::string const& alias ) const; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; + void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + static TagAliasRegistry& get(); + + private: + std::map m_registry; + }; + +} // end namespace Catch + +#include +#include + +namespace Catch { + + TagAliasRegistry::~TagAliasRegistry() {} + + Option TagAliasRegistry::find( std::string const& alias ) const { + std::map::const_iterator it = m_registry.find( alias ); + if( it != m_registry.end() ) + return it->second; + else + return Option(); + } + + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); + it != itEnd; + ++it ) { + std::size_t pos = expandedTestSpec.find( it->first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + it->second.tag + + expandedTestSpec.substr( pos + it->first.size() ); + } + } + return expandedTestSpec; + } + + void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + + if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { + std::ostringstream oss; + oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; + throw std::domain_error( oss.str().c_str() ); + } + if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { + std::ostringstream oss; + oss << "error: tag alias, \"" << alias << "\" already registered.\n" + << "\tFirst seen at " << find(alias)->lineInfo << "\n" + << "\tRedefined at " << lineInfo; + throw std::domain_error( oss.str().c_str() ); + } + } + + TagAliasRegistry& TagAliasRegistry::get() { + static TagAliasRegistry instance; + return instance; + + } + + ITagAliasRegistry::~ITagAliasRegistry() {} + ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } + + RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + try { + TagAliasRegistry::get().add( alias, tag, lineInfo ); + } + catch( std::exception& ex ) { + Colour colourGuard( Colour::Red ); + std::cerr << ex.what() << std::endl; + exit(1); + } + } + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_xml.hpp +#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED + +// #included from: catch_reporter_bases.hpp +#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED + +namespace Catch { + + struct StreamingReporterBase : SharedImpl { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + {} + + virtual ~StreamingReporterBase(); + + virtual void noMatchingTestCases( std::string const& ) {} + + virtual void testRunStarting( TestRunInfo const& _testRunInfo ) { + currentTestRunInfo = _testRunInfo; + } + virtual void testGroupStarting( GroupInfo const& _groupInfo ) { + currentGroupInfo = _groupInfo; + } + + virtual void testCaseStarting( TestCaseInfo const& _testInfo ) { + currentTestCaseInfo = _testInfo; + } + virtual void sectionStarting( SectionInfo const& _sectionInfo ) { + m_sectionStack.push_back( _sectionInfo ); + } + + virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) { + m_sectionStack.pop_back(); + } + virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) { + currentTestCaseInfo.reset(); + assert( m_sectionStack.empty() ); + } + virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) { + currentGroupInfo.reset(); + } + virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + Ptr m_config; + std::ostream& stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + }; + + struct CumulativeReporterBase : SharedImpl { + template + struct Node : SharedImpl<> { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + typedef std::vector > ChildNodes; + T value; + ChildNodes children; + }; + struct SectionNode : SharedImpl<> { + explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} + virtual ~SectionNode(); + + bool operator == ( SectionNode const& other ) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator == ( Ptr const& other ) const { + return operator==( *other ); + } + + SectionStats stats; + typedef std::vector > ChildSections; + typedef std::vector Assertions; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + bool operator() ( Ptr const& node ) const { + return node->stats.sectionInfo.lineInfo == m_other.lineInfo; + } + private: + void operator=( BySectionInfo const& ); + SectionInfo const& m_other; + }; + + typedef Node TestCaseNode; + typedef Node TestGroupNode; + typedef Node TestRunNode; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + {} + ~CumulativeReporterBase(); + + virtual void testRunStarting( TestRunInfo const& ) {} + virtual void testGroupStarting( GroupInfo const& ) {} + + virtual void testCaseStarting( TestCaseInfo const& ) {} + + virtual void sectionStarting( SectionInfo const& sectionInfo ) { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + Ptr node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = new SectionNode( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + SectionNode::ChildSections::const_iterator it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = new SectionNode( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = node; + } + + virtual void assertionStarting( AssertionInfo const& ) {} + + virtual bool assertionEnded( AssertionStats const& assertionStats ) { + assert( !m_sectionStack.empty() ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back( assertionStats ); + return true; + } + virtual void sectionEnded( SectionStats const& sectionStats ) { + assert( !m_sectionStack.empty() ); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { + Ptr node = new TestCaseNode( testCaseStats ); + assert( m_sectionStack.size() == 0 ); + node->children.push_back( m_rootSection ); + m_testCases.push_back( node ); + m_rootSection.reset(); + + assert( m_deepestSection ); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { + Ptr node = new TestGroupNode( testGroupStats ); + node->children.swap( m_testCases ); + m_testGroups.push_back( node ); + } + virtual void testRunEnded( TestRunStats const& testRunStats ) { + Ptr node = new TestRunNode( testRunStats ); + node->children.swap( m_testGroups ); + m_testRuns.push_back( node ); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + Ptr m_config; + std::ostream& stream; + std::vector m_assertions; + std::vector > > m_sections; + std::vector > m_testCases; + std::vector > m_testGroups; + + std::vector > m_testRuns; + + Ptr m_rootSection; + Ptr m_deepestSection; + std::vector > m_sectionStack; + + }; + +} // end namespace Catch + +// #included from: ../internal/catch_reporter_registrars.hpp +#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED + +namespace Catch { + + template + class LegacyReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new LegacyReporterAdapter( new T( config ) ); + } + + virtual std::string getDescription() const { + return T::getDescription(); + } + }; + + public: + + LegacyReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); + } + }; + + template + class ReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + + // *** Please Note ***: + // - If you end up here looking at a compiler error because it's trying to register + // your custom reporter class be aware that the native reporter interface has changed + // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via + // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. + // However please consider updating to the new interface as the old one is now + // deprecated and will probably be removed quite soon! + // Please contact me via github if you have any questions at all about this. + // In fact, ideally, please contact me anyway to let me know you've hit this - as I have + // no idea who is actually using custom reporters at all (possibly no-one!). + // The new interface is designed to minimise exposure to interface changes in the future. + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new T( config ); + } + + virtual std::string getDescription() const { + return T::getDescription(); + } + }; + + public: + + ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); + } + }; +} + +#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ + namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } +#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ + namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } + +// #included from: ../internal/catch_xmlwriter.hpp +#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + ScopedElement( ScopedElement const& other ) + : m_writer( other.m_writer ){ + other.m_writer = NULL; + } + + ~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } + + ScopedElement& writeText( std::string const& text, bool indent = true ) { + m_writer->writeText( text, indent ); + return *this; + } + + template + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer; + }; + + XmlWriter() + : m_tagIsOpen( false ), + m_needsNewline( false ), + m_os( &std::cout ) + {} + + XmlWriter( std::ostream& os ) + : m_tagIsOpen( false ), + m_needsNewline( false ), + m_os( &os ) + {} + + ~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } + +//# ifndef CATCH_CPP11_OR_GREATER +// XmlWriter& operator = ( XmlWriter const& other ) { +// XmlWriter temp( other ); +// swap( temp ); +// return *this; +// } +//# else +// XmlWriter( XmlWriter const& ) = default; +// XmlWriter( XmlWriter && ) = default; +// XmlWriter& operator = ( XmlWriter const& ) = default; +// XmlWriter& operator = ( XmlWriter && ) = default; +//# endif +// +// void swap( XmlWriter& other ) { +// std::swap( m_tagIsOpen, other.m_tagIsOpen ); +// std::swap( m_needsNewline, other.m_needsNewline ); +// std::swap( m_tags, other.m_tags ); +// std::swap( m_indent, other.m_indent ); +// std::swap( m_os, other.m_os ); +// } + + XmlWriter& startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + stream() << m_indent << "<" << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + ScopedElement scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + stream() << "/>\n"; + m_tagIsOpen = false; + } + else { + stream() << m_indent << "\n"; + } + m_tags.pop_back(); + return *this; + } + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) { + stream() << " " << name << "=\""; + writeEncodedText( attribute ); + stream() << "\""; + } + return *this; + } + + XmlWriter& writeAttribute( std::string const& name, bool attribute ) { + stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; + return *this; + } + + template + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + if( !name.empty() ) + stream() << " " << name << "=\"" << attribute << "\""; + return *this; + } + + XmlWriter& writeText( std::string const& text, bool indent = true ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + stream() << m_indent; + writeEncodedText( text ); + m_needsNewline = true; + } + return *this; + } + + XmlWriter& writeComment( std::string const& text ) { + ensureTagClosed(); + stream() << m_indent << ""; + m_needsNewline = true; + return *this; + } + + XmlWriter& writeBlankLine() { + ensureTagClosed(); + stream() << "\n"; + return *this; + } + + void setStream( std::ostream& os ) { + m_os = &os; + } + + private: + XmlWriter( XmlWriter const& ); + void operator=( XmlWriter const& ); + + std::ostream& stream() { + return *m_os; + } + + void ensureTagClosed() { + if( m_tagIsOpen ) { + stream() << ">\n"; + m_tagIsOpen = false; + } + } + + void newlineIfNecessary() { + if( m_needsNewline ) { + stream() << "\n"; + m_needsNewline = false; + } + } + + void writeEncodedText( std::string const& text ) { + static const char* charsToEncode = "<&\""; + std::string mtext = text; + std::string::size_type pos = mtext.find_first_of( charsToEncode ); + while( pos != std::string::npos ) { + stream() << mtext.substr( 0, pos ); + + switch( mtext[pos] ) { + case '<': + stream() << "<"; + break; + case '&': + stream() << "&"; + break; + case '\"': + stream() << """; + break; + } + mtext = mtext.substr( pos+1 ); + pos = mtext.find_first_of( charsToEncode ); + } + stream() << mtext; + } + + bool m_tagIsOpen; + bool m_needsNewline; + std::vector m_tags; + std::string m_indent; + std::ostream* m_os; + }; + +} +namespace Catch { + class XmlReporter : public SharedImpl { + public: + XmlReporter( ReporterConfig const& config ) : m_config( config ), m_sectionDepth( 0 ) {} + + static std::string getDescription() { + return "Reports test results as an XML document"; + } + virtual ~XmlReporter(); + + private: // IReporter + + virtual bool shouldRedirectStdout() const { + return true; + } + + virtual void StartTesting() { + m_xml.setStream( m_config.stream() ); + m_xml.startElement( "Catch" ); + if( !m_config.fullConfig()->name().empty() ) + m_xml.writeAttribute( "name", m_config.fullConfig()->name() ); + } + + virtual void EndTesting( const Totals& totals ) { + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", totals.assertions.passed ) + .writeAttribute( "failures", totals.assertions.failed ) + .writeAttribute( "expectedFailures", totals.assertions.failedButOk ); + m_xml.endElement(); + } + + virtual void StartGroup( const std::string& groupName ) { + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupName ); + } + + virtual void EndGroup( const std::string&, const Totals& totals ) { + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", totals.assertions.passed ) + .writeAttribute( "failures", totals.assertions.failed ) + .writeAttribute( "expectedFailures", totals.assertions.failedButOk ); + m_xml.endElement(); + } + + virtual void StartSection( const std::string& sectionName, const std::string& description ) { + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionName ) ) + .writeAttribute( "description", description ); + } + } + virtual void NoAssertionsInSection( const std::string& ) {} + virtual void NoAssertionsInTestCase( const std::string& ) {} + + virtual void EndSection( const std::string& /*sectionName*/, const Counts& assertions ) { + if( --m_sectionDepth > 0 ) { + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", assertions.passed ) + .writeAttribute( "failures", assertions.failed ) + .writeAttribute( "expectedFailures", assertions.failedButOk ); + m_xml.endElement(); + } + } + + virtual void StartTestCase( const Catch::TestCaseInfo& testInfo ) { + m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); + m_currentTestSuccess = true; + } + + virtual void Result( const Catch::AssertionResult& assertionResult ) { + if( !m_config.fullConfig()->includeSuccessfulResults() && assertionResult.getResultType() == ResultWas::Ok ) + return; + + if( assertionResult.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", assertionResult.succeeded() ) + .writeAttribute( "filename", assertionResult.getSourceInfo().file ) + .writeAttribute( "line", assertionResult.getSourceInfo().line ); + + m_xml.scopedElement( "Original" ) + .writeText( assertionResult.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( assertionResult.getExpandedExpression() ); + m_currentTestSuccess &= assertionResult.succeeded(); + } + + switch( assertionResult.getResultType() ) { + case ResultWas::ThrewException: + m_xml.scopedElement( "Exception" ) + .writeAttribute( "filename", assertionResult.getSourceInfo().file ) + .writeAttribute( "line", assertionResult.getSourceInfo().line ) + .writeText( assertionResult.getMessage() ); + m_currentTestSuccess = false; + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( assertionResult.getMessage() ); + break; + case ResultWas::Warning: + m_xml.scopedElement( "Warning" ) + .writeText( assertionResult.getMessage() ); + break; + case ResultWas::ExplicitFailure: + m_xml.scopedElement( "Failure" ) + .writeText( assertionResult.getMessage() ); + m_currentTestSuccess = false; + break; + case ResultWas::Unknown: + case ResultWas::Ok: + case ResultWas::FailureBit: + case ResultWas::ExpressionFailed: + case ResultWas::Exception: + case ResultWas::DidntThrowException: + break; + } + if( assertionResult.hasExpression() ) + m_xml.endElement(); + } + + virtual void Aborted() { + // !TBD + } + + virtual void EndTestCase( const Catch::TestCaseInfo&, const Totals&, const std::string&, const std::string& ) { + m_xml.scopedElement( "OverallResult" ).writeAttribute( "success", m_currentTestSuccess ); + m_xml.endElement(); + } + + private: + ReporterConfig m_config; + bool m_currentTestSuccess; + XmlWriter m_xml; + int m_sectionDepth; + }; + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_junit.hpp +#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED + +#include + +namespace Catch { + + class JunitReporter : public CumulativeReporterBase { + public: + JunitReporter( ReporterConfig const& _config ) + : CumulativeReporterBase( _config ), + xml( _config.stream() ) + {} + + ~JunitReporter(); + + static std::string getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + virtual void noMatchingTestCases( std::string const& /*spec*/ ) {} + + virtual ReporterPreferences getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = true; + return prefs; + } + + virtual void testRunStarting( TestRunInfo const& runInfo ) { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + } + + virtual void testGroupStarting( GroupInfo const& groupInfo ) { + suiteTimer.start(); + stdOutForSuite.str(""); + stdErrForSuite.str(""); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); + } + + virtual bool assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { + stdOutForSuite << testCaseStats.stdOut; + stdErrForSuite << testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } + + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } + + virtual void testRunEndedCumulative() { + xml.endElement(); + } + + void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else + xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "timestamp", "tbd" ); // !TBD + + // Write test cases + for( TestGroupNode::ChildNodes::const_iterator + it = groupNode.children.begin(), itEnd = groupNode.children.end(); + it != itEnd; + ++it ) + writeTestCase( **it ); + + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); + } + + void writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if( className.empty() ) { + if( rootSection.childSections.empty() ) + className = "global"; + } + writeSection( className, "", rootSection ); + } + + void writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode ) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + "/" + name; + + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); + } + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } + xml.writeAttribute( "time", toString( sectionNode.stats.durationInSeconds ) ); + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); + } + for( SectionNode::ChildSections::const_iterator + it = sectionNode.childSections.begin(), + itEnd = sectionNode.childSections.end(); + it != itEnd; + ++it ) + if( className.empty() ) + writeSection( name, "", **it ); + else + writeSection( className, name, **it ); + } + + void writeAssertions( SectionNode const& sectionNode ) { + for( SectionNode::Assertions::const_iterator + it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); + it != itEnd; + ++it ) + writeAssertion( *it ); + } + void writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; + switch( result.getResultType() ) { + case ResultWas::ThrewException: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + + xml.writeAttribute( "message", result.getExpandedExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); + + std::ostringstream oss; + if( !result.getMessage().empty() ) + oss << result.getMessage() << "\n"; + for( std::vector::const_iterator + it = stats.infoMessages.begin(), + itEnd = stats.infoMessages.end(); + it != itEnd; + ++it ) + if( it->type == ResultWas::Info ) + oss << it->message << "\n"; + + oss << "at " << result.getSourceInfo(); + xml.writeText( oss.str(), false ); + } + } + + XmlWriter xml; + Timer suiteTimer; + std::ostringstream stdOutForSuite; + std::ostringstream stdErrForSuite; + unsigned int unexpectedExceptions; + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_console.hpp +#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED + +#include + +namespace Catch { + + struct ConsoleReporter : StreamingReporterBase { + ConsoleReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_headerPrinted( false ) + {} + + virtual ~ConsoleReporter(); + static std::string getDescription() { + return "Reports test results as plain lines of text"; + } + virtual ReporterPreferences getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = false; + return prefs; + } + + virtual void noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << "'" << std::endl; + } + + virtual void assertionStarting( AssertionInfo const& ) { + } + + virtual bool assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + lazyPrint(); + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + stream << std::endl; + return true; + } + + virtual void sectionStarting( SectionInfo const& _sectionInfo ) { + m_headerPrinted = false; + StreamingReporterBase::sectionStarting( _sectionInfo ); + } + virtual void sectionEnded( SectionStats const& _sectionStats ) { + if( _sectionStats.missingAssertions ) { + lazyPrint(); + Colour colour( Colour::ResultError ); + if( m_sectionStack.size() > 1 ) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + if( m_headerPrinted ) { + if( m_config->showDurations() == ShowDurations::Always ) + stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + m_headerPrinted = false; + } + else { + if( m_config->showDurations() == ShowDurations::Always ) + stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + } + StreamingReporterBase::sectionEnded( _sectionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) { + StreamingReporterBase::testCaseEnded( _testCaseStats ); + m_headerPrinted = false; + } + virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) { + if( currentGroupInfo.used ) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals( _testGroupStats.totals ); + stream << "\n" << std::endl; + } + StreamingReporterBase::testGroupEnded( _testGroupStats ); + } + virtual void testRunEnded( TestRunStats const& _testRunStats ) { + printTotalsDivider( _testRunStats.totals ); + printTotals( _testRunStats.totals ); + stream << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + private: + + class AssertionPrinter { + void operator= ( AssertionPrinter const& ); + public: + AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) + : stream( _stream ), + stats( _stats ), + result( _stats.assertionResult ), + colour( Colour::None ), + message( result.getMessage() ), + messages( _stats.infoMessages ), + printInfoMessages( _printInfoMessages ) + { + switch( result.getResultType() ) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if( _stats.infoMessages.size() == 1 ) + messageLabel = "with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if( result.isOk() ) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } + else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if( _stats.infoMessages.size() == 1 ) + messageLabel = "with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with message"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if( _stats.infoMessages.size() == 1 ) + messageLabel = "explicitly with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } + + void print() const { + printSourceInfo(); + if( stats.totals.assertions.total() > 0 ) { + if( result.isOk() ) + stream << "\n"; + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } + else { + stream << "\n"; + } + printMessage(); + } + + private: + void printResultType() const { + if( !passOrFail.empty() ) { + Colour colourGuard( colour ); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if( result.hasExpression() ) { + Colour colourGuard( Colour::OriginalExpression ); + stream << " "; + stream << result.getExpressionInMacro(); + stream << "\n"; + } + } + void printReconstructedExpression() const { + if( result.hasExpandedExpression() ) { + stream << "with expansion:\n"; + Colour colourGuard( Colour::ReconstructedExpression ); + stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n"; + } + } + void printMessage() const { + if( !messageLabel.empty() ) + stream << messageLabel << ":" << "\n"; + for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); + it != itEnd; + ++it ) { + // If this assertion is a warning ignore any INFO messages + if( printInfoMessages || it->type != ResultWas::Info ) + stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; + } + } + void printSourceInfo() const { + Colour colourGuard( Colour::FileName ); + stream << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + bool printInfoMessages; + }; + + void lazyPrint() { + + if( !currentTestRunInfo.used ) + lazyPrintRunInfo(); + if( !currentGroupInfo.used ) + lazyPrintGroupInfo(); + + if( !m_headerPrinted ) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } + } + void lazyPrintRunInfo() { + stream << "\n" << getLineOfChars<'~'>() << "\n"; + Colour colour( Colour::SecondaryText ); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion.majorVersion << "." + << libraryVersion.minorVersion << " b" + << libraryVersion.buildNumber; + if( libraryVersion.branchName != std::string( "master" ) ) + stream << " (" << libraryVersion.branchName << ")"; + stream << " host application.\n" + << "Run with -? for options\n\n"; + + currentTestRunInfo.used = true; + } + void lazyPrintGroupInfo() { + if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { + printClosedHeader( "Group: " + currentGroupInfo->name ); + currentGroupInfo.used = true; + } + } + void printTestCaseAndSectionHeader() { + assert( !m_sectionStack.empty() ); + printOpenHeader( currentTestCaseInfo->name ); + + if( m_sectionStack.size() > 1 ) { + Colour colourGuard( Colour::Headers ); + + std::vector::const_iterator + it = m_sectionStack.begin()+1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for( ; it != itEnd; ++it ) + printHeaderString( it->name, 2 ); + } + + SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; + + if( !lineInfo.empty() ){ + stream << getLineOfChars<'-'>() << "\n"; + Colour colourGuard( Colour::FileName ); + stream << lineInfo << "\n"; + } + stream << getLineOfChars<'.'>() << "\n" << std::endl; + } + + void printClosedHeader( std::string const& _name ) { + printOpenHeader( _name ); + stream << getLineOfChars<'.'>() << "\n"; + } + void printOpenHeader( std::string const& _name ) { + stream << getLineOfChars<'-'>() << "\n"; + { + Colour colourGuard( Colour::Headers ); + printHeaderString( _name ); + } + } + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { + std::size_t i = _string.find( ": " ); + if( i != std::string::npos ) + i+=2; + else + i = 0; + stream << Text( _string, TextAttributes() + .setIndent( indent+i) + .setInitialIndent( indent ) ) << "\n"; + } + + struct SummaryColumn { + + SummaryColumn( std::string const& _label, Colour::Code _colour ) + : label( _label ), + colour( _colour ) + {} + SummaryColumn addRow( std::size_t count ) { + std::ostringstream oss; + oss << count; + std::string row = oss.str(); + for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { + while( it->size() < row.size() ) + *it = " " + *it; + while( it->size() > row.size() ) + row = " " + row; + } + rows.push_back( row ); + return *this; + } + + std::string label; + Colour::Code colour; + std::vector rows; + + }; + + void printTotals( Totals const& totals ) { + if( totals.testCases.total() == 0 ) { + stream << Colour( Colour::Warning ) << "No tests ran\n"; + } + else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) { + stream << Colour( Colour::ResultSuccess ) << "All tests passed"; + stream << " (" + << pluralise( totals.assertions.passed, "assertion" ) << " in " + << pluralise( totals.testCases.passed, "test case" ) << ")" + << "\n"; + } + else { + + std::vector columns; + columns.push_back( SummaryColumn( "", Colour::None ) + .addRow( totals.testCases.total() ) + .addRow( totals.assertions.total() ) ); + columns.push_back( SummaryColumn( "passed", Colour::Success ) + .addRow( totals.testCases.passed ) + .addRow( totals.assertions.passed ) ); + columns.push_back( SummaryColumn( "failed", Colour::ResultError ) + .addRow( totals.testCases.failed ) + .addRow( totals.assertions.failed ) ); + columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) + .addRow( totals.testCases.failedButOk ) + .addRow( totals.assertions.failedButOk ) ); + + printSummaryRow( "test cases", columns, 0 ); + printSummaryRow( "assertions", columns, 1 ); + } + } + void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { + for( std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it ) { + std::string value = it->rows[row]; + if( it->label.empty() ) { + stream << label << ": "; + if( value != "0" ) + stream << value; + else + stream << Colour( Colour::Warning ) << "- none -"; + } + else if( value != "0" ) { + stream << Colour( Colour::LightGrey ) << " | "; + stream << Colour( it->colour ) + << value << " " << it->label; + } + } + stream << "\n"; + } + + static std::size_t makeRatio( std::size_t number, std::size_t total ) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; + return ( ratio == 0 && number > 0 ) ? 1 : ratio; + } + static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { + if( i > j && i > k ) + return i; + else if( j > k ) + return j; + else + return k; + } + + void printTotalsDivider( Totals const& totals ) { + if( totals.testCases.total() > 0 ) { + std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); + std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); + std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); + while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) + findMax( failedRatio, failedButOkRatio, passedRatio )++; + while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) + findMax( failedRatio, failedButOkRatio, passedRatio )--; + + stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); + stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); + if( totals.testCases.allPassed() ) + stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); + else + stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); + } + else { + stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); + } + stream << "\n"; + } + void printSummaryDivider() { + stream << getLineOfChars<'-'>() << "\n"; + } + template + static char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; + } + + private: + bool m_headerPrinted; + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_compact.hpp +#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED + +namespace Catch { + + struct CompactReporter : StreamingReporterBase { + + CompactReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ) + {} + + virtual ~CompactReporter(); + + static std::string getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + virtual ReporterPreferences getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = false; + return prefs; + } + + virtual void noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << "'" << std::endl; + } + + virtual void assertionStarting( AssertionInfo const& ) { + } + + virtual bool assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + + stream << std::endl; + return true; + } + + virtual void testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( _testRunStats.totals ); + stream << "\n" << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + private: + class AssertionPrinter { + void operator= ( AssertionPrinter const& ); + public: + AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) + : stream( _stream ) + , stats( _stats ) + , result( _stats.assertionResult ) + , messages( _stats.infoMessages ) + , itMessage( _stats.infoMessages.begin() ) + , printInfoMessages( _printInfoMessages ) + {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch( result.getResultType() ) { + case ResultWas::Ok: + printResultType( Colour::ResultSuccess, passedString() ); + printOriginalExpression(); + printReconstructedExpression(); + if ( ! result.hasExpression() ) + printRemainingMessages( Colour::None ); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if( result.isOk() ) + printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); + else + printResultType( Colour::Error, failedString() ); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType( Colour::Error, failedString() ); + printIssue( "unexpected exception with message:" ); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType( Colour::Error, failedString() ); + printIssue( "expected exception, got none" ); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType( Colour::None, "info" ); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType( Colour::None, "warning" ); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType( Colour::Error, failedString() ); + printIssue( "explicitly" ); + printRemainingMessages( Colour::None ); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType( Colour::Error, "** internal error **" ); + break; + } + } + + private: + // Colour::LightGrey + + static Colour::Code dimColour() { return Colour::FileName; } + +#ifdef CATCH_PLATFORM_MAC + static const char* failedString() { return "FAILED"; } + static const char* passedString() { return "PASSED"; } +#else + static const char* failedString() { return "failed"; } + static const char* passedString() { return "passed"; } +#endif + + void printSourceInfo() const { + Colour colourGuard( Colour::FileName ); + stream << result.getSourceInfo() << ":"; + } + + void printResultType( Colour::Code colour, std::string passOrFail ) const { + if( !passOrFail.empty() ) { + { + Colour colourGuard( colour ); + stream << " " << passOrFail; + } + stream << ":"; + } + } + + void printIssue( std::string issue ) const { + stream << " " << issue; + } + + void printExpressionWas() { + if( result.hasExpression() ) { + stream << ";"; + { + Colour colour( dimColour() ); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if( result.hasExpression() ) { + stream << " " << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if( result.hasExpandedExpression() ) { + { + Colour colour( dimColour() ); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if ( itMessage != messages.end() ) { + stream << " '" << itMessage->message << "'"; + ++itMessage; + } + } + + void printRemainingMessages( Colour::Code colour = dimColour() ) { + if ( itMessage == messages.end() ) + return; + + // using messages.end() directly yields compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); + + { + Colour colourGuard( colour ); + stream << " with " << pluralise( N, "message" ) << ":"; + } + + for(; itMessage != itEnd; ) { + // If this assertion is a warning ignore any INFO messages + if( printInfoMessages || itMessage->type != ResultWas::Info ) { + stream << " '" << itMessage->message << "'"; + if ( ++itMessage != itEnd ) { + Colour colourGuard( dimColour() ); + stream << " and"; + } + } + } + } + + private: + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + }; + + // Colour, message variants: + // - white: No tests ran. + // - red: Failed [both/all] N test cases, failed [both/all] M assertions. + // - white: Passed [both/all] N test cases (no assertions). + // - red: Failed N tests cases, failed M assertions. + // - green: Passed [both/all] N tests cases with M assertions. + + std::string bothOrAll( std::size_t count ) const { + return count == 1 ? "" : count == 2 ? "both " : "all " ; + } + + void printTotals( const Totals& totals ) const { + if( totals.testCases.total() == 0 ) { + stream << "No tests ran."; + } + else if( totals.testCases.failed == totals.testCases.total() ) { + Colour colour( Colour::ResultError ); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll( totals.assertions.failed ) : ""; + stream << + "Failed " << bothOrAll( totals.testCases.failed ) + << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << qualify_assertions_failed << + pluralise( totals.assertions.failed, "assertion" ) << "."; + } + else if( totals.assertions.total() == 0 ) { + stream << + "Passed " << bothOrAll( totals.testCases.total() ) + << pluralise( totals.testCases.total(), "test case" ) + << " (no assertions)."; + } + else if( totals.assertions.failed ) { + Colour colour( Colour::ResultError ); + stream << + "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; + } + else { + Colour colour( Colour::ResultSuccess ); + stream << + "Passed " << bothOrAll( totals.testCases.passed ) + << pluralise( totals.testCases.passed, "test case" ) << + " with " << pluralise( totals.assertions.passed, "assertion" ) << "."; + } + } + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + +} // end namespace Catch + +namespace Catch { + NonCopyable::~NonCopyable() {} + IShared::~IShared() {} + StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} + IContext::~IContext() {} + IResultCapture::~IResultCapture() {} + ITestCase::~ITestCase() {} + ITestCaseRegistry::~ITestCaseRegistry() {} + IRegistryHub::~IRegistryHub() {} + IMutableRegistryHub::~IMutableRegistryHub() {} + IExceptionTranslator::~IExceptionTranslator() {} + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} + IReporter::~IReporter() {} + IReporterFactory::~IReporterFactory() {} + IReporterRegistry::~IReporterRegistry() {} + IStreamingReporter::~IStreamingReporter() {} + AssertionStats::~AssertionStats() {} + SectionStats::~SectionStats() {} + TestCaseStats::~TestCaseStats() {} + TestGroupStats::~TestGroupStats() {} + TestRunStats::~TestRunStats() {} + CumulativeReporterBase::SectionNode::~SectionNode() {} + CumulativeReporterBase::~CumulativeReporterBase() {} + + StreamingReporterBase::~StreamingReporterBase() {} + ConsoleReporter::~ConsoleReporter() {} + CompactReporter::~CompactReporter() {} + IRunner::~IRunner() {} + IMutableContext::~IMutableContext() {} + IConfig::~IConfig() {} + XmlReporter::~XmlReporter() {} + JunitReporter::~JunitReporter() {} + TestRegistry::~TestRegistry() {} + FreeFunctionTestCase::~FreeFunctionTestCase() {} + IGeneratorInfo::~IGeneratorInfo() {} + IGeneratorsForTest::~IGeneratorsForTest() {} + TestSpec::Pattern::~Pattern() {} + TestSpec::NamePattern::~NamePattern() {} + TestSpec::TagPattern::~TagPattern() {} + TestSpec::ExcludedPattern::~ExcludedPattern() {} + + Matchers::Impl::StdString::Equals::~Equals() {} + Matchers::Impl::StdString::Contains::~Contains() {} + Matchers::Impl::StdString::StartsWith::~StartsWith() {} + Matchers::Impl::StdString::EndsWith::~EndsWith() {} + + void Config::dummy() {} + + INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( "xml", XmlReporter ) +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif + +#ifdef CATCH_CONFIG_MAIN +// #included from: internal/catch_default_main.hpp +#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED + +#ifndef __OBJC__ + +// Standard C/C++ main entry point +int main (int argc, char * const argv[]) { + return Catch::Session().run( argc, argv ); +} + +#else // __OBJC__ + +// Objective-C entry point +int main (int argc, char * const argv[]) { +#if !CATCH_ARC_ENABLED + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +#endif + + Catch::registerTestMethods(); + int result = Catch::Session().run( argc, (char* const*)argv ); + +#if !CATCH_ARC_ENABLED + [pool drain]; +#endif + + return result; +} + +#endif // __OBJC__ + +#endif + +#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED +# undef CLARA_CONFIG_MAIN +#endif + +////// + +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" ) + +#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) +#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) + +#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) +#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" ) +#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) +#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) +#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) + +#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) +#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) + +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) + +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) +#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) +#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) +#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) + #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) + #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) +#else + #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) + #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) + #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) + #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) + #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) + #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) +#endif +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) + +#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) +#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) + +#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) + +// "BDD-style" convenience wrappers +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +#else +#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) +#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) +#endif +#define CATCH_GIVEN( desc ) CATCH_SECTION( "Given: " desc, "" ) +#define CATCH_WHEN( desc ) CATCH_SECTION( " When: " desc, "" ) +#define CATCH_AND_WHEN( desc ) CATCH_SECTION( " And: " desc, "" ) +#define CATCH_THEN( desc ) CATCH_SECTION( " Then: " desc, "" ) +#define CATCH_AND_THEN( desc ) CATCH_SECTION( " And: " desc, "" ) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" ) + +#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "REQUIRE_THROWS" ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) +#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) + +#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) +#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" ) +#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) +#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) +#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) + +#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) +#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) + +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) + +#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) +#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) +#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) +#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) +#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) + #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) + #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) +#else + #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) + #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) + #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) + #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) + #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) + #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) +#endif +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) + +#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) +#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) + +#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) + +// "BDD-style" convenience wrappers +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +#else +#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) +#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) +#endif +#define GIVEN( desc ) SECTION( " Given: " desc, "" ) +#define WHEN( desc ) SECTION( " When: " desc, "" ) +#define AND_WHEN( desc ) SECTION( "And when: " desc, "" ) +#define THEN( desc ) SECTION( " Then: " desc, "" ) +#define AND_THEN( desc ) SECTION( " And: " desc, "" ) + +using Catch::Detail::Approx; + +// #included from: internal/catch_reenable_warnings.h + +#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic pop +#elif defined __GNUC__ +#pragma GCC diagnostic pop +#endif + +#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + diff --git a/third_party/glfw/bin/osx/libglfw3.a b/third_party/glfw/bin/osx/libglfw3.a new file mode 100644 index 0000000000000000000000000000000000000000..6df252cf08f3c244b520c39e3a111c2883447545 GIT binary patch literal 140640 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 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would + * be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + *************************************************************************/ + +#ifndef _glfw3_h_ +#define _glfw3_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************************************* + * Doxygen documentation + *************************************************************************/ + +/*! @defgroup clipboard Clipboard support + */ +/*! @defgroup context Context handling + */ +/*! @defgroup error Error handling + */ +/*! @defgroup init Initialization and version information + */ +/*! @defgroup input Input handling + */ +/*! @defgroup monitor Monitor handling + * + * This is the reference documentation for monitor related functions and types. + * For more information, see the @ref monitor. + */ +/*! @defgroup time Time input + */ +/*! @defgroup window Window handling + * + * This is the reference documentation for window related functions and types, + * including creation, deletion and event polling. For more information, see + * the @ref window. + */ + + +/************************************************************************* + * Global definitions + *************************************************************************/ + +/* ------------------- BEGIN SYSTEM/COMPILER SPECIFIC -------------------- */ + +/* Please report any problems that you find with your compiler, which may + * be solved in this section! There are several compilers that I have not + * been able to test this file with yet. + * + * First: If we are we on Windows, we want a single define for it (_WIN32) + * (Note: For Cygwin the compiler flag -mwin32 should be used, but to + * make sure that things run smoothly for Cygwin users, we add __CYGWIN__ + * to the list of "valid Win32 identifiers", which removes the need for + * -mwin32) + */ +#if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__)) + #define _WIN32 +#endif /* _WIN32 */ + +/* In order for extension support to be portable, we need to define an + * OpenGL function call method. We use the keyword APIENTRY, which is + * defined for Win32. (Note: Windows also needs this for ) + */ +#ifndef APIENTRY + #ifdef _WIN32 + #define APIENTRY __stdcall + #else + #define APIENTRY + #endif +#endif /* APIENTRY */ + +/* The following three defines are here solely to make some Windows-based + * files happy. Theoretically we could include , but + * it has the major drawback of severely polluting our namespace. + */ + +/* Under Windows, we need WINGDIAPI defined */ +#if !defined(WINGDIAPI) && defined(_WIN32) + #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__POCC__) + /* Microsoft Visual C++, Borland C++ Builder and Pelles C */ + #define WINGDIAPI __declspec(dllimport) + #elif defined(__LCC__) + /* LCC-Win32 */ + #define WINGDIAPI __stdcall + #else + /* Others (e.g. MinGW, Cygwin) */ + #define WINGDIAPI extern + #endif + #define GLFW_WINGDIAPI_DEFINED +#endif /* WINGDIAPI */ + +/* Some files also need CALLBACK defined */ +#if !defined(CALLBACK) && defined(_WIN32) + #if defined(_MSC_VER) + /* Microsoft Visual C++ */ + #if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS) + #define CALLBACK __stdcall + #else + #define CALLBACK + #endif + #else + /* Other Windows compilers */ + #define CALLBACK __stdcall + #endif + #define GLFW_CALLBACK_DEFINED +#endif /* CALLBACK */ + +/* Most GL/glu.h variants on Windows need wchar_t + * OpenGL/gl.h blocks the definition of ptrdiff_t by glext.h on OS X */ +#if !defined(GLFW_INCLUDE_NONE) + #include +#endif + +/* Include the chosen client API headers. + */ +#if defined(__APPLE_CC__) + #if defined(GLFW_INCLUDE_GLCOREARB) + #include + #elif !defined(GLFW_INCLUDE_NONE) + #define GL_GLEXT_LEGACY + #include + #endif + #if defined(GLFW_INCLUDE_GLU) + #include + #endif +#else + #if defined(GLFW_INCLUDE_GLCOREARB) + #include + #elif defined(GLFW_INCLUDE_ES1) + #include + #elif defined(GLFW_INCLUDE_ES2) + #include + #elif defined(GLFW_INCLUDE_ES3) + #include + #elif defined(GLFW_INCLUDE_ES31) + #include + #elif !defined(GLFW_INCLUDE_NONE) + #include + #endif + #if defined(GLFW_INCLUDE_GLU) + #include + #endif +#endif + +#if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL) + /* GLFW_DLL must be defined by applications that are linking against the DLL + * version of the GLFW library. _GLFW_BUILD_DLL is defined by the GLFW + * configuration header when compiling the DLL version of the library. + */ + #error "You must not have both GLFW_DLL and _GLFW_BUILD_DLL defined" +#endif + +#if defined(_WIN32) && defined(_GLFW_BUILD_DLL) + + /* We are building a Win32 DLL */ + #define GLFWAPI __declspec(dllexport) + +#elif defined(_WIN32) && defined(GLFW_DLL) + + /* We are calling a Win32 DLL */ + #if defined(__LCC__) + #define GLFWAPI extern + #else + #define GLFWAPI __declspec(dllimport) + #endif + +#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL) + + #define GLFWAPI __attribute__((visibility("default"))) + +#else + + /* We are either building/calling a static lib or we are non-win32 */ + #define GLFWAPI + +#endif + +/* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ + + +/************************************************************************* + * GLFW API tokens + *************************************************************************/ + +/*! @name GLFW version macros + * @{ */ +/*! @brief The major version number of the GLFW library. + * + * This is incremented when the API is changed in non-compatible ways. + * @ingroup init + */ +#define GLFW_VERSION_MAJOR 3 +/*! @brief The minor version number of the GLFW library. + * + * This is incremented when features are added to the API but it remains + * backward-compatible. + * @ingroup init + */ +#define GLFW_VERSION_MINOR 1 +/*! @brief The revision number of the GLFW library. + * + * This is incremented when a bug fix release is made that does not contain any + * API changes. + * @ingroup init + */ +#define GLFW_VERSION_REVISION 0 +/*! @} */ + +/*! @name Key and button actions + * @{ */ +/*! @brief The key or button was released. + * @ingroup input + */ +#define GLFW_RELEASE 0 +/*! @brief The key or button was pressed. + * @ingroup input + */ +#define GLFW_PRESS 1 +/*! @brief The key was held down until it repeated. + * @ingroup input + */ +#define GLFW_REPEAT 2 +/*! @} */ + +/*! @defgroup keys Keyboard keys + * + * These key codes are inspired by the *USB HID Usage Tables v1.12* (p. 53-60), + * but re-arranged to map to 7-bit ASCII for printable keys (function keys are + * put in the 256+ range). + * + * The naming of the key codes follow these rules: + * - The US keyboard layout is used + * - Names of printable alpha-numeric characters are used (e.g. "A", "R", + * "3", etc.) + * - For non-alphanumeric characters, Unicode:ish names are used (e.g. + * "COMMA", "LEFT_SQUARE_BRACKET", etc.). Note that some names do not + * correspond to the Unicode standard (usually for brevity) + * - Keys that lack a clear US mapping are named "WORLD_x" + * - For non-printable keys, custom names are used (e.g. "F4", + * "BACKSPACE", etc.) + * + * @ingroup input + * @{ + */ + +/* The unknown key */ +#define GLFW_KEY_UNKNOWN -1 + +/* Printable keys */ +#define GLFW_KEY_SPACE 32 +#define GLFW_KEY_APOSTROPHE 39 /* ' */ +#define GLFW_KEY_COMMA 44 /* , */ +#define GLFW_KEY_MINUS 45 /* - */ +#define GLFW_KEY_PERIOD 46 /* . */ +#define GLFW_KEY_SLASH 47 /* / */ +#define GLFW_KEY_0 48 +#define GLFW_KEY_1 49 +#define GLFW_KEY_2 50 +#define GLFW_KEY_3 51 +#define GLFW_KEY_4 52 +#define GLFW_KEY_5 53 +#define GLFW_KEY_6 54 +#define GLFW_KEY_7 55 +#define GLFW_KEY_8 56 +#define GLFW_KEY_9 57 +#define GLFW_KEY_SEMICOLON 59 /* ; */ +#define GLFW_KEY_EQUAL 61 /* = */ +#define GLFW_KEY_A 65 +#define GLFW_KEY_B 66 +#define GLFW_KEY_C 67 +#define GLFW_KEY_D 68 +#define GLFW_KEY_E 69 +#define GLFW_KEY_F 70 +#define GLFW_KEY_G 71 +#define GLFW_KEY_H 72 +#define GLFW_KEY_I 73 +#define GLFW_KEY_J 74 +#define GLFW_KEY_K 75 +#define GLFW_KEY_L 76 +#define GLFW_KEY_M 77 +#define GLFW_KEY_N 78 +#define GLFW_KEY_O 79 +#define GLFW_KEY_P 80 +#define GLFW_KEY_Q 81 +#define GLFW_KEY_R 82 +#define GLFW_KEY_S 83 +#define GLFW_KEY_T 84 +#define GLFW_KEY_U 85 +#define GLFW_KEY_V 86 +#define GLFW_KEY_W 87 +#define GLFW_KEY_X 88 +#define GLFW_KEY_Y 89 +#define GLFW_KEY_Z 90 +#define GLFW_KEY_LEFT_BRACKET 91 /* [ */ +#define GLFW_KEY_BACKSLASH 92 /* \ */ +#define GLFW_KEY_RIGHT_BRACKET 93 /* ] */ +#define GLFW_KEY_GRAVE_ACCENT 96 /* ` */ +#define GLFW_KEY_WORLD_1 161 /* non-US #1 */ +#define GLFW_KEY_WORLD_2 162 /* non-US #2 */ + +/* Function keys */ +#define GLFW_KEY_ESCAPE 256 +#define GLFW_KEY_ENTER 257 +#define GLFW_KEY_TAB 258 +#define GLFW_KEY_BACKSPACE 259 +#define GLFW_KEY_INSERT 260 +#define GLFW_KEY_DELETE 261 +#define GLFW_KEY_RIGHT 262 +#define GLFW_KEY_LEFT 263 +#define GLFW_KEY_DOWN 264 +#define GLFW_KEY_UP 265 +#define GLFW_KEY_PAGE_UP 266 +#define GLFW_KEY_PAGE_DOWN 267 +#define GLFW_KEY_HOME 268 +#define GLFW_KEY_END 269 +#define GLFW_KEY_CAPS_LOCK 280 +#define GLFW_KEY_SCROLL_LOCK 281 +#define GLFW_KEY_NUM_LOCK 282 +#define GLFW_KEY_PRINT_SCREEN 283 +#define GLFW_KEY_PAUSE 284 +#define GLFW_KEY_F1 290 +#define GLFW_KEY_F2 291 +#define GLFW_KEY_F3 292 +#define GLFW_KEY_F4 293 +#define GLFW_KEY_F5 294 +#define GLFW_KEY_F6 295 +#define GLFW_KEY_F7 296 +#define GLFW_KEY_F8 297 +#define GLFW_KEY_F9 298 +#define GLFW_KEY_F10 299 +#define GLFW_KEY_F11 300 +#define GLFW_KEY_F12 301 +#define GLFW_KEY_F13 302 +#define GLFW_KEY_F14 303 +#define GLFW_KEY_F15 304 +#define GLFW_KEY_F16 305 +#define GLFW_KEY_F17 306 +#define GLFW_KEY_F18 307 +#define GLFW_KEY_F19 308 +#define GLFW_KEY_F20 309 +#define GLFW_KEY_F21 310 +#define GLFW_KEY_F22 311 +#define GLFW_KEY_F23 312 +#define GLFW_KEY_F24 313 +#define GLFW_KEY_F25 314 +#define GLFW_KEY_KP_0 320 +#define GLFW_KEY_KP_1 321 +#define GLFW_KEY_KP_2 322 +#define GLFW_KEY_KP_3 323 +#define GLFW_KEY_KP_4 324 +#define GLFW_KEY_KP_5 325 +#define GLFW_KEY_KP_6 326 +#define GLFW_KEY_KP_7 327 +#define GLFW_KEY_KP_8 328 +#define GLFW_KEY_KP_9 329 +#define GLFW_KEY_KP_DECIMAL 330 +#define GLFW_KEY_KP_DIVIDE 331 +#define GLFW_KEY_KP_MULTIPLY 332 +#define GLFW_KEY_KP_SUBTRACT 333 +#define GLFW_KEY_KP_ADD 334 +#define GLFW_KEY_KP_ENTER 335 +#define GLFW_KEY_KP_EQUAL 336 +#define GLFW_KEY_LEFT_SHIFT 340 +#define GLFW_KEY_LEFT_CONTROL 341 +#define GLFW_KEY_LEFT_ALT 342 +#define GLFW_KEY_LEFT_SUPER 343 +#define GLFW_KEY_RIGHT_SHIFT 344 +#define GLFW_KEY_RIGHT_CONTROL 345 +#define GLFW_KEY_RIGHT_ALT 346 +#define GLFW_KEY_RIGHT_SUPER 347 +#define GLFW_KEY_MENU 348 +#define GLFW_KEY_LAST GLFW_KEY_MENU + +/*! @} */ + +/*! @defgroup mods Modifier key flags + * @ingroup input + * @{ */ + +/*! @brief If this bit is set one or more Shift keys were held down. + */ +#define GLFW_MOD_SHIFT 0x0001 +/*! @brief If this bit is set one or more Control keys were held down. + */ +#define GLFW_MOD_CONTROL 0x0002 +/*! @brief If this bit is set one or more Alt keys were held down. + */ +#define GLFW_MOD_ALT 0x0004 +/*! @brief If this bit is set one or more Super keys were held down. + */ +#define GLFW_MOD_SUPER 0x0008 + +/*! @} */ + +/*! @defgroup buttons Mouse buttons + * @ingroup input + * @{ */ +#define GLFW_MOUSE_BUTTON_1 0 +#define GLFW_MOUSE_BUTTON_2 1 +#define GLFW_MOUSE_BUTTON_3 2 +#define GLFW_MOUSE_BUTTON_4 3 +#define GLFW_MOUSE_BUTTON_5 4 +#define GLFW_MOUSE_BUTTON_6 5 +#define GLFW_MOUSE_BUTTON_7 6 +#define GLFW_MOUSE_BUTTON_8 7 +#define GLFW_MOUSE_BUTTON_LAST GLFW_MOUSE_BUTTON_8 +#define GLFW_MOUSE_BUTTON_LEFT GLFW_MOUSE_BUTTON_1 +#define GLFW_MOUSE_BUTTON_RIGHT GLFW_MOUSE_BUTTON_2 +#define GLFW_MOUSE_BUTTON_MIDDLE GLFW_MOUSE_BUTTON_3 +/*! @} */ + +/*! @defgroup joysticks Joysticks + * @ingroup input + * @{ */ +#define GLFW_JOYSTICK_1 0 +#define GLFW_JOYSTICK_2 1 +#define GLFW_JOYSTICK_3 2 +#define GLFW_JOYSTICK_4 3 +#define GLFW_JOYSTICK_5 4 +#define GLFW_JOYSTICK_6 5 +#define GLFW_JOYSTICK_7 6 +#define GLFW_JOYSTICK_8 7 +#define GLFW_JOYSTICK_9 8 +#define GLFW_JOYSTICK_10 9 +#define GLFW_JOYSTICK_11 10 +#define GLFW_JOYSTICK_12 11 +#define GLFW_JOYSTICK_13 12 +#define GLFW_JOYSTICK_14 13 +#define GLFW_JOYSTICK_15 14 +#define GLFW_JOYSTICK_16 15 +#define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16 +/*! @} */ + +/*! @defgroup errors Error codes + * @ingroup error + * @{ */ +/*! @brief GLFW has not been initialized. + */ +#define GLFW_NOT_INITIALIZED 0x00010001 +/*! @brief No context is current for this thread. + */ +#define GLFW_NO_CURRENT_CONTEXT 0x00010002 +/*! @brief One of the enum parameters for the function was given an invalid + * enum. + */ +#define GLFW_INVALID_ENUM 0x00010003 +/*! @brief One of the parameters for the function was given an invalid value. + */ +#define GLFW_INVALID_VALUE 0x00010004 +/*! @brief A memory allocation failed. + */ +#define GLFW_OUT_OF_MEMORY 0x00010005 +/*! @brief GLFW could not find support for the requested client API on the + * system. + */ +#define GLFW_API_UNAVAILABLE 0x00010006 +/*! @brief The requested client API version is not available. + */ +#define GLFW_VERSION_UNAVAILABLE 0x00010007 +/*! @brief A platform-specific error occurred that does not match any of the + * more specific categories. + */ +#define GLFW_PLATFORM_ERROR 0x00010008 +/*! @brief The clipboard did not contain data in the requested format. + */ +#define GLFW_FORMAT_UNAVAILABLE 0x00010009 +/*! @} */ + +#define GLFW_FOCUSED 0x00020001 +#define GLFW_ICONIFIED 0x00020002 +#define GLFW_RESIZABLE 0x00020003 +#define GLFW_VISIBLE 0x00020004 +#define GLFW_DECORATED 0x00020005 +#define GLFW_AUTO_ICONIFY 0x00020006 +#define GLFW_FLOATING 0x00020007 + +#define GLFW_RED_BITS 0x00021001 +#define GLFW_GREEN_BITS 0x00021002 +#define GLFW_BLUE_BITS 0x00021003 +#define GLFW_ALPHA_BITS 0x00021004 +#define GLFW_DEPTH_BITS 0x00021005 +#define GLFW_STENCIL_BITS 0x00021006 +#define GLFW_ACCUM_RED_BITS 0x00021007 +#define GLFW_ACCUM_GREEN_BITS 0x00021008 +#define GLFW_ACCUM_BLUE_BITS 0x00021009 +#define GLFW_ACCUM_ALPHA_BITS 0x0002100A +#define GLFW_AUX_BUFFERS 0x0002100B +#define GLFW_STEREO 0x0002100C +#define GLFW_SAMPLES 0x0002100D +#define GLFW_SRGB_CAPABLE 0x0002100E +#define GLFW_REFRESH_RATE 0x0002100F +#define GLFW_DOUBLEBUFFER 0x00021010 + +#define GLFW_CLIENT_API 0x00022001 +#define GLFW_CONTEXT_VERSION_MAJOR 0x00022002 +#define GLFW_CONTEXT_VERSION_MINOR 0x00022003 +#define GLFW_CONTEXT_REVISION 0x00022004 +#define GLFW_CONTEXT_ROBUSTNESS 0x00022005 +#define GLFW_OPENGL_FORWARD_COMPAT 0x00022006 +#define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 +#define GLFW_OPENGL_PROFILE 0x00022008 + +#define GLFW_OPENGL_API 0x00030001 +#define GLFW_OPENGL_ES_API 0x00030002 + +#define GLFW_NO_ROBUSTNESS 0 +#define GLFW_NO_RESET_NOTIFICATION 0x00031001 +#define GLFW_LOSE_CONTEXT_ON_RESET 0x00031002 + +#define GLFW_OPENGL_ANY_PROFILE 0 +#define GLFW_OPENGL_CORE_PROFILE 0x00032001 +#define GLFW_OPENGL_COMPAT_PROFILE 0x00032002 + +#define GLFW_CURSOR 0x00033001 +#define GLFW_STICKY_KEYS 0x00033002 +#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 + +#define GLFW_CURSOR_NORMAL 0x00034001 +#define GLFW_CURSOR_HIDDEN 0x00034002 +#define GLFW_CURSOR_DISABLED 0x00034003 + +#define GLFW_CONNECTED 0x00040001 +#define GLFW_DISCONNECTED 0x00040002 + +#define GLFW_DONT_CARE -1 + + +/************************************************************************* + * GLFW API types + *************************************************************************/ + +/*! @brief Client API function pointer type. + * + * Generic function pointer used for returning client API function pointers + * without forcing a cast from a regular pointer. + * + * @ingroup context + */ +typedef void (*GLFWglproc)(void); + +/*! @brief Opaque monitor object. + * + * Opaque monitor object. + * + * @ingroup monitor + */ +typedef struct GLFWmonitor GLFWmonitor; + +/*! @brief Opaque window object. + * + * Opaque window object. + * + * @ingroup window + */ +typedef struct GLFWwindow GLFWwindow; + +/*! @brief Opaque cursor object. + * + * Opaque cursor object. + * + * @ingroup cursor + */ +typedef struct GLFWcursor GLFWcursor; + +/*! @brief The function signature for error callbacks. + * + * This is the function signature for error callback functions. + * + * @param[in] error An [error code](@ref errors). + * @param[in] description A UTF-8 encoded string describing the error. + * + * @sa glfwSetErrorCallback + * + * @ingroup error + */ +typedef void (* GLFWerrorfun)(int,const char*); + +/*! @brief The function signature for window position callbacks. + * + * This is the function signature for window position callback functions. + * + * @param[in] window The window that was moved. + * @param[in] xpos The new x-coordinate, in screen coordinates, of the + * upper-left corner of the client area of the window. + * @param[in] ypos The new y-coordinate, in screen coordinates, of the + * upper-left corner of the client area of the window. + * + * @sa glfwSetWindowPosCallback + * + * @ingroup window + */ +typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); + +/*! @brief The function signature for window resize callbacks. + * + * This is the function signature for window size callback functions. + * + * @param[in] window The window that was resized. + * @param[in] width The new width, in screen coordinates, of the window. + * @param[in] height The new height, in screen coordinates, of the window. + * + * @sa glfwSetWindowSizeCallback + * + * @ingroup window + */ +typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); + +/*! @brief The function signature for window close callbacks. + * + * This is the function signature for window close callback functions. + * + * @param[in] window The window that the user attempted to close. + * + * @sa glfwSetWindowCloseCallback + * + * @ingroup window + */ +typedef void (* GLFWwindowclosefun)(GLFWwindow*); + +/*! @brief The function signature for window content refresh callbacks. + * + * This is the function signature for window refresh callback functions. + * + * @param[in] window The window whose content needs to be refreshed. + * + * @sa glfwSetWindowRefreshCallback + * + * @ingroup window + */ +typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); + +/*! @brief The function signature for window focus/defocus callbacks. + * + * This is the function signature for window focus callback functions. + * + * @param[in] window The window that was focused or defocused. + * @param[in] focused `GL_TRUE` if the window was focused, or `GL_FALSE` if + * it was defocused. + * + * @sa glfwSetWindowFocusCallback + * + * @ingroup window + */ +typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); + +/*! @brief The function signature for window iconify/restore callbacks. + * + * This is the function signature for window iconify/restore callback + * functions. + * + * @param[in] window The window that was iconified or restored. + * @param[in] iconified `GL_TRUE` if the window was iconified, or `GL_FALSE` + * if it was restored. + * + * @sa glfwSetWindowIconifyCallback + * + * @ingroup window + */ +typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); + +/*! @brief The function signature for framebuffer resize callbacks. + * + * This is the function signature for framebuffer resize callback + * functions. + * + * @param[in] window The window whose framebuffer was resized. + * @param[in] width The new width, in pixels, of the framebuffer. + * @param[in] height The new height, in pixels, of the framebuffer. + * + * @sa glfwSetFramebufferSizeCallback + * + * @ingroup window + */ +typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); + +/*! @brief The function signature for mouse button callbacks. + * + * This is the function signature for mouse button callback functions. + * + * @param[in] window The window that received the event. + * @param[in] button The [mouse button](@ref buttons) that was pressed or + * released. + * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa glfwSetMouseButtonCallback + * + * @ingroup input + */ +typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); + +/*! @brief The function signature for cursor position callbacks. + * + * This is the function signature for cursor position callback functions. + * + * @param[in] window The window that received the event. + * @param[in] xpos The new x-coordinate, in screen coordinates, of the cursor. + * @param[in] ypos The new y-coordinate, in screen coordinates, of the cursor. + * + * @sa glfwSetCursorPosCallback + * + * @ingroup input + */ +typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); + +/*! @brief The function signature for cursor enter/leave callbacks. + * + * This is the function signature for cursor enter/leave callback functions. + * + * @param[in] window The window that received the event. + * @param[in] entered `GL_TRUE` if the cursor entered the window's client + * area, or `GL_FALSE` if it left it. + * + * @sa glfwSetCursorEnterCallback + * + * @ingroup input + */ +typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); + +/*! @brief The function signature for scroll callbacks. + * + * This is the function signature for scroll callback functions. + * + * @param[in] window The window that received the event. + * @param[in] xoffset The scroll offset along the x-axis. + * @param[in] yoffset The scroll offset along the y-axis. + * + * @sa glfwSetScrollCallback + * + * @ingroup input + */ +typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); + +/*! @brief The function signature for keyboard key callbacks. + * + * This is the function signature for keyboard key callback functions. + * + * @param[in] window The window that received the event. + * @param[in] key The [keyboard key](@ref keys) that was pressed or released. + * @param[in] scancode The system-specific scancode of the key. + * @param[in] action @ref GLFW_PRESS, @ref GLFW_RELEASE or @ref GLFW_REPEAT. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa glfwSetKeyCallback + * + * @ingroup input + */ +typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); + +/*! @brief The function signature for Unicode character callbacks. + * + * This is the function signature for Unicode character callback functions. + * + * @param[in] window The window that received the event. + * @param[in] codepoint The Unicode code point of the character. + * + * @sa glfwSetCharCallback + * + * @ingroup input + */ +typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); + +/*! @brief The function signature for Unicode character with modifiers + * callbacks. + * + * This is the function signature for Unicode character with modifiers callback + * functions. It is called for each input character, regardless of what + * modifier keys are held down. + * + * @param[in] window The window that received the event. + * @param[in] codepoint The Unicode code point of the character. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa glfwSetCharModsCallback + * + * @ingroup input + */ +typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); + +/*! @brief The function signature for file drop callbacks. + * + * This is the function signature for file drop callbacks. + * + * @param[in] window The window that received the event. + * @param[in] count The number of dropped files. + * @param[in] names The UTF-8 encoded path names of the dropped files. + * + * @sa glfwSetDropCallback + * + * @ingroup input + */ +typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); + +/*! @brief The function signature for monitor configuration callbacks. + * + * This is the function signature for monitor configuration callback functions. + * + * @param[in] monitor The monitor that was connected or disconnected. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. + * + * @sa glfwSetMonitorCallback + * + * @ingroup monitor + */ +typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); + +/*! @brief Video mode type. + * + * This describes a single video mode. + * + * @ingroup monitor + */ +typedef struct GLFWvidmode +{ + /*! The width, in screen coordinates, of the video mode. + */ + int width; + /*! The height, in screen coordinates, of the video mode. + */ + int height; + /*! The bit depth of the red channel of the video mode. + */ + int redBits; + /*! The bit depth of the green channel of the video mode. + */ + int greenBits; + /*! The bit depth of the blue channel of the video mode. + */ + int blueBits; + /*! The refresh rate, in Hz, of the video mode. + */ + int refreshRate; +} GLFWvidmode; + +/*! @brief Gamma ramp. + * + * This describes the gamma ramp for a monitor. + * + * @sa glfwGetGammaRamp glfwSetGammaRamp + * + * @ingroup monitor + */ +typedef struct GLFWgammaramp +{ + /*! An array of value describing the response of the red channel. + */ + unsigned short* red; + /*! An array of value describing the response of the green channel. + */ + unsigned short* green; + /*! An array of value describing the response of the blue channel. + */ + unsigned short* blue; + /*! The number of elements in each array. + */ + unsigned int size; +} GLFWgammaramp; + +/*! @brief Image data. + * + * @ingroup window + */ +typedef struct GLFWimage +{ + /*! The width, in pixels, of this image. + */ + int width; + /*! The height, in pixels, of this image. + */ + int height; + /*! The pixel data of this image, arranged left-to-right, top-to-bottom. + */ + unsigned char* pixels; +} GLFWimage; + + +/************************************************************************* + * GLFW API functions + *************************************************************************/ + +/*! @brief Initializes the GLFW library. + * + * This function initializes the GLFW library. Before most GLFW functions can + * be used, GLFW must be initialized, and before a program terminates GLFW + * should be terminated in order to free any resources allocated during or + * after initialization. + * + * If this function fails, it calls @ref glfwTerminate before returning. If it + * succeeds, you should call @ref glfwTerminate before the program exits. + * + * Additional calls to this function after successful initialization but before + * termination will return `GL_TRUE` immediately. + * + * @return `GL_TRUE` if successful, or `GL_FALSE` if an error occurred. Errors + * are reported to the [error callback](@ref intro_error). + * + * @par New in GLFW 3 + * This function no longer registers @ref glfwTerminate with `atexit`. + * + * @note This function may only be called from the main thread. + * + * @note **OS X:** This function will change the current directory of the + * application to the `Contents/Resources` subdirectory of the application's + * bundle, if present. + * + * @sa glfwTerminate + * + * @ingroup init + */ +GLFWAPI int glfwInit(void); + +/*! @brief Terminates the GLFW library. + * + * This function destroys all remaining windows, frees any allocated resources + * and sets the library to an uninitialized state. Once this is called, you + * must again call @ref glfwInit successfully before you will be able to use + * most GLFW functions. + * + * If GLFW has been successfully initialized, this function should be called + * before the program exits. If initialization fails, there is no need to call + * this function, as it is called by @ref glfwInit before it returns failure. + * + * @remarks This function may be called before @ref glfwInit. + * + * @note This function may only be called from the main thread. + * + * @warning No window's context may be current on another thread when this + * function is called. + * + * @sa glfwInit + * + * @ingroup init + */ +GLFWAPI void glfwTerminate(void); + +/*! @brief Retrieves the version of the GLFW library. + * + * This function retrieves the major, minor and revision numbers of the GLFW + * library. It is intended for when you are using GLFW as a shared library and + * want to ensure that you are using the minimum required version. + * + * @param[out] major Where to store the major version number, or `NULL`. + * @param[out] minor Where to store the minor version number, or `NULL`. + * @param[out] rev Where to store the revision number, or `NULL`. + * + * @remarks This function always succeeds. + * + * @remarks This function may be called before @ref glfwInit. + * + * @remarks This function may be called from any thread. + * + * @sa glfwGetVersionString + * + * @ingroup init + */ +GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); + +/*! @brief Returns a string describing the compile-time configuration. + * + * This function returns the compile-time generated + * [version string](@ref intro_version_string) of the GLFW library binary. It + * describes the version, platform, compiler and any platform-specific + * compile-time options. + * + * @return The GLFW version string. + * + * @remarks This function always succeeds. + * + * @remarks This function may be called before @ref glfwInit. + * + * @remarks This function may be called from any thread. + * + * @sa glfwGetVersion + * + * @ingroup init + */ +GLFWAPI const char* glfwGetVersionString(void); + +/*! @brief Sets the error callback. + * + * This function sets the error callback, which is called with an error code + * and a human-readable description each time a GLFW error occurs. + * + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set. + * + * @remarks This function always succeeds. + * + * @remarks This function may be called before @ref glfwInit. + * + * @note This function may only be called from the main thread. + * + * @note The error callback is called by the thread where the error occurred. + * If you are using GLFW from multiple threads, your error callback needs to be + * written accordingly. + * + * @note Because the description string provided to the callback may have been + * generated specifically for that error, it is not guaranteed to be valid + * after the callback has returned. If you wish to use it after that, you need + * to make your own copy of it before returning. + * + * @ingroup error + */ +GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun); + +/*! @brief Returns the currently connected monitors. + * + * This function returns an array of handles for all currently connected + * monitors. + * + * @param[out] count Where to store the size of the returned array. This is + * set to zero if an error occurred. + * @return An array of monitor handles, or `NULL` if an error occurred. Errors + * are reported to the [error callback](@ref intro_error). + * + * @note This function may only be called from the main thread. + * + * @note The returned array is allocated and freed by GLFW. You should not + * free it yourself. + * + * @note The returned array is valid only until the monitor configuration + * changes. See @ref glfwSetMonitorCallback to receive notifications of + * configuration changes. + * + * @sa glfwGetPrimaryMonitor + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitor** glfwGetMonitors(int* count); + +/*! @brief Returns the primary monitor. + * + * This function returns the primary monitor. This is usually the monitor + * where elements like the Windows task bar or the OS X menu bar is located. + * + * @return The primary monitor, or `NULL` if an error occurred. Errors are + * reported to the [error callback](@ref intro_error). + * + * @note This function may only be called from the main thread. + * + * @sa glfwGetMonitors + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void); + +/*! @brief Returns the position of the monitor's viewport on the virtual screen. + * + * This function returns the position, in screen coordinates, of the upper-left + * corner of the specified monitor. + * + * @param[in] monitor The monitor to query. + * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. + * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. + * + * @note This function may only be called from the main thread. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); + +/*! @brief Returns the physical size of the monitor. + * + * This function returns the size, in millimetres, of the display area of the + * specified monitor. + * + * @param[in] monitor The monitor to query. + * @param[out] width Where to store the width, in mm, of the monitor's display + * area, or `NULL`. + * @param[out] height Where to store the height, in mm, of the monitor's + * display area, or `NULL`. + * + * @note This function may only be called from the main thread. + * + * @note Some operating systems do not provide accurate information, either + * because the monitor's EDID data is incorrect, or because the driver does not + * report it accurately. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* width, int* height); + +/*! @brief Returns the name of the specified monitor. + * + * This function returns a human-readable name, encoded as UTF-8, of the + * specified monitor. + * + * @param[in] monitor The monitor to query. + * @return The UTF-8 encoded name of the monitor, or `NULL` if an error + * occurred. Errors are reported to the [error callback](@ref intro_error). + * + * @note This function may only be called from the main thread. + * + * @note The returned string is allocated and freed by GLFW. You should not + * free it yourself. + * + * @ingroup monitor + */ +GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor); + +/*! @brief Sets the monitor configuration callback. + * + * This function sets the monitor configuration callback, or removes the + * currently set callback. This is called when a monitor is connected to or + * disconnected from the system. + * + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @note This function may only be called from the main thread. + * + * @bug **X11:** This callback is not yet called on monitor configuration + * changes. + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun); + +/*! @brief Returns the available video modes for the specified monitor. + * + * This function returns an array of all video modes supported by the specified + * monitor. The returned array is sorted in ascending order, first by color + * bit depth (the sum of all channel depths) and then by resolution area (the + * product of width and height). + * + * @param[in] monitor The monitor to query. + * @param[out] count Where to store the number of video modes in the returned + * array. This is set to zero if an error occurred. + * @return An array of video modes, or `NULL` if an error occurred. Errors are + * reported to the [error callback](@ref intro_error). + * + * @note This function may only be called from the main thread. + * + * @note The returned array is allocated and freed by GLFW. You should not + * free it yourself. + * + * @note The returned array is valid only until this function is called again + * for the specified monitor. + * + * @sa glfwGetVideoMode + * + * @ingroup monitor + */ +GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count); + +/*! @brief Returns the current mode of the specified monitor. + * + * This function returns the current video mode of the specified monitor. If + * you are using a full screen window, the return value will therefore depend + * on whether it is focused. + * + * @param[in] monitor The monitor to query. + * @return The current mode of the monitor, or `NULL` if an error occurred. + * Errors are reported to the [error callback](@ref intro_error). + * + * @note This function may only be called from the main thread. + * + * @note The returned struct is allocated and freed by GLFW. You should not + * free it yourself. + * + * @sa glfwGetVideoModes + * + * @ingroup monitor + */ +GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); + +/*! @brief Generates a gamma ramp and sets it for the specified monitor. + * + * This function generates a 256-element gamma ramp from the specified exponent + * and then calls @ref glfwSetGammaRamp with it. + * + * @param[in] monitor The monitor whose gamma ramp to set. + * @param[in] gamma The desired exponent. + * + * @remark You cannot generate sRGB gamma using this function, because although + * it is approximately 2.2 it cannot be accurately expressed as a single + * numerical value. + * + * @note This function may only be called from the main thread. + * + * @ingroup monitor + */ +GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); + +/*! @brief Retrieves the current gamma ramp for the specified monitor. + * + * This function retrieves the current gamma ramp of the specified monitor. + * + * @param[in] monitor The monitor to query. + * @return The current gamma ramp, or `NULL` if an error occurred. Errors are + * reported to the [error callback](@ref intro_error). + * + * @note This function may only be called from the main thread. + * + * @note The value arrays of the returned ramp are allocated and freed by GLFW. + * You should not free them yourself. + * + * @ingroup monitor + */ +GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); + +/*! @brief Sets the current gamma ramp for the specified monitor. + * + * This function sets the current gamma ramp for the specified monitor. + * + * @param[in] monitor The monitor whose gamma ramp to set. + * @param[in] ramp The gamma ramp to use. + * + * @note This function may only be called from the main thread. + * + * @note Gamma ramp sizes other than 256 are not supported by all hardware. + * + * @ingroup monitor + */ +GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp); + +/*! @brief Resets all window hints to their default values. + * + * This function resets all window hints to their + * [default values](@ref window_hints_values). + * + * @note This function may only be called from the main thread. + * + * @sa glfwWindowHint + * + * @ingroup window + */ +GLFWAPI void glfwDefaultWindowHints(void); + +/*! @brief Sets the specified window hint to the desired value. + * + * This function sets hints for the next call to @ref glfwCreateWindow. The + * hints, once set, retain their values until changed by a call to @ref + * glfwWindowHint or @ref glfwDefaultWindowHints, or until the library is + * terminated with @ref glfwTerminate. + * + * @param[in] target The [window hint](@ref window_hints) to set. + * @param[in] hint The new value of the window hint. + * + * @par New in GLFW 3 + * Hints are no longer reset to their default values on window creation. To + * set default hint values, use @ref glfwDefaultWindowHints. + * + * @note This function may only be called from the main thread. + * + * @sa glfwDefaultWindowHints + * + * @ingroup window + */ +GLFWAPI void glfwWindowHint(int target, int hint); + +/*! @brief Creates a window and its associated context. + * + * This function creates a window and its associated context. Most of the + * options controlling how the window and its context should be created are + * specified through @ref glfwWindowHint. + * + * Successful creation does not change which context is current. Before you + * can use the newly created context, you need to make it current using @ref + * glfwMakeContextCurrent. + * + * The created window, framebuffer and context may differ from what you + * requested, as not all parameters and hints are + * [hard constraints](@ref window_hints_hard). This includes the size of the + * window, especially for full screen windows. To retrieve the actual + * attributes of the created window, framebuffer and context, use queries like + * @ref glfwGetWindowAttrib and @ref glfwGetWindowSize. + * + * To create a full screen window, you need to specify the monitor the window + * will cover. If no monitor is specified, windowed mode will be used. Unless + * you have a way for the user to choose a specific monitor, it is recommended + * that you pick the primary monitor. For more information on how to retrieve + * monitors, see @ref monitor_monitors. + * + * By default, newly created windows use the placement recommended by the + * window system. To create the window at a specific position, make it + * initially invisible using the `GLFW_VISIBLE` window hint, set its position + * and then show it. + * + * If a full screen window is active, the screensaver is prohibited from + * starting. + * + * @param[in] width The desired width, in screen coordinates, of the window. + * This must be greater than zero. + * @param[in] height The desired height, in screen coordinates, of the window. + * This must be greater than zero. + * @param[in] title The initial, UTF-8 encoded window title. + * @param[in] monitor The monitor to use for full screen mode, or `NULL` to use + * windowed mode. + * @param[in] share The window whose context to share resources with, or `NULL` + * to not share resources. + * @return The handle of the created window, or `NULL` if an error occurred. + * Errors are reported to the [error callback](@ref intro_error). + * + * @remarks The [swap interval](@ref window_swap) is not set during window + * creation and the initial value may vary depending on driver settings and + * defaults. + * + * @remarks **Windows:** Window creation will fail if the Microsoft GDI + * software OpenGL implementation is the only one available. + * + * @remarks **Windows:** If the executable has an icon resource named + * `GLFW_ICON,` it will be set as the icon for the window. If no such icon is + * present, the `IDI_WINLOGO` icon will be used instead. + * + * @remarks **OS X:** The GLFW window has no icon, as it is not a document + * window, but the dock icon will be the same as the application bundle's icon. + * Also, the first time a window is opened the menu bar is populated with + * common commands like Hide, Quit and About. The (minimal) about dialog uses + * information from the application's bundle. For more information on bundles, + * see the + * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) + * in the Mac Developer Library. + * + * @remarks **X11:** There is no mechanism for setting the window icon yet. + * + * @remarks **X11:** Some window managers will not respect the placement of + * initially hidden windows. + * + * @note This function may only be called from the main thread. + * + * @sa glfwDestroyWindow + * + * @ingroup window + */ +GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share); + +/*! @brief Destroys the specified window and its context. + * + * This function destroys the specified window and its context. On calling + * this function, no further callbacks will be called for that window. + * + * @param[in] window The window to destroy. + * + * @note This function may only be called from the main thread. + * + * @note This function may not be called from a callback. + * + * @note If the window's context is current on the main thread, it is + * detached before being destroyed. + * + * @warning The window's context must not be current on any other thread. + * + * @sa glfwCreateWindow + * + * @ingroup window + */ +GLFWAPI void glfwDestroyWindow(GLFWwindow* window); + +/*! @brief Checks the close flag of the specified window. + * + * This function returns the value of the close flag of the specified window. + * + * @param[in] window The window to query. + * @return The value of the close flag. + * + * @remarks This function may be called from any thread. + * + * @ingroup window + */ +GLFWAPI int glfwWindowShouldClose(GLFWwindow* window); + +/*! @brief Sets the close flag of the specified window. + * + * This function sets the value of the close flag of the specified window. + * This can be used to override the user's attempt to close the window, or + * to signal that it should be closed. + * + * @param[in] window The window whose flag to change. + * @param[in] value The new value. + * + * @remarks This function may be called from any thread. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); + +/*! @brief Sets the title of the specified window. + * + * This function sets the window title, encoded as UTF-8, of the specified + * window. + * + * @param[in] window The window whose title to change. + * @param[in] title The UTF-8 encoded window title. + * + * @note This function may only be called from the main thread. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); + +/*! @brief Retrieves the position of the client area of the specified window. + * + * This function retrieves the position, in screen coordinates, of the + * upper-left corner of the client area of the specified window. + * + * @param[in] window The window to query. + * @param[out] xpos Where to store the x-coordinate of the upper-left corner of + * the client area, or `NULL`. + * @param[out] ypos Where to store the y-coordinate of the upper-left corner of + * the client area, or `NULL`. + * + * @note This function may only be called from the main thread. + * + * @sa glfwSetWindowPos + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); + +/*! @brief Sets the position of the client area of the specified window. + * + * This function sets the position, in screen coordinates, of the upper-left + * corner of the client area of the window. + * + * If the specified window is a full screen window, this function does nothing. + * + * If you wish to set an initial window position you should create a hidden + * window (using @ref glfwWindowHint and `GLFW_VISIBLE`), set its position and + * then show it. + * + * @param[in] window The window to query. + * @param[in] xpos The x-coordinate of the upper-left corner of the client area. + * @param[in] ypos The y-coordinate of the upper-left corner of the client area. + * + * @note It is very rarely a good idea to move an already visible window, as it + * will confuse and annoy the user. + * + * @note This function may only be called from the main thread. + * + * @note The window manager may put limits on what positions are allowed. + * + * @sa glfwGetWindowPos + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); + +/*! @brief Retrieves the size of the client area of the specified window. + * + * This function retrieves the size, in screen coordinates, of the client area + * of the specified window. If you wish to retrieve the size of the + * framebuffer in pixels, see @ref glfwGetFramebufferSize. + * + * @param[in] window The window whose size to retrieve. + * @param[out] width Where to store the width, in screen coordinates, of the + * client area, or `NULL`. + * @param[out] height Where to store the height, in screen coordinates, of the + * client area, or `NULL`. + * + * @note This function may only be called from the main thread. + * + * @sa glfwSetWindowSize + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); + +/*! @brief Sets the size of the client area of the specified window. + * + * This function sets the size, in screen coordinates, of the client area of + * the specified window. + * + * For full screen windows, this function selects and switches to the resolution + * closest to the specified size, without affecting the window's context. As + * the context is unaffected, the bit depths of the framebuffer remain + * unchanged. + * + * @param[in] window The window to resize. + * @param[in] width The desired width of the specified window. + * @param[in] height The desired height of the specified window. + * + * @note This function may only be called from the main thread. + * + * @note The window manager may put limits on what window sizes are allowed. + * + * @sa glfwGetWindowSize + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height); + +/*! @brief Retrieves the size of the framebuffer of the specified window. + * + * This function retrieves the size, in pixels, of the framebuffer of the + * specified window. If you wish to retrieve the size of the window in screen + * coordinates, see @ref glfwGetWindowSize. + * + * @param[in] window The window whose framebuffer to query. + * @param[out] width Where to store the width, in pixels, of the framebuffer, + * or `NULL`. + * @param[out] height Where to store the height, in pixels, of the framebuffer, + * or `NULL`. + * + * @note This function may only be called from the main thread. + * + * @sa glfwSetFramebufferSizeCallback + * + * @ingroup window + */ +GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height); + +/*! @brief Retrieves the size of the frame of the window. + * + * This function retrieves the size, in screen coordinates, of each edge of the + * frame of the specified window. This size includes the title bar, if the + * window has one. The size of the frame may vary depending on the + * [window-related hints](@ref window_hints_wnd) used to create it. + * + * @param[in] window The window whose frame size to query. + * @param[out] left Where to store the size, in screen coordinates, of the left + * edge of the window frame. + * @param[out] top Where to store the size, in screen coordinates, of the top + * edge of the window frame. + * @param[out] right Where to store the size, in screen coordinates, of the + * right edge of the window frame. + * @param[out] bottom Where to store the size, in screen coordinates, of the + * bottom edge of the window frame. + * + * @remarks This function retrieves the size of each window frame edge, not the + * offset along a screen coordinate axis, so the retrieved values will always + * be zero or positive. + * + * @note This function may only be called from the main thread. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int* right, int* bottom); + +/*! @brief Iconifies the specified window. + * + * This function iconifies/minimizes the specified window, if it was previously + * restored. If it is a full screen window, the original monitor resolution is + * restored until the window is restored. If the window is already iconified, + * this function does nothing. + * + * @param[in] window The window to iconify. + * + * @note This function may only be called from the main thread. + * + * @sa glfwRestoreWindow + * + * @ingroup window + */ +GLFWAPI void glfwIconifyWindow(GLFWwindow* window); + +/*! @brief Restores the specified window. + * + * This function restores the specified window, if it was previously + * iconified/minimized. If it is a full screen window, the resolution chosen + * for the window is restored on the selected monitor. If the window is + * already restored, this function does nothing. + * + * @param[in] window The window to restore. + * + * @note This function may only be called from the main thread. + * + * @sa glfwIconifyWindow + * + * @ingroup window + */ +GLFWAPI void glfwRestoreWindow(GLFWwindow* window); + +/*! @brief Makes the specified window visible. + * + * This function makes the specified window visible, if it was previously + * hidden. If the window is already visible or is in full screen mode, this + * function does nothing. + * + * @param[in] window The window to make visible. + * + * @note This function may only be called from the main thread. + * + * @sa glfwHideWindow + * + * @ingroup window + */ +GLFWAPI void glfwShowWindow(GLFWwindow* window); + +/*! @brief Hides the specified window. + * + * This function hides the specified window, if it was previously visible. If + * the window is already hidden or is in full screen mode, this function does + * nothing. + * + * @param[in] window The window to hide. + * + * @note This function may only be called from the main thread. + * + * @sa glfwShowWindow + * + * @ingroup window + */ +GLFWAPI void glfwHideWindow(GLFWwindow* window); + +/*! @brief Returns the monitor that the window uses for full screen mode. + * + * This function returns the handle of the monitor that the specified window is + * in full screen on. + * + * @param[in] window The window to query. + * @return The monitor, or `NULL` if the window is in windowed mode. + * + * @note This function may only be called from the main thread. + * + * @ingroup window + */ +GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); + +/*! @brief Returns an attribute of the specified window. + * + * This function returns an attribute of the specified window. There are many + * attributes, some related to the window and others to its context. + * + * @param[in] window The window to query. + * @param[in] attrib The [window attribute](@ref window_attribs) whose value to + * return. + * @return The value of the attribute, or zero if an error occurred. Errors + * are reported to the [error callback](@ref intro_error). + * + * @note This function may only be called from the main thread. + * + * @ingroup window + */ +GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); + +/*! @brief Sets the user pointer of the specified window. + * + * This function sets the user-defined pointer of the specified window. The + * current value is retained until the window is destroyed. The initial value + * is `NULL`. + * + * @param[in] window The window whose pointer to set. + * @param[in] pointer The new value. + * + * @remarks This function may be called from any thread. + * + * @sa glfwGetWindowUserPointer + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer); + +/*! @brief Returns the user pointer of the specified window. + * + * This function returns the current value of the user-defined pointer of the + * specified window. The initial value is `NULL`. + * + * @param[in] window The window whose pointer to return. + * + * @remarks This function may be called from any thread. + * + * @sa glfwSetWindowUserPointer + * + * @ingroup window + */ +GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); + +/*! @brief Sets the position callback for the specified window. + * + * This function sets the position callback of the specified window, which is + * called when the window is moved. The callback is provided with the screen + * position of the upper-left corner of the client area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @note This function may only be called from the main thread. + * + * @ingroup window + */ +GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun cbfun); + +/*! @brief Sets the size callback for the specified window. + * + * This function sets the size callback of the specified window, which is + * called when the window is resized. The callback is provided with the size, + * in screen coordinates, of the client area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @note This function may only be called from the main thread. + * + * @ingroup window + */ +GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun cbfun); + +/*! @brief Sets the close callback for the specified window. + * + * This function sets the close callback of the specified window, which is + * called when the user attempts to close the window, for example by clicking + * the close widget in the title bar. + * + * The close flag is set before this callback is called, but you can modify it + * at any time with @ref glfwSetWindowShouldClose. + * + * The close callback is not triggered by @ref glfwDestroyWindow. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @par New in GLFW 3 + * The close callback no longer returns a value. + * + * @remarks **OS X:** Selecting Quit from the application menu will + * trigger the close callback for all windows. + * + * @note This function may only be called from the main thread. + * + * @ingroup window + */ +GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun cbfun); + +/*! @brief Sets the refresh callback for the specified window. + * + * This function sets the refresh callback of the specified window, which is + * called when the client area of the window needs to be redrawn, for example + * if the window has been exposed after having been covered by another window. + * + * On compositing window systems such as Aero, Compiz or Aqua, where the window + * contents are saved off-screen, this callback may be called only very + * infrequently or never at all. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @note This function may only be called from the main thread. + * + * @note On compositing window systems such as Aero, Compiz or Aqua, where the + * window contents are saved off-screen, this callback may be called only very + * infrequently or never at all. + * + * @ingroup window + */ +GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun cbfun); + +/*! @brief Sets the focus callback for the specified window. + * + * This function sets the focus callback of the specified window, which is + * called when the window gains or loses focus. + * + * After the focus callback is called for a window that lost focus, synthetic + * key and mouse button release events will be generated for all such that had + * been pressed. For more information, see @ref glfwSetKeyCallback and @ref + * glfwSetMouseButtonCallback. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @note This function may only be called from the main thread. + * + * @ingroup window + */ +GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun cbfun); + +/*! @brief Sets the iconify callback for the specified window. + * + * This function sets the iconification callback of the specified window, which + * is called when the window is iconified or restored. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @note This function may only be called from the main thread. + * + * @ingroup window + */ +GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun cbfun); + +/*! @brief Sets the framebuffer resize callback for the specified window. + * + * This function sets the framebuffer resize callback of the specified window, + * which is called when the framebuffer of the specified window is resized. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @note This function may only be called from the main thread. + * + * @ingroup window + */ +GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun cbfun); + +/*! @brief Processes all pending events. + * + * This function processes only those events that have already been received + * and then returns immediately. Processing events will cause the window and + * input callbacks associated with those events to be called. + * + * This function is not required for joystick input to work. + * + * @par New in GLFW 3 + * This function is no longer called by @ref glfwSwapBuffers. You need to call + * it or @ref glfwWaitEvents yourself. + * + * @remarks On some platforms, a window move, resize or menu operation will + * cause event processing to block. This is due to how event processing is + * designed on those platforms. You can use the + * [window refresh callback](@ref GLFWwindowrefreshfun) to redraw the contents + * of your window when necessary during the operation. + * + * @note This function may only be called from the main thread. + * + * @note This function may not be called from a callback. + * + * @note On some platforms, certain callbacks may be called outside of a call + * to one of the event processing functions. + * + * @sa glfwWaitEvents + * + * @ingroup window + */ +GLFWAPI void glfwPollEvents(void); + +/*! @brief Waits until events are pending and processes them. + * + * This function puts the calling thread to sleep until at least one event has + * been received. Once one or more events have been received, it behaves as if + * @ref glfwPollEvents was called, i.e. the events are processed and the + * function then returns immediately. Processing events will cause the window + * and input callbacks associated with those events to be called. + * + * Since not all events are associated with callbacks, this function may return + * without a callback having been called even if you are monitoring all + * callbacks. + * + * This function is not required for joystick input to work. + * + * @remarks On some platforms, a window move, resize or menu operation will + * cause event processing to block. This is due to how event processing is + * designed on those platforms. You can use the + * [window refresh callback](@ref GLFWwindowrefreshfun) to redraw the contents + * of your window when necessary during the operation. + * + * @remarks If no windows exist, this function returns immediately. For + * synchronization of threads in applications that do not create windows, use + * your threading library of choice. + * + * @note This function may only be called from the main thread. + * + * @note This function may not be called from a callback. + * + * @note On some platforms, certain callbacks may be called outside of a call + * to one of the event processing functions. + * + * @sa glfwPollEvents + * + * @ingroup window + */ +GLFWAPI void glfwWaitEvents(void); + +/*! @brief Posts an empty event to the event queue. + * + * This function posts an empty event from the current thread to the main + * thread event queue, causing @ref glfwWaitEvents to return. + * + * @remarks If no windows exist, this function returns immediately. For + * synchronization of threads in applications that do not create windows, use + * your threading library of choice. + * + * @remarks This function may be called from any thread. + * + * @sa glfwWaitEvents + * + * @ingroup window + */ +GLFWAPI void glfwPostEmptyEvent(void); + +/*! @brief Returns the value of an input option for the specified window. + * + * @param[in] window The window to query. + * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or + * `GLFW_STICKY_MOUSE_BUTTONS`. + * + * @note This function may only be called from the main thread. + * + * @sa glfwSetInputMode + * + * @ingroup input + */ +GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); + +/*! @brief Sets an input option for the specified window. + * @param[in] window The window whose input mode to set. + * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or + * `GLFW_STICKY_MOUSE_BUTTONS`. + * @param[in] value The new value of the specified input mode. + * + * If `mode` is `GLFW_CURSOR`, the value must be one of the supported input + * modes: + * - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally. + * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the client + * area of the window but does not restrict the cursor from leaving. This is + * useful if you wish to render your own cursor or have no visible cursor at + * all. + * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual + * and unlimited cursor movement. This is useful for implementing for + * example 3D camera controls. + * + * If `mode` is `GLFW_STICKY_KEYS`, the value must be either `GL_TRUE` to + * enable sticky keys, or `GL_FALSE` to disable it. If sticky keys are + * enabled, a key press will ensure that @ref glfwGetKey returns @ref + * GLFW_PRESS the next time it is called even if the key had been released + * before the call. This is useful when you are only interested in whether + * keys have been pressed but not when or in which order. + * + * If `mode` is `GLFW_STICKY_MOUSE_BUTTONS`, the value must be either `GL_TRUE` + * to enable sticky mouse buttons, or `GL_FALSE` to disable it. If sticky + * mouse buttons are enabled, a mouse button press will ensure that @ref + * glfwGetMouseButton returns @ref GLFW_PRESS the next time it is called even + * if the mouse button had been released before the call. This is useful when + * you are only interested in whether mouse buttons have been pressed but not + * when or in which order. + * + * @note This function may only be called from the main thread. + * + * @sa glfwGetInputMode + * + * @ingroup input + */ +GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); + +/*! @brief Returns the last reported state of a keyboard key for the specified + * window. + * + * This function returns the last state reported for the specified key to the + * specified window. The returned state is one of `GLFW_PRESS` or + * `GLFW_RELEASE`. The higher-level state `GLFW_REPEAT` is only reported to + * the key callback. + * + * If the `GLFW_STICKY_KEYS` input mode is enabled, this function returns + * `GLFW_PRESS` the first time you call this function after a key has been + * pressed, even if the key has already been released. + * + * The key functions deal with physical keys, with [key tokens](@ref keys) + * named after their use on the standard US keyboard layout. If you want to + * input text, use the Unicode character callback instead. + * + * The [modifier key bit masks](@ref mods) are not key tokens and cannot be + * used with this function. + * + * @param[in] window The desired window. + * @param[in] key The desired [keyboard key](@ref keys). + * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. + * + * @note This function may only be called from the main thread. + * + * @note `GLFW_KEY_UNKNOWN` is not a valid key for this function. + * + * @ingroup input + */ +GLFWAPI int glfwGetKey(GLFWwindow* window, int key); + +/*! @brief Returns the last reported state of a mouse button for the specified + * window. + * + * This function returns the last state reported for the specified mouse button + * to the specified window. + * + * If the `GLFW_STICKY_MOUSE_BUTTONS` input mode is enabled, this function + * returns `GLFW_PRESS` the first time you call this function after a mouse + * button has been pressed, even if the mouse button has already been released. + * + * @param[in] window The desired window. + * @param[in] button The desired [mouse button](@ref buttons). + * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. + * + * @note This function may only be called from the main thread. + * + * @ingroup input + */ +GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); + +/*! @brief Retrieves the last reported cursor position, relative to the client + * area of the window. + * + * This function returns the last reported position of the cursor, in screen + * coordinates, relative to the upper-left corner of the client area of the + * specified window. + * + * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor + * position is unbounded and limited only by the minimum and maximum values of + * a `double`. + * + * The coordinate can be converted to their integer equivalents with the + * `floor` function. Casting directly to an integer type works for positive + * coordinates, but fails for negative ones. + * + * @param[in] window The desired window. + * @param[out] xpos Where to store the cursor x-coordinate, relative to the + * left edge of the client area, or `NULL`. + * @param[out] ypos Where to store the cursor y-coordinate, relative to the to + * top edge of the client area, or `NULL`. + * + * @note This function may only be called from the main thread. + * + * @sa glfwSetCursorPos + * + * @ingroup input + */ +GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); + +/*! @brief Sets the position of the cursor, relative to the client area of the + * window. + * + * This function sets the position, in screen coordinates, of the cursor + * relative to the upper-left corner of the client area of the specified + * window. The window must be focused. If the window does not have focus when + * this function is called, it fails silently. + * + * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor + * position is unbounded and limited only by the minimum and maximum values of + * a `double`. + * + * @param[in] window The desired window. + * @param[in] xpos The desired x-coordinate, relative to the left edge of the + * client area. + * @param[in] ypos The desired y-coordinate, relative to the top edge of the + * client area. + * + * @note This function may only be called from the main thread. + * + * @sa glfwGetCursorPos + * + * @ingroup input + */ +GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); + +/*! @brief Creates a cursor. + * + * Creates a new cursor that can be made the system cursor for a window with + * @ref glfwSetCursor. The cursor can be destroyed with @ref + * glfwDestroyCursor. Any remaining cursors are destroyed by @ref + * glfwTerminate. + * + * The specified image is in 32-bit RGBA format, so eight bits per channel. + * + * @param[in] image The desired cursor image. + * @param[in] xhot The desired x-coordinate of the cursor hotspot. + * @param[in] yhot The desired y-coordinate of the cursor hotspot. + * + * @return A new cursor ready to use or `NULL` if an error occurred. Errors + * are reported to the [error callback](@ref intro_error). + * + * @note This function may only be called from the main thread. + * + * @ingroup input + */ +GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot); + +/*! @brief Destroys a cursor. + * + * This function destroys a cursor previously created with @ref + * glfwCreateCursor. Any remaining cursors will be destroyed by @ref + * glfwTerminate. + * + * @param[in] cursor The cursor object to destroy. + * + * @note This function may only be called from the main thread. + * + * @ingroup input + */ +GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor); + +/*! @brief Sets the system cursor for a given window. + * + * @param[in] window The window to set the system cursor for. + * @param[in] cursor The cursor to change to, or `NULL` to switch back + * to the default system cursor. + * + * @note This function may only be called from the main thread. + * + * @ingroup input + */ +GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); + +/*! @brief Sets the key callback. + * + * This function sets the key callback of the specific window, which is called + * when a key is pressed, repeated or released. + * + * The key functions deal with physical keys, with layout independent + * [key tokens](@ref keys) named after their values in the standard US keyboard + * layout. If you want to input text, use the + * [character callback](@ref glfwSetCharCallback) instead. + * + * When a window loses focus, it will generate synthetic key release events + * for all pressed keys. You can tell these events from user-generated events + * by the fact that the synthetic ones are generated after the window has lost + * focus, i.e. `GLFW_FOCUSED` will be false and the focus callback will have + * already been called. + * + * The scancode of a key is specific to that platform or sometimes even to that + * machine. Scancodes are intended to allow users to bind keys that don't have + * a GLFW key token. Such keys have `key` set to `GLFW_KEY_UNKNOWN`, their + * state is not saved and so it cannot be retrieved with @ref glfwGetKey. + * + * Sometimes GLFW needs to generate synthetic key events, in which case the + * scancode may be zero. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new key callback, or `NULL` to remove the currently + * set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @note This function may only be called from the main thread. + * + * @ingroup input + */ +GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); + +/*! @brief Sets the Unicode character callback. + * + * This function sets the character callback of the specific window, which is + * called when a Unicode character is input. + * + * The character callback is intended for Unicode text input. As it deals with + * characters, it is keyboard layout dependent, whereas the + * [key callback](@ref glfwSetKeyCallback) is not. Characters do not map 1:1 + * to physical keys, as a key may produce zero, one or more characters. If you + * want to know whether a specific physical key was pressed or released, see + * the key callback instead. + * + * The character callback behaves as system text input normally does and will + * not be called if modifier keys are held down that would prevent normal text + * input on that platform, for example a Super (Command) key on OS X or Alt key + * on Windows. There is a + * [character with modifiers callback](@ref glfwSetCharModsCallback) that + * receives these events. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @note This function may only be called from the main thread. + * + * @ingroup input + */ +GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); + +/*! @brief Sets the Unicode character with modifiers callback. + * + * This function sets the character with modifiers callback of the specific + * window, which is called when a Unicode character is input regardless of what + * modifier keys are used. + * + * The character with modifiers callback is intended for implementing custom + * Unicode character input. For regular Unicode text input, see the + * [character callback](@ref glfwSetCharCallback). Like the character + * callback, the character with modifiers callback deals with characters and is + * keyboard layout dependent. Characters do not map 1:1 to physical keys, as + * a key may produce zero, one or more characters. If you want to know whether + * a specific physical key was pressed or released, see the + * [key callback](@ref glfwSetKeyCallback) instead. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @note This function may only be called from the main thread. + * + * @ingroup input + */ +GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun cbfun); + +/*! @brief Sets the mouse button callback. + * + * This function sets the mouse button callback of the specified window, which + * is called when a mouse button is pressed or released. + * + * When a window loses focus, it will generate synthetic mouse button release + * events for all pressed mouse buttons. You can tell these events from + * user-generated events by the fact that the synthetic ones are generated + * after the window has lost focus, i.e. `GLFW_FOCUSED` will be false and the + * focus callback will have already been called. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @note This function may only be called from the main thread. + * + * @ingroup input + */ +GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun cbfun); + +/*! @brief Sets the cursor position callback. + * + * This function sets the cursor position callback of the specified window, + * which is called when the cursor is moved. The callback is provided with the + * position, in screen coordinates, relative to the upper-left corner of the + * client area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @note This function may only be called from the main thread. + * + * @ingroup input + */ +GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun cbfun); + +/*! @brief Sets the cursor enter/exit callback. + * + * This function sets the cursor boundary crossing callback of the specified + * window, which is called when the cursor enters or leaves the client area of + * the window. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @note This function may only be called from the main thread. + * + * @ingroup input + */ +GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun cbfun); + +/*! @brief Sets the scroll callback. + * + * This function sets the scroll callback of the specified window, which is + * called when a scrolling device is used, such as a mouse wheel or scrolling + * area of a touchpad. + * + * The scroll callback receives all scrolling input, like that from a mouse + * wheel or a touchpad scrolling area. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new scroll callback, or `NULL` to remove the currently + * set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @note This function may only be called from the main thread. + * + * @ingroup input + */ +GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cbfun); + +/*! @brief Sets the file drop callback. + * + * This function sets the file drop callback of the specified window, which is + * called when one or more dragged files are dropped on the window. + * + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new file drop callback, or `NULL` to remove the + * currently set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @note This function may only be called from the main thread. + * + * @ingroup input + */ +GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun); + +/*! @brief Returns whether the specified joystick is present. + * + * This function returns whether the specified joystick is present. + * + * @param[in] joy The joystick to query. + * @return `GL_TRUE` if the joystick is present, or `GL_FALSE` otherwise. + * + * @note This function may only be called from the main thread. + * + * @ingroup input + */ +GLFWAPI int glfwJoystickPresent(int joy); + +/*! @brief Returns the values of all axes of the specified joystick. + * + * This function returns the values of all axes of the specified joystick. + * + * @param[in] joy The joystick to query. + * @param[out] count Where to store the size of the returned array. This is + * set to zero if an error occurred. + * @return An array of axis values, or `NULL` if the joystick is not present. + * + * @note This function may only be called from the main thread. + * + * @note The returned array is allocated and freed by GLFW. You should not + * free it yourself. + * + * @note The returned array is valid only until the next call to @ref + * glfwGetJoystickAxes for that joystick. + * + * @ingroup input + */ +GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count); + +/*! @brief Returns the state of all buttons of the specified joystick. + * + * This function returns the state of all buttons of the specified joystick. + * + * @param[in] joy The joystick to query. + * @param[out] count Where to store the size of the returned array. This is + * set to zero if an error occurred. + * @return An array of button states, or `NULL` if the joystick is not present. + * + * @note This function may only be called from the main thread. + * + * @note The returned array is allocated and freed by GLFW. You should not + * free it yourself. + * + * @note The returned array is valid only until the next call to @ref + * glfwGetJoystickButtons for that joystick. + * + * @ingroup input + */ +GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); + +/*! @brief Returns the name of the specified joystick. + * + * This function returns the name, encoded as UTF-8, of the specified joystick. + * + * @param[in] joy The joystick to query. + * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick + * is not present. + * + * @note This function may only be called from the main thread. + * + * @note The returned string is allocated and freed by GLFW. You should not + * free it yourself. + * + * @note The returned string is valid only until the next call to @ref + * glfwGetJoystickName for that joystick. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetJoystickName(int joy); + +/*! @brief Sets the clipboard to the specified string. + * + * This function sets the system clipboard to the specified, UTF-8 encoded + * string. The string is copied before returning, so you don't have to retain + * it afterwards. + * + * @param[in] window The window that will own the clipboard contents. + * @param[in] string A UTF-8 encoded string. + * + * @note This function may only be called from the main thread. + * + * @sa glfwGetClipboardString + * + * @ingroup clipboard + */ +GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); + +/*! @brief Retrieves the contents of the clipboard as a string. + * + * This function returns the contents of the system clipboard, if it contains + * or is convertible to a UTF-8 encoded string. + * + * @param[in] window The window that will request the clipboard contents. + * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` + * if an error occurred. Errors are reported to the + * [error callback](@ref intro_error). + * + * @note This function may only be called from the main thread. + * + * @note The returned string is allocated and freed by GLFW. You should not + * free it yourself. + * + * @note The returned string is valid only until the next call to @ref + * glfwGetClipboardString or @ref glfwSetClipboardString. + * + * @sa glfwSetClipboardString + * + * @ingroup clipboard + */ +GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); + +/*! @brief Returns the value of the GLFW timer. + * + * This function returns the value of the GLFW timer. Unless the timer has + * been set using @ref glfwSetTime, the timer measures time elapsed since GLFW + * was initialized. + * + * @return The current value, in seconds, or zero if an error occurred. Errors + * are reported to the [error callback](@ref intro_error). + * + * @remarks This function may be called from any thread. + * + * @note The resolution of the timer is system dependent, but is usually on the + * order of a few micro- or nanoseconds. It uses the highest-resolution + * monotonic time source on each supported platform. + * + * @ingroup time + */ +GLFWAPI double glfwGetTime(void); + +/*! @brief Sets the GLFW timer. + * + * This function sets the value of the GLFW timer. It then continues to count + * up from that value. + * + * @param[in] time The new value, in seconds. + * + * @note This function may only be called from the main thread. + * + * @note The resolution of the timer is system dependent, but is usually on the + * order of a few micro- or nanoseconds. It uses the highest-resolution + * monotonic time source on each supported platform. + * + * @ingroup time + */ +GLFWAPI void glfwSetTime(double time); + +/*! @brief Makes the context of the specified window current for the calling + * thread. + * + * This function makes the context of the specified window current on the + * calling thread. A context can only be made current on a single thread at + * a time and each thread can have only a single current context at a time. + * + * @param[in] window The window whose context to make current, or `NULL` to + * detach the current context. + * + * @remarks This function may be called from any thread. + * + * @sa glfwGetCurrentContext + * + * @ingroup context + */ +GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window); + +/*! @brief Returns the window whose context is current on the calling thread. + * + * This function returns the window whose context is current on the calling + * thread. + * + * @return The window whose context is current, or `NULL` if no window's + * context is current. + * + * @remarks This function may be called from any thread. + * + * @sa glfwMakeContextCurrent + * + * @ingroup context + */ +GLFWAPI GLFWwindow* glfwGetCurrentContext(void); + +/*! @brief Swaps the front and back buffers of the specified window. + * + * This function swaps the front and back buffers of the specified window. If + * the swap interval is greater than zero, the GPU driver waits the specified + * number of screen updates before swapping the buffers. + * + * @param[in] window The window whose buffers to swap. + * + * @par New in GLFW 3 + * This function no longer calls @ref glfwPollEvents. You need to call it or + * @ref glfwWaitEvents yourself. + * + * @remarks This function may be called from any thread. + * + * @sa glfwSwapInterval + * + * @ingroup context + */ +GLFWAPI void glfwSwapBuffers(GLFWwindow* window); + +/*! @brief Sets the swap interval for the current context. + * + * This function sets the swap interval for the current context, i.e. the + * number of screen updates to wait before swapping the buffers of a window and + * returning from @ref glfwSwapBuffers. This is sometimes called 'vertical + * synchronization', 'vertical retrace synchronization' or 'vsync'. + * + * Contexts that support either of the `WGL_EXT_swap_control_tear` and + * `GLX_EXT_swap_control_tear` extensions also accept negative swap intervals, + * which allow the driver to swap even if a frame arrives a little bit late. + * You can check for the presence of these extensions using @ref + * glfwExtensionSupported. For more information about swap tearing, see the + * extension specifications. + * + * @param[in] interval The minimum number of screen updates to wait for + * until the buffers are swapped by @ref glfwSwapBuffers. + * + * @remarks This function may be called from any thread. + * + * @note This function is not called during window creation, leaving the swap + * interval set to whatever is the default on that platform. This is done + * because some swap interval extensions used by GLFW do not allow the swap + * interval to be reset to zero once it has been set to a non-zero value. + * + * @note Some GPU drivers do not honor the requested swap interval, either + * because of user settings that override the request or due to bugs in the + * driver. + * + * @sa glfwSwapBuffers + * + * @ingroup context + */ +GLFWAPI void glfwSwapInterval(int interval); + +/*! @brief Returns whether the specified extension is available. + * + * This function returns whether the specified + * [OpenGL or context creation API extension](@ref context_glext) is supported + * by the current context. For example, on Windows both the OpenGL and WGL + * extension strings are checked. + * + * @param[in] extension The ASCII encoded name of the extension. + * @return `GL_TRUE` if the extension is available, or `GL_FALSE` otherwise. + * + * @remarks This function may be called from any thread. + * + * @note As this functions searches one or more extension strings on each call, + * it is recommended that you cache its results if it's going to be used + * frequently. The extension strings will not change during the lifetime of + * a context, so there is no danger in doing this. + * + * @ingroup context + */ +GLFWAPI int glfwExtensionSupported(const char* extension); + +/*! @brief Returns the address of the specified function for the current + * context. + * + * This function returns the address of the specified + * [client API or extension function](@ref context_glext), if it is supported + * by the current context. + * + * @param[in] procname The ASCII encoded name of the function. + * @return The address of the function, or `NULL` if the function is + * unavailable. + * + * @remarks This function may be called from any thread. + * + * @note The addresses of these functions are not guaranteed to be the same for + * all contexts, especially if they use different client APIs or even different + * context creation hints. + * + * @ingroup context + */ +GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); + + +/************************************************************************* + * Global definition cleanup + *************************************************************************/ + +/* ------------------- BEGIN SYSTEM/COMPILER SPECIFIC -------------------- */ + +#ifdef GLFW_WINGDIAPI_DEFINED + #undef WINGDIAPI + #undef GLFW_WINGDIAPI_DEFINED +#endif + +#ifdef GLFW_CALLBACK_DEFINED + #undef CALLBACK + #undef GLFW_CALLBACK_DEFINED +#endif + +/* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _glfw3_h_ */ + diff --git a/third_party/glfw/glfw3native.h b/third_party/glfw/glfw3native.h new file mode 100644 index 0000000000..f8e6662e05 --- /dev/null +++ b/third_party/glfw/glfw3native.h @@ -0,0 +1,202 @@ +/************************************************************************* + * GLFW 3.1 - www.glfw.org + * A library for OpenGL, window and input + *------------------------------------------------------------------------ + * Copyright (c) 2002-2006 Marcus Geelnard + * Copyright (c) 2006-2010 Camilla Berglund + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would + * be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + *************************************************************************/ + +#ifndef _glfw3_native_h_ +#define _glfw3_native_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************************************* + * Doxygen documentation + *************************************************************************/ + +/*! @defgroup native Native access + * + * **By using the native API, you assert that you know what you're doing and + * how to fix problems caused by using it. If you don't, you shouldn't be + * using it.** + * + * Before the inclusion of @ref glfw3native.h, you must define exactly one + * window API macro and exactly one context API macro. Failure to do this + * will cause a compile-time error. + * + * The available window API macros are: + * * `GLFW_EXPOSE_NATIVE_WIN32` + * * `GLFW_EXPOSE_NATIVE_COCOA` + * * `GLFW_EXPOSE_NATIVE_X11` + * + * The available context API macros are: + * * `GLFW_EXPOSE_NATIVE_WGL` + * * `GLFW_EXPOSE_NATIVE_NSGL` + * * `GLFW_EXPOSE_NATIVE_GLX` + * * `GLFW_EXPOSE_NATIVE_EGL` + * + * These macros select which of the native access functions that are declared + * and which platform-specific headers to include. It is then up your (by + * definition platform-specific) code to handle which of these should be + * defined. + */ + + +/************************************************************************* + * System headers and types + *************************************************************************/ + +#if defined(GLFW_EXPOSE_NATIVE_WIN32) + #include +#elif defined(GLFW_EXPOSE_NATIVE_COCOA) + #include + #if defined(__OBJC__) + #import + #else + typedef void* id; + #endif +#elif defined(GLFW_EXPOSE_NATIVE_X11) + #include +#else + #error "No window API specified" +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WGL) + /* WGL is declared by windows.h */ +#elif defined(GLFW_EXPOSE_NATIVE_NSGL) + /* NSGL is declared by Cocoa.h */ +#elif defined(GLFW_EXPOSE_NATIVE_GLX) + #include +#elif defined(GLFW_EXPOSE_NATIVE_EGL) + #include +#else + #error "No context API specified" +#endif + + +/************************************************************************* + * Functions + *************************************************************************/ + +#if defined(GLFW_EXPOSE_NATIVE_WIN32) +/*! @brief Returns the device name of the specified monitor. + * @return The the device name of the specified monitor. + * @ingroup native + */ +GLFWAPI const WCHAR* glfwGetWin32Monitor(GLFWmonitor* monitor); + +/*! @brief Returns the `HWND` of the specified window. + * @return The `HWND` of the specified window. + * @ingroup native + */ +GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WGL) +/*! @brief Returns the `HGLRC` of the specified window. + * @return The `HGLRC` of the specified window. + * @ingroup native + */ +GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_COCOA) +/*! @brief Returns the `CGDirectDisplayID` of the specified monitor. + * @return The the `CGDirectDisplayID` of the specified monitor. + * @ingroup native + */ +GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor); + +/*! @brief Returns the `NSWindow` of the specified window. + * @return The `NSWindow` of the specified window. + * @ingroup native + */ +GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_NSGL) +/*! @brief Returns the `NSOpenGLContext` of the specified window. + * @return The `NSOpenGLContext` of the specified window. + * @ingroup native + */ +GLFWAPI id glfwGetNSGLContext(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_X11) +/*! @brief Returns the `Display` used by GLFW. + * @return The `Display` used by GLFW. + * @ingroup native + */ +GLFWAPI Display* glfwGetX11Display(void); + +/*! @brief Returns the `RRCrtc` of the specified monitor. + * @return The the `RRCrtc` of the specified monitor. + * @ingroup native + */ +GLFWAPI RRCrtc glfwGetX11Monitor(GLFWmonitor* monitor); + +/*! @brief Returns the `Window` of the specified window. + * @return The `Window` of the specified window. + * @ingroup native + */ +GLFWAPI Window glfwGetX11Window(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_GLX) +/*! @brief Returns the `GLXContext` of the specified window. + * @return The `GLXContext` of the specified window. + * @ingroup native + */ +GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_EGL) +/*! @brief Returns the `EGLDisplay` used by GLFW. + * @return The `EGLDisplay` used by GLFW. + * @ingroup native + */ +GLFWAPI EGLDisplay glfwGetEGLDisplay(void); + +/*! @brief Returns the `EGLContext` of the specified window. + * @return The `EGLContext` of the specified window. + * @ingroup native + */ +GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); + +/*! @brief Returns the `EGLSurface` of the specified window. + * @return The `EGLSurface` of the specified window. + * @ingroup native + */ +GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _glfw3_native_h_ */ + diff --git a/third_party/libuvc/include/libuvc/libuvc.h b/third_party/libuvc/include/libuvc/libuvc.h new file mode 100644 index 0000000000..b7ef8426ac --- /dev/null +++ b/third_party/libuvc/include/libuvc/libuvc.h @@ -0,0 +1,718 @@ +#ifndef LIBUVC_H +#define LIBUVC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include // FILE +#include +#include + +/** UVC error types, based on libusb errors + * @ingroup diag + */ +typedef enum uvc_error { + /** Success (no error) */ + UVC_SUCCESS = 0, + /** Input/output error */ + UVC_ERROR_IO = -1, + /** Invalid parameter */ + UVC_ERROR_INVALID_PARAM = -2, + /** Access denied */ + UVC_ERROR_ACCESS = -3, + /** No such device */ + UVC_ERROR_NO_DEVICE = -4, + /** Entity not found */ + UVC_ERROR_NOT_FOUND = -5, + /** Resource busy */ + UVC_ERROR_BUSY = -6, + /** Operation timed out */ + UVC_ERROR_TIMEOUT = -7, + /** Overflow */ + UVC_ERROR_OVERFLOW = -8, + /** Pipe error */ + UVC_ERROR_PIPE = -9, + /** System call interrupted */ + UVC_ERROR_INTERRUPTED = -10, + /** Insufficient memory */ + UVC_ERROR_NO_MEM = -11, + /** Operation not supported */ + UVC_ERROR_NOT_SUPPORTED = -12, + /** Device is not UVC-compliant */ + UVC_ERROR_INVALID_DEVICE = -50, + /** Mode not supported */ + UVC_ERROR_INVALID_MODE = -51, + /** Resource has a callback (can't use polling and async) */ + UVC_ERROR_CALLBACK_EXISTS = -52, + /** Undefined error */ + UVC_ERROR_OTHER = -99 +} uvc_error_t; + +/** Color coding of stream, transport-independent + * @ingroup streaming + */ +enum uvc_frame_format { + UVC_FRAME_FORMAT_UNKNOWN = 0, + /** Any supported format */ + UVC_FRAME_FORMAT_ANY = 0, + UVC_FRAME_FORMAT_UNCOMPRESSED, + UVC_FRAME_FORMAT_COMPRESSED, + /** YUYV/YUV2/YUV422: YUV encoding with one luminance value per pixel and + * one UV (chrominance) pair for every two pixels. + */ + UVC_FRAME_FORMAT_YUYV, + UVC_FRAME_FORMAT_UYVY, + UVC_FRAME_FORMAT_Y12I, + UVC_FRAME_FORMAT_Y16, + UVC_FRAME_FORMAT_Y8, + UVC_FRAME_FORMAT_Z16, + /** 24-bit RGB */ + UVC_FRAME_FORMAT_RGB, + UVC_FRAME_FORMAT_BGR, + /** Motion-JPEG (or JPEG) encoded images */ + UVC_FRAME_FORMAT_MJPEG, + UVC_FRAME_FORMAT_GRAY8, + UVC_FRAME_FORMAT_BY8, + + /** IVCam Depth */ + UVC_FRAME_FORMAT_INVI, //IR + UVC_FRAME_FORMAT_RELI, //IR + UVC_FRAME_FORMAT_INVR, //Depth + UVC_FRAME_FORMAT_INVZ, //Depth + UVC_FRAME_FORMAT_INRI, //Depth (24 bit) + + /** Number of formats understood */ + UVC_FRAME_FORMAT_COUNT, +}; + +/* UVC_COLOR_FORMAT_* have been replaced with UVC_FRAME_FORMAT_*. Please use + * UVC_FRAME_FORMAT_* instead of using these. */ +#define UVC_COLOR_FORMAT_UNKNOWN UVC_FRAME_FORMAT_UNKNOWN +#define UVC_COLOR_FORMAT_UNCOMPRESSED UVC_FRAME_FORMAT_UNCOMPRESSED +#define UVC_COLOR_FORMAT_COMPRESSED UVC_FRAME_FORMAT_COMPRESSED +#define UVC_COLOR_FORMAT_YUYV UVC_FRAME_FORMAT_YUYV +#define UVC_COLOR_FORMAT_UYVY UVC_FRAME_FORMAT_UYVY +#define UVC_COLOR_FORMAT_RGB UVC_FRAME_FORMAT_RGB +#define UVC_COLOR_FORMAT_BGR UVC_FRAME_FORMAT_BGR +#define UVC_COLOR_FORMAT_MJPEG UVC_FRAME_FORMAT_MJPEG +#define UVC_COLOR_FORMAT_GRAY8 UVC_FRAME_FORMAT_GRAY8 + +/** VideoStreaming interface descriptor subtype (A.6) */ +enum uvc_vs_desc_subtype { + UVC_VS_UNDEFINED = 0x00, + UVC_VS_INPUT_HEADER = 0x01, + UVC_VS_OUTPUT_HEADER = 0x02, + UVC_VS_STILL_IMAGE_FRAME = 0x03, + UVC_VS_FORMAT_UNCOMPRESSED = 0x04, + UVC_VS_FRAME_UNCOMPRESSED = 0x05, + UVC_VS_FORMAT_MJPEG = 0x06, + UVC_VS_FRAME_MJPEG = 0x07, + UVC_VS_FORMAT_MPEG2TS = 0x0a, + UVC_VS_FORMAT_DV = 0x0c, + UVC_VS_COLORFORMAT = 0x0d, + UVC_VS_FORMAT_FRAME_BASED = 0x10, + UVC_VS_FRAME_FRAME_BASED = 0x11, + UVC_VS_FORMAT_STREAM_BASED = 0x12 +}; + +struct uvc_format_desc; +struct uvc_frame_desc; + +/** Frame descriptor + * + * A "frame" is a configuration of a streaming format + * for a particular image size at one of possibly several + * available frame rates. + */ +typedef struct uvc_frame_desc { + struct uvc_format_desc *parent; + struct uvc_frame_desc *prev, *next; + /** Type of frame, such as JPEG frame or uncompressed frme */ + enum uvc_vs_desc_subtype bDescriptorSubtype; + /** Index of the frame within the list of specs available for this format */ + uint8_t bFrameIndex; + uint8_t bmCapabilities; + /** Image width */ + uint16_t wWidth; + /** Image height */ + uint16_t wHeight; + /** Bitrate of corresponding stream at minimal frame rate */ + uint32_t dwMinBitRate; + /** Bitrate of corresponding stream at maximal frame rate */ + uint32_t dwMaxBitRate; + /** Maximum number of bytes for a video frame */ + uint32_t dwMaxVideoFrameBufferSize; + /** Default frame interval (in 100ns units) */ + uint32_t dwDefaultFrameInterval; + /** Minimum frame interval for continuous mode (100ns units) */ + uint32_t dwMinFrameInterval; + /** Maximum frame interval for continuous mode (100ns units) */ + uint32_t dwMaxFrameInterval; + /** Granularity of frame interval range for continuous mode (100ns) */ + uint32_t dwFrameIntervalStep; + /** Frame intervals */ + uint8_t bFrameIntervalType; + /** number of bytes per line */ + uint32_t dwBytesPerLine; + /** Available frame rates, zero-terminated (in 100ns units) */ + uint32_t *intervals; +} uvc_frame_desc_t; + +/** Format descriptor + * + * A "format" determines a stream's image type (e.g., raw YUYV or JPEG) + * and includes many "frame" configurations. + */ +typedef struct uvc_format_desc { + struct uvc_streaming_interface *parent; + struct uvc_format_desc *prev, *next; + /** Type of image stream, such as JPEG or uncompressed. */ + enum uvc_vs_desc_subtype bDescriptorSubtype; + /** Identifier of this format within the VS interface's format list */ + uint8_t bFormatIndex; + uint8_t bNumFrameDescriptors; + /** Format specifier */ + union { + uint8_t guidFormat[16]; + uint8_t fourccFormat[4]; + }; + /** Format-specific data */ + union { + /** BPP for uncompressed stream */ + uint8_t bBitsPerPixel; + /** Flags for JPEG stream */ + uint8_t bmFlags; + }; + /** Default {uvc_frame_desc} to choose given this format */ + uint8_t bDefaultFrameIndex; + uint8_t bAspectRatioX; + uint8_t bAspectRatioY; + uint8_t bmInterlaceFlags; + uint8_t bCopyProtect; + uint8_t bVariableSize; + /** Available frame specifications for this format */ + struct uvc_frame_desc *frame_descs; +} uvc_format_desc_t; + +/** UVC request code (A.8) */ +enum uvc_req_code { + UVC_RC_UNDEFINED = 0x00, + UVC_SET_CUR = 0x01, + UVC_GET_CUR = 0x81, + UVC_GET_MIN = 0x82, + UVC_GET_MAX = 0x83, + UVC_GET_RES = 0x84, + UVC_GET_LEN = 0x85, + UVC_GET_INFO = 0x86, + UVC_GET_DEF = 0x87 +}; + +enum uvc_device_power_mode { + UVC_VC_VIDEO_POWER_MODE_FULL = 0x000b, + UVC_VC_VIDEO_POWER_MODE_DEVICE_DEPENDENT = 0x001b, +}; + +/** Camera terminal control selector (A.9.4) */ +enum uvc_ct_ctrl_selector { + UVC_CT_CONTROL_UNDEFINED = 0x00, + UVC_CT_SCANNING_MODE_CONTROL = 0x01, + UVC_CT_AE_MODE_CONTROL = 0x02, + UVC_CT_AE_PRIORITY_CONTROL = 0x03, + UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL = 0x04, + UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL = 0x05, + UVC_CT_FOCUS_ABSOLUTE_CONTROL = 0x06, + UVC_CT_FOCUS_RELATIVE_CONTROL = 0x07, + UVC_CT_FOCUS_AUTO_CONTROL = 0x08, + UVC_CT_IRIS_ABSOLUTE_CONTROL = 0x09, + UVC_CT_IRIS_RELATIVE_CONTROL = 0x0a, + UVC_CT_ZOOM_ABSOLUTE_CONTROL = 0x0b, + UVC_CT_ZOOM_RELATIVE_CONTROL = 0x0c, + UVC_CT_PANTILT_ABSOLUTE_CONTROL = 0x0d, + UVC_CT_PANTILT_RELATIVE_CONTROL = 0x0e, + UVC_CT_ROLL_ABSOLUTE_CONTROL = 0x0f, + UVC_CT_ROLL_RELATIVE_CONTROL = 0x10, + UVC_CT_PRIVACY_CONTROL = 0x11, + UVC_CT_FOCUS_SIMPLE_CONTROL = 0x12, + UVC_CT_DIGITAL_WINDOW_CONTROL = 0x13, + UVC_CT_REGION_OF_INTEREST_CONTROL = 0x14 +}; + +/** Processing unit control selector (A.9.5) */ +enum uvc_pu_ctrl_selector { + UVC_PU_CONTROL_UNDEFINED = 0x00, + UVC_PU_BACKLIGHT_COMPENSATION_CONTROL = 0x01, + UVC_PU_BRIGHTNESS_CONTROL = 0x02, + UVC_PU_CONTRAST_CONTROL = 0x03, + UVC_PU_GAIN_CONTROL = 0x04, + UVC_PU_POWER_LINE_FREQUENCY_CONTROL = 0x05, + UVC_PU_HUE_CONTROL = 0x06, + UVC_PU_SATURATION_CONTROL = 0x07, + UVC_PU_SHARPNESS_CONTROL = 0x08, + UVC_PU_GAMMA_CONTROL = 0x09, + UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL = 0x0a, + UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL = 0x0b, + UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL = 0x0c, + UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL = 0x0d, + UVC_PU_DIGITAL_MULTIPLIER_CONTROL = 0x0e, + UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL = 0x0f, + UVC_PU_HUE_AUTO_CONTROL = 0x10, + UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL = 0x11, + UVC_PU_ANALOG_LOCK_STATUS_CONTROL = 0x12, + UVC_PU_CONTRAST_AUTO_CONTROL = 0x13 +}; + +/** USB terminal type (B.1) */ +enum uvc_term_type { + UVC_TT_VENDOR_SPECIFIC = 0x0100, + UVC_TT_STREAMING = 0x0101 +}; + +/** Input terminal type (B.2) */ +enum uvc_it_type { + UVC_ITT_VENDOR_SPECIFIC = 0x0200, + UVC_ITT_CAMERA = 0x0201, + UVC_ITT_MEDIA_TRANSPORT_INPUT = 0x0202 +}; + +/** Output terminal type (B.3) */ +enum uvc_ot_type { + UVC_OTT_VENDOR_SPECIFIC = 0x0300, + UVC_OTT_DISPLAY = 0x0301, + UVC_OTT_MEDIA_TRANSPORT_OUTPUT = 0x0302 +}; + +/** External terminal type (B.4) */ +enum uvc_et_type { + UVC_EXTERNAL_VENDOR_SPECIFIC = 0x0400, + UVC_COMPOSITE_CONNECTOR = 0x0401, + UVC_SVIDEO_CONNECTOR = 0x0402, + UVC_COMPONENT_CONNECTOR = 0x0403 +}; + +/** Context, equivalent to libusb's contexts. + * + * May either own a libusb context or use one that's already made. + * + * Always create these with uvc_get_context. + */ +struct uvc_context; +typedef struct uvc_context uvc_context_t; + +/** UVC device. + * + * Get this from uvc_get_device_list() or uvc_find_device(). + */ +struct uvc_device; +typedef struct uvc_device uvc_device_t; + +/** Handle on an open UVC device. + * + * Get one of these from uvc_open(). Once you uvc_close() + * it, it's no longer valid. + */ +struct uvc_device_handle; +typedef struct uvc_device_handle uvc_device_handle_t; + +/** Handle on an open UVC stream. + * + * Get one of these from uvc_stream_open*(). + * Once you uvc_stream_close() it, it will no longer be valid. + */ +struct uvc_stream_handle; +typedef struct uvc_stream_handle uvc_stream_handle_t; + +/** Representation of the interface that brings data into the UVC device */ +typedef struct uvc_input_terminal { + struct uvc_input_terminal *prev, *next; + /** Index of the terminal within the device */ + uint8_t bTerminalID; + /** Type of terminal (e.g., camera) */ + enum uvc_it_type wTerminalType; + uint16_t wObjectiveFocalLengthMin; + uint16_t wObjectiveFocalLengthMax; + uint16_t wOcularFocalLength; + /** Camera controls (meaning of bits given in {uvc_ct_ctrl_selector}) */ + uint64_t bmControls; +} uvc_input_terminal_t; + +typedef struct uvc_output_terminal { + struct uvc_output_terminal *prev, *next; + /** @todo */ +} uvc_output_terminal_t; + +/** Represents post-capture processing functions */ +typedef struct uvc_processing_unit { + struct uvc_processing_unit *prev, *next; + /** Index of the processing unit within the device */ + uint8_t bUnitID; + /** Index of the terminal from which the device accepts images */ + uint8_t bSourceID; + /** Processing controls (meaning of bits given in {uvc_pu_ctrl_selector}) */ + uint64_t bmControls; +} uvc_processing_unit_t; + +/** Custom processing or camera-control functions */ +typedef struct uvc_extension_unit { + struct uvc_extension_unit *prev, *next; + /** Index of the extension unit within the device */ + uint8_t bUnitID; + /** GUID identifying the extension unit */ + uint8_t guidExtensionCode[16]; + /** Bitmap of available controls (manufacturer-dependent) */ + uint64_t bmControls; +} uvc_extension_unit_t; + +enum uvc_status_class { + UVC_STATUS_CLASS_CONTROL = 0x10, + UVC_STATUS_CLASS_CONTROL_CAMERA = 0x11, + UVC_STATUS_CLASS_CONTROL_PROCESSING = 0x12, +}; + +enum uvc_status_attribute { + UVC_STATUS_ATTRIBUTE_VALUE_CHANGE = 0x00, + UVC_STATUS_ATTRIBUTE_INFO_CHANGE = 0x01, + UVC_STATUS_ATTRIBUTE_FAILURE_CHANGE = 0x02, + UVC_STATUS_ATTRIBUTE_UNKNOWN = 0xff +}; + +/** A callback function to accept status updates + * @ingroup device + */ +typedef void(uvc_status_callback_t)(enum uvc_status_class status_class, + int event, + int selector, + enum uvc_status_attribute status_attribute, + void *data, size_t data_len, + void *user_ptr); + +/** Structure representing a UVC device descriptor. + * + * (This isn't a standard structure.) + */ +typedef struct uvc_device_descriptor { + /** Vendor ID */ + uint16_t idVendor; + /** Product ID */ + uint16_t idProduct; + /** UVC compliance level, e.g. 0x0100 (1.0), 0x0110 */ + uint16_t bcdUVC; + /** Serial number (null if unavailable) */ + const char *serialNumber; + /** Device-reported manufacturer name (or null) */ + const char *manufacturer; + /** Device-reporter product name (or null) */ + const char *product; +} uvc_device_descriptor_t; + +/** An image frame received from the UVC device + * @ingroup streaming + */ +typedef struct uvc_frame { + /** Image data for this frame */ + void *data; + /** Size of image data buffer */ + size_t data_bytes; + /** Width of image in pixels */ + uint32_t width; + /** Height of image in pixels */ + uint32_t height; + /** Pixel data format */ + enum uvc_frame_format frame_format; + /** Number of bytes per horizontal line (undefined for compressed format) */ + size_t step; + /** Frame number (may skip, but is strictly monotonically increasing) */ + uint32_t sequence; + /** Estimate of system time when the device started capturing the image */ + struct timeval capture_time; + /** Handle on the device that produced the image. + * @warning You must not call any uvc_* functions during a callback. */ + uvc_device_handle_t *source; + /** Is the data buffer owned by the library? + * If 1, the data buffer can be arbitrarily reallocated by frame conversion + * functions. + * If 0, the data buffer will not be reallocated or freed by the library. + * Set this field to zero if you are supplying the buffer. + */ + uint8_t library_owns_data; +} uvc_frame_t; + +/** A callback function to handle incoming assembled UVC frames + * @ingroup streaming + */ +typedef void(uvc_frame_callback_t)(struct uvc_frame *frame, void *user_ptr); + +/** Streaming mode, includes all information needed to select stream + * @ingroup streaming + */ +typedef struct uvc_stream_ctrl { + uint16_t bmHint; + uint8_t bFormatIndex; + uint8_t bFrameIndex; + uint32_t dwFrameInterval; + uint16_t wKeyFrameRate; + uint16_t wPFrameRate; + uint16_t wCompQuality; + uint16_t wCompWindowSize; + uint16_t wDelay; + uint32_t dwMaxVideoFrameSize; + uint32_t dwMaxPayloadTransferSize; + uint32_t dwClockFrequency; + uint8_t bmFramingInfo; + uint8_t bPreferredVersion; + uint8_t bMinVersion; + uint8_t bMaxVersion; + uint8_t bInterfaceNumber; +} uvc_stream_ctrl_t; + +uvc_error_t uvc_init(uvc_context_t **ctx, struct libusb_context *usb_ctx); +void uvc_exit(uvc_context_t *ctx); + +uvc_error_t uvc_get_device_list( + uvc_context_t *ctx, + uvc_device_t ***list); +void uvc_free_device_list(uvc_device_t **list, uint8_t unref_devices); + +uvc_error_t uvc_get_device_descriptor( + uvc_device_t *dev, + uvc_device_descriptor_t **desc); +void uvc_free_device_descriptor( + uvc_device_descriptor_t *desc); + +uint8_t uvc_get_bus_number(uvc_device_t *dev); +uint8_t uvc_get_device_address(uvc_device_t *dev); + +uvc_error_t uvc_find_device( + uvc_context_t *ctx, + uvc_device_t **dev, + int vid, int pid, const char *sn); + +uvc_error_t uvc_open( + uvc_device_t *dev, + uvc_device_handle_t **devh); + +uvc_error_t uvc_open2( + uvc_device_t *dev, + uvc_device_handle_t **devh, + int camera_number); + +void uvc_close(uvc_device_handle_t *devh); + +uvc_device_t *uvc_get_device(uvc_device_handle_t *devh); +libusb_device_handle *uvc_get_libusb_handle(uvc_device_handle_t *devh); + +void uvc_ref_device(uvc_device_t *dev); +void uvc_unref_device(uvc_device_t *dev); + +void uvc_set_status_callback(uvc_device_handle_t *devh, + uvc_status_callback_t cb, + void *user_ptr); + +const uvc_input_terminal_t *uvc_get_input_terminals(uvc_device_handle_t *devh); +const uvc_output_terminal_t *uvc_get_output_terminals(uvc_device_handle_t *devh); +const uvc_processing_unit_t *uvc_get_processing_units(uvc_device_handle_t *devh); +const uvc_extension_unit_t *uvc_get_extension_units(uvc_device_handle_t *devh); + +uvc_error_t uvc_get_stream_ctrl_format_size( + uvc_device_handle_t *devh, + uvc_stream_ctrl_t *ctrl, + enum uvc_frame_format format, + int width, int height, + int fps + ); + +const uvc_format_desc_t *uvc_get_format_descs(uvc_device_handle_t* ); + +uvc_error_t uvc_probe_stream_ctrl( + uvc_device_handle_t *devh, + uvc_stream_ctrl_t *ctrl); + +uvc_error_t uvc_start_streaming( + uvc_device_handle_t *devh, + uvc_stream_ctrl_t *ctrl, + uvc_frame_callback_t *cb, + void *user_ptr, + uint8_t flags); + +uvc_error_t uvc_start_iso_streaming( + uvc_device_handle_t *devh, + uvc_stream_ctrl_t *ctrl, + uvc_frame_callback_t *cb, + void *user_ptr); + +void uvc_stop_streaming(uvc_device_handle_t *devh); + +uvc_error_t uvc_stream_open_ctrl(uvc_device_handle_t *devh, uvc_stream_handle_t **strmh, uvc_stream_ctrl_t *ctrl); +uvc_error_t uvc_stream_ctrl(uvc_stream_handle_t *strmh, uvc_stream_ctrl_t *ctrl); +uvc_error_t uvc_stream_start(uvc_stream_handle_t *strmh, + uvc_frame_callback_t *cb, + void *user_ptr, + uint8_t flags); +uvc_error_t uvc_stream_start_iso(uvc_stream_handle_t *strmh, + uvc_frame_callback_t *cb, + void *user_ptr); +uvc_error_t uvc_stream_get_frame( + uvc_stream_handle_t *strmh, + uvc_frame_t **frame, + int32_t timeout_us +); +uvc_error_t uvc_stream_stop(uvc_stream_handle_t *strmh); +void uvc_stream_close(uvc_stream_handle_t *strmh); + +int uvc_get_ctrl_len(uvc_device_handle_t *devh, uint8_t unit, uint8_t ctrl); +int uvc_get_ctrl(uvc_device_handle_t *devh, uint8_t unit, uint8_t ctrl, void *data, int len, enum uvc_req_code req_code); +int uvc_set_ctrl(uvc_device_handle_t *devh, uint8_t unit, uint8_t ctrl, void *data, int len); + +uvc_error_t uvc_get_power_mode(uvc_device_handle_t *devh, enum uvc_device_power_mode *mode, enum uvc_req_code req_code); +uvc_error_t uvc_set_power_mode(uvc_device_handle_t *devh, enum uvc_device_power_mode mode); + +/* AUTO-GENERATED control accessors! Update them with the output of `ctrl-gen.py decl`. */ +uvc_error_t uvc_get_scanning_mode(uvc_device_handle_t *devh, uint8_t* mode, enum uvc_req_code req_code); +uvc_error_t uvc_set_scanning_mode(uvc_device_handle_t *devh, uint8_t mode); + +uvc_error_t uvc_get_ae_mode(uvc_device_handle_t *devh, uint8_t* mode, enum uvc_req_code req_code); +uvc_error_t uvc_set_ae_mode(uvc_device_handle_t *devh, uint8_t mode); + +uvc_error_t uvc_get_ae_priority(uvc_device_handle_t *devh, uint8_t* priority, enum uvc_req_code req_code); +uvc_error_t uvc_set_ae_priority(uvc_device_handle_t *devh, uint8_t priority); + +uvc_error_t uvc_get_exposure_abs(uvc_device_handle_t *devh, uint32_t* time, enum uvc_req_code req_code); +uvc_error_t uvc_set_exposure_abs(uvc_device_handle_t *devh, uint32_t time); + +uvc_error_t uvc_get_exposure_rel(uvc_device_handle_t *devh, int8_t* step, enum uvc_req_code req_code); +uvc_error_t uvc_set_exposure_rel(uvc_device_handle_t *devh, int8_t step); + +uvc_error_t uvc_get_focus_abs(uvc_device_handle_t *devh, uint16_t* focus, enum uvc_req_code req_code); +uvc_error_t uvc_set_focus_abs(uvc_device_handle_t *devh, uint16_t focus); + +uvc_error_t uvc_get_focus_rel(uvc_device_handle_t *devh, int8_t* focus_rel, uint8_t* speed, enum uvc_req_code req_code); +uvc_error_t uvc_set_focus_rel(uvc_device_handle_t *devh, int8_t focus_rel, uint8_t speed); + +uvc_error_t uvc_get_focus_simple_range(uvc_device_handle_t *devh, uint8_t* focus, enum uvc_req_code req_code); +uvc_error_t uvc_set_focus_simple_range(uvc_device_handle_t *devh, uint8_t focus); + +uvc_error_t uvc_get_focus_auto(uvc_device_handle_t *devh, uint8_t* state, enum uvc_req_code req_code); +uvc_error_t uvc_set_focus_auto(uvc_device_handle_t *devh, uint8_t state); + +uvc_error_t uvc_get_iris_abs(uvc_device_handle_t *devh, uint16_t* iris, enum uvc_req_code req_code); +uvc_error_t uvc_set_iris_abs(uvc_device_handle_t *devh, uint16_t iris); + +uvc_error_t uvc_get_iris_rel(uvc_device_handle_t *devh, uint8_t* iris_rel, enum uvc_req_code req_code); +uvc_error_t uvc_set_iris_rel(uvc_device_handle_t *devh, uint8_t iris_rel); + +uvc_error_t uvc_get_zoom_abs(uvc_device_handle_t *devh, uint16_t* focal_length, enum uvc_req_code req_code); +uvc_error_t uvc_set_zoom_abs(uvc_device_handle_t *devh, uint16_t focal_length); + +uvc_error_t uvc_get_zoom_rel(uvc_device_handle_t *devh, int8_t* zoom_rel, uint8_t* digital_zoom, uint8_t* speed, enum uvc_req_code req_code); +uvc_error_t uvc_set_zoom_rel(uvc_device_handle_t *devh, int8_t zoom_rel, uint8_t digital_zoom, uint8_t speed); + +uvc_error_t uvc_get_pantilt_abs(uvc_device_handle_t *devh, int32_t* pan, int32_t* tilt, enum uvc_req_code req_code); +uvc_error_t uvc_set_pantilt_abs(uvc_device_handle_t *devh, int32_t pan, int32_t tilt); + +uvc_error_t uvc_get_pantilt_rel(uvc_device_handle_t *devh, int8_t* pan_rel, uint8_t* pan_speed, int8_t* tilt_rel, uint8_t* tilt_speed, enum uvc_req_code req_code); +uvc_error_t uvc_set_pantilt_rel(uvc_device_handle_t *devh, int8_t pan_rel, uint8_t pan_speed, int8_t tilt_rel, uint8_t tilt_speed); + +uvc_error_t uvc_get_roll_abs(uvc_device_handle_t *devh, int16_t* roll, enum uvc_req_code req_code); +uvc_error_t uvc_set_roll_abs(uvc_device_handle_t *devh, int16_t roll); + +uvc_error_t uvc_get_roll_rel(uvc_device_handle_t *devh, int8_t* roll_rel, uint8_t* speed, enum uvc_req_code req_code); +uvc_error_t uvc_set_roll_rel(uvc_device_handle_t *devh, int8_t roll_rel, uint8_t speed); + +uvc_error_t uvc_get_privacy(uvc_device_handle_t *devh, uint8_t* privacy, enum uvc_req_code req_code); +uvc_error_t uvc_set_privacy(uvc_device_handle_t *devh, uint8_t privacy); + +uvc_error_t uvc_get_digital_window(uvc_device_handle_t *devh, uint16_t* window_top, uint16_t* window_left, uint16_t* window_bottom, uint16_t* window_right, uint16_t* num_steps, uint16_t* num_steps_units, enum uvc_req_code req_code); +uvc_error_t uvc_set_digital_window(uvc_device_handle_t *devh, uint16_t window_top, uint16_t window_left, uint16_t window_bottom, uint16_t window_right, uint16_t num_steps, uint16_t num_steps_units); + +uvc_error_t uvc_get_digital_roi(uvc_device_handle_t *devh, uint16_t* roi_top, uint16_t* roi_left, uint16_t* roi_bottom, uint16_t* roi_right, uint16_t* auto_controls, enum uvc_req_code req_code); +uvc_error_t uvc_set_digital_roi(uvc_device_handle_t *devh, uint16_t roi_top, uint16_t roi_left, uint16_t roi_bottom, uint16_t roi_right, uint16_t auto_controls); + +uvc_error_t uvc_get_backlight_compensation(uvc_device_handle_t *devh, uint16_t* backlight_compensation, enum uvc_req_code req_code); +uvc_error_t uvc_set_backlight_compensation(uvc_device_handle_t *devh, uint16_t backlight_compensation); + +uvc_error_t uvc_get_brightness(uvc_device_handle_t *devh, int16_t* brightness, enum uvc_req_code req_code); +uvc_error_t uvc_set_brightness(uvc_device_handle_t *devh, int16_t brightness); + +uvc_error_t uvc_get_contrast(uvc_device_handle_t *devh, uint16_t* contrast, enum uvc_req_code req_code); +uvc_error_t uvc_set_contrast(uvc_device_handle_t *devh, uint16_t contrast); + +uvc_error_t uvc_get_contrast_auto(uvc_device_handle_t *devh, uint8_t* contrast_auto, enum uvc_req_code req_code); +uvc_error_t uvc_set_contrast_auto(uvc_device_handle_t *devh, uint8_t contrast_auto); + +uvc_error_t uvc_get_gain(uvc_device_handle_t *devh, uint16_t* gain, enum uvc_req_code req_code); +uvc_error_t uvc_set_gain(uvc_device_handle_t *devh, uint16_t gain); + +uvc_error_t uvc_get_power_line_frequency(uvc_device_handle_t *devh, uint8_t* power_line_frequency, enum uvc_req_code req_code); +uvc_error_t uvc_set_power_line_frequency(uvc_device_handle_t *devh, uint8_t power_line_frequency); + +uvc_error_t uvc_get_hue(uvc_device_handle_t *devh, int16_t* hue, enum uvc_req_code req_code); +uvc_error_t uvc_set_hue(uvc_device_handle_t *devh, int16_t hue); + +uvc_error_t uvc_get_hue_auto(uvc_device_handle_t *devh, uint8_t* hue_auto, enum uvc_req_code req_code); +uvc_error_t uvc_set_hue_auto(uvc_device_handle_t *devh, uint8_t hue_auto); + +uvc_error_t uvc_get_saturation(uvc_device_handle_t *devh, uint16_t* saturation, enum uvc_req_code req_code); +uvc_error_t uvc_set_saturation(uvc_device_handle_t *devh, uint16_t saturation); + +uvc_error_t uvc_get_sharpness(uvc_device_handle_t *devh, uint16_t* sharpness, enum uvc_req_code req_code); +uvc_error_t uvc_set_sharpness(uvc_device_handle_t *devh, uint16_t sharpness); + +uvc_error_t uvc_get_gamma(uvc_device_handle_t *devh, uint16_t* gamma, enum uvc_req_code req_code); +uvc_error_t uvc_set_gamma(uvc_device_handle_t *devh, uint16_t gamma); + +uvc_error_t uvc_get_white_balance_temperature(uvc_device_handle_t *devh, uint16_t* temperature, enum uvc_req_code req_code); +uvc_error_t uvc_set_white_balance_temperature(uvc_device_handle_t *devh, uint16_t temperature); + +uvc_error_t uvc_get_white_balance_temperature_auto(uvc_device_handle_t *devh, uint8_t* temperature_auto, enum uvc_req_code req_code); +uvc_error_t uvc_set_white_balance_temperature_auto(uvc_device_handle_t *devh, uint8_t temperature_auto); + +uvc_error_t uvc_get_white_balance_component(uvc_device_handle_t *devh, uint16_t* blue, uint16_t* red, enum uvc_req_code req_code); +uvc_error_t uvc_set_white_balance_component(uvc_device_handle_t *devh, uint16_t blue, uint16_t red); + +uvc_error_t uvc_get_white_balance_component_auto(uvc_device_handle_t *devh, uint8_t* white_balance_component_auto, enum uvc_req_code req_code); +uvc_error_t uvc_set_white_balance_component_auto(uvc_device_handle_t *devh, uint8_t white_balance_component_auto); + +uvc_error_t uvc_get_digital_multiplier(uvc_device_handle_t *devh, uint16_t* multiplier_step, enum uvc_req_code req_code); +uvc_error_t uvc_set_digital_multiplier(uvc_device_handle_t *devh, uint16_t multiplier_step); + +uvc_error_t uvc_get_digital_multiplier_limit(uvc_device_handle_t *devh, uint16_t* multiplier_step, enum uvc_req_code req_code); +uvc_error_t uvc_set_digital_multiplier_limit(uvc_device_handle_t *devh, uint16_t multiplier_step); + +uvc_error_t uvc_get_analog_video_standard(uvc_device_handle_t *devh, uint8_t* video_standard, enum uvc_req_code req_code); +uvc_error_t uvc_set_analog_video_standard(uvc_device_handle_t *devh, uint8_t video_standard); + +uvc_error_t uvc_get_analog_video_lock_status(uvc_device_handle_t *devh, uint8_t* status, enum uvc_req_code req_code); +uvc_error_t uvc_set_analog_video_lock_status(uvc_device_handle_t *devh, uint8_t status); + +uvc_error_t uvc_get_input_select(uvc_device_handle_t *devh, uint8_t* selector, enum uvc_req_code req_code); +uvc_error_t uvc_set_input_select(uvc_device_handle_t *devh, uint8_t selector); +/* end AUTO-GENERATED control accessors */ + +void uvc_perror(uvc_error_t err, const char *msg); +const char* uvc_strerror(uvc_error_t err); +void uvc_print_diag(uvc_device_handle_t *devh, FILE *stream); +void uvc_print_stream_ctrl(uvc_stream_ctrl_t *ctrl, FILE *stream); + +uvc_frame_t *uvc_allocate_frame(size_t data_bytes); +void uvc_free_frame(uvc_frame_t *frame); + +uvc_error_t uvc_duplicate_frame(uvc_frame_t *in, uvc_frame_t *out); + +uvc_error_t uvc_yuyv2rgb(uvc_frame_t *in, uvc_frame_t *out); +uvc_error_t uvc_uyvy2rgb(uvc_frame_t *in, uvc_frame_t *out); +uvc_error_t uvc_any2rgb(uvc_frame_t *in, uvc_frame_t *out); + +uvc_error_t uvc_yuyv2bgr(uvc_frame_t *in, uvc_frame_t *out); +uvc_error_t uvc_uyvy2bgr(uvc_frame_t *in, uvc_frame_t *out); +uvc_error_t uvc_any2bgr(uvc_frame_t *in, uvc_frame_t *out); + +#ifdef LIBUVC_HAS_JPEG +uvc_error_t uvc_mjpeg2rgb(uvc_frame_t *in, uvc_frame_t *out); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // !def(LIBUVC_H) + diff --git a/third_party/libuvc/include/libuvc/libuvc_config.h b/third_party/libuvc/include/libuvc/libuvc_config.h new file mode 100644 index 0000000000..88cfe83ff8 --- /dev/null +++ b/third_party/libuvc/include/libuvc/libuvc_config.h @@ -0,0 +1,22 @@ +#ifndef LIBUVC_CONFIG_H +#define LIBUVC_CONFIG_H + +#define LIBUVC_VERSION_MAJOR 0 +#define LIBUVC_VERSION_MINOR 0 +#define LIBUVC_VERSION_PATCH 5 +#define LIBUVC_VERSION_STR "0.0.5" +#define LIBUVC_VERSION_INT \ + ((0 << 16) | \ + (0 << 8) | \ + (5)) + +/** @brief Test whether libuvc is new enough + * This macro evaluates true iff the current version is + * at least as new as the version specified. + */ +#define LIBUVC_VERSION_GTE(major, minor, patch) \ + (LIBUVC_VERSION_INT >= (((major) << 16) | ((minor) << 8) | (patch))) + +#define LIBUVC_HAS_JPEG 1 + +#endif // !def(LIBUVC_CONFIG_H) diff --git a/third_party/libuvc/include/libuvc/libuvc_config.h.in b/third_party/libuvc/include/libuvc/libuvc_config.h.in new file mode 100644 index 0000000000..3bbd653abf --- /dev/null +++ b/third_party/libuvc/include/libuvc/libuvc_config.h.in @@ -0,0 +1,22 @@ +#ifndef LIBUVC_CONFIG_H +#define LIBUVC_CONFIG_H + +#define LIBUVC_VERSION_MAJOR @libuvc_VERSION_MAJOR@ +#define LIBUVC_VERSION_MINOR @libuvc_VERSION_MINOR@ +#define LIBUVC_VERSION_PATCH @libuvc_VERSION_PATCH@ +#define LIBUVC_VERSION_STR "@libuvc_VERSION@" +#define LIBUVC_VERSION_INT \ + ((@libuvc_VERSION_MAJOR@ << 16) | \ + (@libuvc_VERSION_MINOR@ << 8) | \ + (@libuvc_VERSION_PATCH@)) + +/** @brief Test whether libuvc is new enough + * This macro evaluates true iff the current version is + * at least as new as the version specified. + */ +#define LIBUVC_VERSION_GTE(major, minor, patch) \ + (LIBUVC_VERSION_INT >= (((major) << 16) | ((minor) << 8) | (patch))) + +#cmakedefine LIBUVC_HAS_JPEG 1 + +#endif // !def(LIBUVC_CONFIG_H) diff --git a/third_party/libuvc/include/libuvc/libuvc_internal.h b/third_party/libuvc/include/libuvc/libuvc_internal.h new file mode 100644 index 0000000000..7fbc74aa62 --- /dev/null +++ b/third_party/libuvc/include/libuvc/libuvc_internal.h @@ -0,0 +1,296 @@ +/** @file libuvc_internal.h + * @brief Implementation-specific UVC constants and structures. + * @cond include_hidden + */ +#ifndef LIBUVC_INTERNAL_H +#define LIBUVC_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include "utlist.h" + +/** Converts an unaligned four-byte little-endian integer into an int32 */ +#define DW_TO_INT(p) ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24)) +/** Converts an unaligned two-byte little-endian integer into an int16 */ +#define SW_TO_SHORT(p) ((p)[0] | ((p)[1] << 8)) +/** Converts an int16 into an unaligned two-byte little-endian integer */ +#define SHORT_TO_SW(s, p) \ + (p)[0] = (s); \ + (p)[1] = (s) >> 8; +/** Converts an int32 into an unaligned four-byte little-endian integer */ +#define INT_TO_DW(i, p) \ + (p)[0] = (i); \ + (p)[1] = (i) >> 8; \ + (p)[2] = (i) >> 16; \ + (p)[3] = (i) >> 24; + +/** Selects the nth item in a doubly linked list. n=-1 selects the last item. */ +#define DL_NTH(head, out, n) \ + do { \ + int dl_nth_i = 0; \ + LDECLTYPE(head) dl_nth_p = (head); \ + if ((n) < 0) { \ + while (dl_nth_p && dl_nth_i > (n)) { \ + dl_nth_p = dl_nth_p->prev; \ + dl_nth_i--; \ + } \ + } else { \ + while (dl_nth_p && dl_nth_i < (n)) { \ + dl_nth_p = dl_nth_p->next; \ + dl_nth_i++; \ + } \ + } \ + (out) = dl_nth_p; \ + } while (0); + +#ifdef UVC_DEBUGGING +#include +#define UVC_DEBUG(format, ...) fprintf(stderr, "[%s:%d/%s] " format "\n", basename(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__) +#define UVC_ENTER() fprintf(stderr, "[%s:%d] begin %s\n", basename(__FILE__), __LINE__, __FUNCTION__) +#define UVC_EXIT(code) fprintf(stderr, "[%s:%d] end %s (%d)\n", basename(__FILE__), __LINE__, __FUNCTION__, code) +#define UVC_EXIT_VOID() fprintf(stderr, "[%s:%d] end %s\n", basename(__FILE__), __LINE__, __FUNCTION__) +#else +#define UVC_DEBUG(format, ...) +#define UVC_ENTER() +#define UVC_EXIT_VOID() +#define UVC_EXIT(code) +#endif + +/* http://stackoverflow.com/questions/19452971/array-size-macro-that-rejects-pointers */ +#define IS_INDEXABLE(arg) (sizeof(arg[0])) +#define IS_ARRAY(arg) (IS_INDEXABLE(arg) && (((void *) &arg) == ((void *) arg))) +#define ARRAYSIZE(arr) (sizeof(arr) / (IS_ARRAY(arr) ? sizeof(arr[0]) : 0)) + +/** Video interface subclass code (A.2) */ +enum uvc_int_subclass_code { + UVC_SC_UNDEFINED = 0x00, + UVC_SC_VIDEOCONTROL = 0x01, + UVC_SC_VIDEOSTREAMING = 0x02, + UVC_SC_VIDEO_INTERFACE_COLLECTION = 0x03 +}; + +/** Video interface protocol code (A.3) */ +enum uvc_int_proto_code { + UVC_PC_PROTOCOL_UNDEFINED = 0x00 +}; + +/** VideoControl interface descriptor subtype (A.5) */ +enum uvc_vc_desc_subtype { + UVC_VC_DESCRIPTOR_UNDEFINED = 0x00, + UVC_VC_HEADER = 0x01, + UVC_VC_INPUT_TERMINAL = 0x02, + UVC_VC_OUTPUT_TERMINAL = 0x03, + UVC_VC_SELECTOR_UNIT = 0x04, + UVC_VC_PROCESSING_UNIT = 0x05, + UVC_VC_EXTENSION_UNIT = 0x06 +}; + +/** UVC endpoint descriptor subtype (A.7) */ +enum uvc_ep_desc_subtype { + UVC_EP_UNDEFINED = 0x00, + UVC_EP_GENERAL = 0x01, + UVC_EP_ENDPOINT = 0x02, + UVC_EP_INTERRUPT = 0x03 +}; + +/** VideoControl interface control selector (A.9.1) */ +enum uvc_vc_ctrl_selector { + UVC_VC_CONTROL_UNDEFINED = 0x00, + UVC_VC_VIDEO_POWER_MODE_CONTROL = 0x01, + UVC_VC_REQUEST_ERROR_CODE_CONTROL = 0x02 +}; + +/** Terminal control selector (A.9.2) */ +enum uvc_term_ctrl_selector { + UVC_TE_CONTROL_UNDEFINED = 0x00 +}; + +/** Selector unit control selector (A.9.3) */ +enum uvc_su_ctrl_selector { + UVC_SU_CONTROL_UNDEFINED = 0x00, + UVC_SU_INPUT_SELECT_CONTROL = 0x01 +}; + +/** Extension unit control selector (A.9.6) */ +enum uvc_xu_ctrl_selector { + UVC_XU_CONTROL_UNDEFINED = 0x00 +}; + +/** VideoStreaming interface control selector (A.9.7) */ +enum uvc_vs_ctrl_selector { + UVC_VS_CONTROL_UNDEFINED = 0x00, + UVC_VS_PROBE_CONTROL = 0x01, + UVC_VS_COMMIT_CONTROL = 0x02, + UVC_VS_STILL_PROBE_CONTROL = 0x03, + UVC_VS_STILL_COMMIT_CONTROL = 0x04, + UVC_VS_STILL_IMAGE_TRIGGER_CONTROL = 0x05, + UVC_VS_STREAM_ERROR_CODE_CONTROL = 0x06, + UVC_VS_GENERATE_KEY_FRAME_CONTROL = 0x07, + UVC_VS_UPDATE_FRAME_SEGMENT_CONTROL = 0x08, + UVC_VS_SYNC_DELAY_CONTROL = 0x09 +}; + +/** Status packet type (2.4.2.2) */ +enum uvc_status_type { + UVC_STATUS_TYPE_CONTROL = 1, + UVC_STATUS_TYPE_STREAMING = 2 +}; + +/** Payload header flags (2.4.3.3) */ +#define UVC_STREAM_EOH (1 << 7) +#define UVC_STREAM_ERR (1 << 6) +#define UVC_STREAM_STI (1 << 5) +#define UVC_STREAM_RES (1 << 4) +#define UVC_STREAM_SCR (1 << 3) +#define UVC_STREAM_PTS (1 << 2) +#define UVC_STREAM_EOF (1 << 1) +#define UVC_STREAM_FID (1 << 0) + +/** Control capabilities (4.1.2) */ +#define UVC_CONTROL_CAP_GET (1 << 0) +#define UVC_CONTROL_CAP_SET (1 << 1) +#define UVC_CONTROL_CAP_DISABLED (1 << 2) +#define UVC_CONTROL_CAP_AUTOUPDATE (1 << 3) +#define UVC_CONTROL_CAP_ASYNCHRONOUS (1 << 4) + +struct uvc_streaming_interface; +struct uvc_device_info; + +/** VideoStream interface */ +typedef struct uvc_streaming_interface { + struct uvc_device_info *parent; + struct uvc_streaming_interface *prev, *next; + /** Interface number */ + uint8_t bInterfaceNumber; + /** Video formats that this interface provides */ + struct uvc_format_desc *format_descs; + /** USB endpoint to use when communicating with this interface */ + uint8_t bEndpointAddress; + uint8_t bTerminalLink; +} uvc_streaming_interface_t; + +/** VideoControl interface */ +typedef struct uvc_control_interface { + struct uvc_device_info *parent; + struct uvc_input_terminal *input_term_descs; + // struct uvc_output_terminal *output_term_descs; + struct uvc_processing_unit *processing_unit_descs; + struct uvc_extension_unit *extension_unit_descs; + uint16_t bcdUVC; + uint8_t bEndpointAddress; + /** Interface number */ + uint8_t bInterfaceNumber; +} uvc_control_interface_t; + +struct uvc_stream_ctrl; + +struct uvc_device { + struct uvc_context *ctx; + int ref; + libusb_device *usb_dev; +}; + +typedef struct uvc_device_info { + /** Configuration descriptor for USB device */ + struct libusb_config_descriptor *config; + /** VideoControl interface provided by device */ + uvc_control_interface_t ctrl_if; + /** VideoStreaming interfaces on the device */ + uvc_streaming_interface_t *stream_ifs; + + /* Store the interface for multiple UVCs on a single VID/PID device (Intel RealSense, VF200, e.g) */ + int camera_number; +} uvc_device_info_t; + +/* + set a high number of transfer buffers. This uses a lot of ram, but + avoids problems with scheduling delays on slow boards causing missed + transfers. A better approach may be to make the transfer thread FIFO + scheduled (if we have root). + We could/should change this to allow reduce it to, say, 5 by default + and then allow the user to change the number of buffers as required. + */ +#define LIBUVC_NUM_TRANSFER_BUFS 2 + +#define LIBUVC_XFER_BUF_SIZE ( 32 * 1024 * 1024 ) + +struct uvc_stream_handle { + struct uvc_device_handle *devh; + struct uvc_stream_handle *prev, *next; + struct uvc_streaming_interface *stream_if; + + /** if true, stream is running (streaming video to host) */ + uint8_t running; + /** Current control block */ + struct uvc_stream_ctrl cur_ctrl; + + /* listeners may only access hold*, and only when holding a + * lock on cb_mutex (probably signaled with cb_cond) */ + uint8_t fid; + uint32_t seq, hold_seq; + uint32_t pts, hold_pts; + uint32_t last_scr, hold_last_scr; + size_t got_bytes, hold_bytes; + uint8_t *outbuf, *holdbuf; + pthread_mutex_t cb_mutex; + pthread_cond_t cb_cond; + pthread_t cb_thread; + uint32_t last_polled_seq; + uvc_frame_callback_t *user_cb; + void *user_ptr; + struct libusb_transfer *transfers[LIBUVC_NUM_TRANSFER_BUFS]; + uint8_t *transfer_bufs[LIBUVC_NUM_TRANSFER_BUFS]; + struct uvc_frame frame; + enum uvc_frame_format frame_format; +}; + +/** Handle on an open UVC device + * + * @todo move most of this into a uvc_device struct? + */ +struct uvc_device_handle { + struct uvc_device *dev; + struct uvc_device_handle *prev, *next; + /** Underlying USB device handle */ + libusb_device_handle *usb_devh; + struct uvc_device_info *info; + struct libusb_transfer *status_xfer; + uint8_t status_buf[32]; + /** Function to call when we receive status updates from the camera */ + uvc_status_callback_t *status_cb; + void *status_user_ptr; + + uvc_stream_handle_t *streams; + /** Whether the camera is an iSight that sends one header per frame */ + uint8_t is_isight; +}; + +/** Context within which we communicate with devices */ +struct uvc_context { + /** Underlying context for USB communication */ + struct libusb_context *usb_ctx; + /** True iff libuvc initialized the underlying USB context */ + uint8_t own_usb_ctx; + /** List of open devices in this context */ + uvc_device_handle_t *open_devices; + pthread_t handler_thread; + uint8_t kill_handler_thread; +}; + +uvc_error_t uvc_query_stream_ctrl( + uvc_device_handle_t *devh, + uvc_stream_ctrl_t *ctrl, + uint8_t probe, + enum uvc_req_code req); + +void uvc_start_handler_thread(uvc_context_t *ctx); +uvc_error_t uvc_claim_if(uvc_device_handle_t *devh, int idx); +uvc_error_t uvc_release_if(uvc_device_handle_t *devh, int idx); + +#endif // !def(LIBUVC_INTERNAL_H) +/** @endcond */ + diff --git a/third_party/libuvc/include/utlist.h b/third_party/libuvc/include/utlist.h new file mode 100644 index 0000000000..34c725b917 --- /dev/null +++ b/third_party/libuvc/include/utlist.h @@ -0,0 +1,490 @@ +/* +Copyright (c) 2007-2010, Troy D. Hanson http://uthash.sourceforge.net +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 1.9.1 + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ code), this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define LDECLTYPE(x) char* +#endif +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define _NEXT(elt,list) ((char*)((list)->next)) +#define _NEXTASGN(elt,list,to) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +#define _PREV(elt,list) ((char*)((list)->prev)) +#define _PREVASGN(elt,list,to) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define _SV(elt,list) +#define _NEXT(elt,list) ((elt)->next) +#define _NEXTASGN(elt,list,to) ((elt)->next)=(to) +#define _PREV(elt,list) ((elt)->prev) +#define _PREVASGN(elt,list,to) ((elt)->prev)=(to) +#define _RS(list) +#define _CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +#define DL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev, _ls_tail); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +#define CDL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + LDECLTYPE(list) _tmp2; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); \ + if (_NEXT(_ls_q,list) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = _NEXT(_ls_q,list); \ + } \ + _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev,_ls_tail); \ + _CASTASGN(_tmp2,list); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp2); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ +do { \ + (add)->next = head; \ + head = add; \ +} while (0) + +#define LL_APPEND(head,add) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = head; \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_DELETE(head,del) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = head; \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = ((del)->next); \ + } \ + } \ +} while (0) + +/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ +#define LL_APPEND_VS2008(head,add) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#define LL_DELETE_VS2008(head,del) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while (head->next && (head->next != (del))) { \ + head = head->next; \ + } \ + if (head->next) { \ + head->next = ((del)->next); \ + } \ + { \ + char **_head_alias = (char**)&(head); \ + *_head_alias = _tmp; \ + } \ + } \ +} while (0) +#ifdef NO_DECLTYPE +#undef LL_APPEND +#define LL_APPEND LL_APPEND_VS2008 +#undef LL_DELETE +#define LL_DELETE LL_DELETE_VS2008 +#endif +/* end VS2008 replacements */ + +#define LL_FOREACH(head,el) \ + for(el=head;el;el=el->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ +do { \ + LL_FOREACH(head,out) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define LL_SEARCH(head,out,elt,cmp) \ +do { \ + LL_FOREACH(head,out) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ +do { \ + (add)->next = head; \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0); + +#define DL_DELETE(head,del) \ +do { \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del)==(head)) { \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0); + + +#define DL_FOREACH(head,el) \ + for(el=head;el;el=el->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_PREPEND(head,add) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ +(head)=(add); \ +} while (0) + +#define CDL_DELETE(head,del) \ +do { \ + if ( ((head)==(del)) && ((head)->next == (head))) { \ + (head) = 0L; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0); + +#define CDL_FOREACH(head,el) \ + for(el=head;el;el=(el->next==head ? 0L : el->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ + (el) && ((tmp2)=(el)->next, 1); \ + ((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ +do { \ + CDL_FOREACH(head,out) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define CDL_SEARCH(head,out,elt,cmp) \ +do { \ + CDL_FOREACH(head,out) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +#endif /* UTLIST_H */ + diff --git a/third_party/libuvc/src/ctrl.c b/third_party/libuvc/src/ctrl.c new file mode 100644 index 0000000000..bad85bba95 --- /dev/null +++ b/third_party/libuvc/src/ctrl.c @@ -0,0 +1,165 @@ +/********************************************************************* +* Software License Agreement (BSD License) +* +* Copyright (C) 2010-2012 Ken Tossell +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of the author nor other contributors may be +* used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*********************************************************************/ +/** + * @defgroup ctrl Video capture and processing controls + * @brief Functions for manipulating device settings and stream parameters + * + * The `uvc_get_*` and `uvc_set_*` functions are used to read and write the settings associated + * with the device's input, processing and output units. + */ + +#include "libuvc/libuvc.h" +#include "libuvc/libuvc_internal.h" + +static const int REQ_TYPE_SET = 0x21; +static const int REQ_TYPE_GET = 0xa1; + +/***** GENERIC CONTROLS *****/ +/** + * @brief Get the length of a control on a terminal or unit. + * + * @param devh UVC device handle + * @param unit Unit or Terminal ID; obtain this from the uvc_extension_unit_t describing the extension unit + * @param ctrl Vendor-specific control number to query + * @return On success, the length of the control as reported by the device. Otherwise, + * a uvc_error_t error describing the error encountered. + * @ingroup ctrl + */ +int uvc_get_ctrl_len(uvc_device_handle_t *devh, uint8_t unit, uint8_t ctrl) { + unsigned char buf[2]; + + int ret = libusb_control_transfer( + devh->usb_devh, + REQ_TYPE_GET, UVC_GET_LEN, + ctrl << 8, + unit << 8, + buf, + 2, + 0 /* timeout */); + + if (ret < 0) + return ret; + else + return (unsigned short)SW_TO_SHORT(buf); +} + +/** + * @brief Perform a GET_* request from an extension unit. + * + * @param devh UVC device handle + * @param unit Unit ID; obtain this from the uvc_extension_unit_t describing the extension unit + * @param ctrl Control number to query + * @param data Data buffer to be filled by the device + * @param len Size of data buffer + * @param req_code GET_* request to execute + * @return On success, the number of bytes actually transferred. Otherwise, + * a uvc_error_t error describing the error encountered. + * @ingroup ctrl + */ +int uvc_get_ctrl(uvc_device_handle_t *devh, uint8_t unit, uint8_t ctrl, void *data, int len, enum uvc_req_code req_code) { + return libusb_control_transfer( + devh->usb_devh, + REQ_TYPE_GET, req_code, + ctrl << 8, + unit << 8, + data, + len, + 0 /* timeout */); +} + +/** + * @brief Perform a SET_CUR request to a terminal or unit. + * + * @param devh UVC device handle + * @param unit Unit or Terminal ID + * @param ctrl Control number to set + * @param data Data buffer to be sent to the device + * @param len Size of data buffer + * @return On success, the number of bytes actually transferred. Otherwise, + * a uvc_error_t error describing the error encountered. + * @ingroup ctrl + */ +int uvc_set_ctrl(uvc_device_handle_t *devh, uint8_t unit, uint8_t ctrl, void *data, int len) { + return libusb_control_transfer( + devh->usb_devh, + REQ_TYPE_SET, UVC_SET_CUR, + ctrl << 8, + unit << 8, + data, + len, + 0 /* timeout */); +} + +/***** INTERFACE CONTROLS *****/ +uvc_error_t uvc_get_power_mode(uvc_device_handle_t *devh, enum uvc_device_power_mode *mode, enum uvc_req_code req_code) { + uint8_t mode_char; + uvc_error_t ret; + + ret = libusb_control_transfer( + devh->usb_devh, + REQ_TYPE_GET, req_code, + UVC_VC_VIDEO_POWER_MODE_CONTROL << 8, + 0, + &mode_char, + sizeof(mode_char), + 0); + + if (ret == 1) { + *mode = mode_char; + return UVC_SUCCESS; + } else { + return ret; + } +} + +uvc_error_t uvc_set_power_mode(uvc_device_handle_t *devh, enum uvc_device_power_mode mode) { + uint8_t mode_char = mode; + uvc_error_t ret; + + ret = libusb_control_transfer( + devh->usb_devh, + REQ_TYPE_SET, UVC_SET_CUR, + UVC_VC_VIDEO_POWER_MODE_CONTROL << 8, + 0, + &mode_char, + sizeof(mode_char), + 0); + + if (ret == 1) + return UVC_SUCCESS; + else + return ret; +} + +/** @todo Request Error Code Control (UVC 1.5, 4.2.1.2) */ diff --git a/third_party/libuvc/src/device.c b/third_party/libuvc/src/device.c new file mode 100644 index 0000000000..905013b672 --- /dev/null +++ b/third_party/libuvc/src/device.c @@ -0,0 +1,1621 @@ + /********************************************************************* +* Software License Agreement (BSD License) +* +* Copyright (C) 2010-2012 Ken Tossell +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of the author nor other contributors may be +* used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*********************************************************************/ +/** + * @defgroup device Device handling and enumeration + * @brief Support for finding, inspecting and opening UVC devices + */ + +#include "libuvc/libuvc.h" +#include "libuvc/libuvc_internal.h" + +int uvc_already_open(uvc_context_t *ctx, struct libusb_device *usb_dev); +void uvc_free_devh(uvc_device_handle_t *devh); + +uvc_error_t uvc_get_device_info(uvc_device_t *dev, uvc_device_info_t **info); +uvc_error_t uvc_get_device_info2(uvc_device_t *dev, uvc_device_info_t **info, int camera_number); +void uvc_free_device_info(uvc_device_info_t *info); + +uvc_error_t uvc_scan_control(uvc_device_t *dev, uvc_device_info_t *info); +uvc_error_t uvc_parse_vc(uvc_device_t *dev, + uvc_device_info_t *info, + const unsigned char *block, size_t block_size); +uvc_error_t uvc_parse_vc_extension_unit(uvc_device_t *dev, + uvc_device_info_t *info, + const unsigned char *block, + size_t block_size); +uvc_error_t uvc_parse_vc_header(uvc_device_t *dev, + uvc_device_info_t *info, + const unsigned char *block, size_t block_size); +uvc_error_t uvc_parse_vc_input_terminal(uvc_device_t *dev, + uvc_device_info_t *info, + const unsigned char *block, + size_t block_size); +uvc_error_t uvc_parse_vc_processing_unit(uvc_device_t *dev, + uvc_device_info_t *info, + const unsigned char *block, + size_t block_size); + +uvc_error_t uvc_scan_streaming(uvc_device_t *dev, + uvc_device_info_t *info, + int interface_idx); +uvc_error_t uvc_parse_vs(uvc_device_t *dev, + uvc_device_info_t *info, + uvc_streaming_interface_t *stream_if, + const unsigned char *block, size_t block_size); +uvc_error_t uvc_parse_vs_format_uncompressed(uvc_streaming_interface_t *stream_if, + const unsigned char *block, + size_t block_size); +uvc_error_t uvc_parse_vs_format_mjpeg(uvc_streaming_interface_t *stream_if, + const unsigned char *block, + size_t block_size); +uvc_error_t uvc_parse_vs_frame_uncompressed(uvc_streaming_interface_t *stream_if, + const unsigned char *block, + size_t block_size); +uvc_error_t uvc_parse_vs_frame_format(uvc_streaming_interface_t *stream_if, + const unsigned char *block, + size_t block_size); +uvc_error_t uvc_parse_vs_frame_frame(uvc_streaming_interface_t *stream_if, + const unsigned char *block, + size_t block_size); +uvc_error_t uvc_parse_vs_input_header(uvc_streaming_interface_t *stream_if, + const unsigned char *block, + size_t block_size); + +void LIBUSB_CALL _uvc_status_callback(struct libusb_transfer *transfer); + +/** @internal + * @brief Test whether the specified USB device has been opened as a UVC device + * @ingroup device + * + * @param ctx Context in which to search for the UVC device + * @param usb_dev USB device to find + * @return true if the device is open in this context + */ +int uvc_already_open(uvc_context_t *ctx, struct libusb_device *usb_dev) { + uvc_device_handle_t *devh; + + DL_FOREACH(ctx->open_devices, devh) { + if (usb_dev == devh->dev->usb_dev) + return 1; + } + + return 0; +} + +/** @brief Finds a camera identified by vendor, product and/or serial number + * @ingroup device + * + * @param[in] ctx UVC context in which to search for the camera + * @param[out] dev Reference to the camera, or NULL if not found + * @param[in] vid Vendor ID number, optional + * @param[in] pid Product ID number, optional + * @param[in] sn Serial number or NULL + * @return Error finding device or UVC_SUCCESS + */ +uvc_error_t uvc_find_device( + uvc_context_t *ctx, uvc_device_t **dev, + int vid, int pid, const char *sn) { + uvc_error_t ret = UVC_SUCCESS; + + uvc_device_t **list; + uvc_device_t *test_dev; + int dev_idx; + int found_dev; + + UVC_ENTER(); + + ret = uvc_get_device_list(ctx, &list); + + if (ret != UVC_SUCCESS) { + UVC_EXIT(ret); + return ret; + } + + dev_idx = 0; + found_dev = 0; + + while (!found_dev && (test_dev = list[dev_idx++]) != NULL) { + uvc_device_descriptor_t *desc; + + if (uvc_get_device_descriptor(test_dev, &desc) != UVC_SUCCESS) + continue; + + if ((!vid || desc->idVendor == vid) + && (!pid || desc->idProduct == pid) + && (!sn || (desc->serialNumber && !strcmp(desc->serialNumber, sn)))) + found_dev = 1; + + uvc_free_device_descriptor(desc); + } + + if (found_dev) + uvc_ref_device(test_dev); + + uvc_free_device_list(list, 1); + + if (found_dev) { + *dev = test_dev; + UVC_EXIT(UVC_SUCCESS); + return UVC_SUCCESS; + } else { + UVC_EXIT(UVC_ERROR_NO_DEVICE); + return UVC_ERROR_NO_DEVICE; + } +} + +/** @brief Get the number of the bus to which the device is attached + * @ingroup device + */ +uint8_t uvc_get_bus_number(uvc_device_t *dev) { + return libusb_get_bus_number(dev->usb_dev); +} + +/** @brief Get the number assigned to the device within its bus + * @ingroup device + */ +uint8_t uvc_get_device_address(uvc_device_t *dev) { + return libusb_get_device_address(dev->usb_dev); +} + + +/** @brief Open a UVC device, defaulting to the first interface found + * @ingroup device + * + * @param dev Device to open + * @param[out] devh Handle on opened device + * @return Error opening device or SUCCESS + */ + +uvc_error_t uvc_open( + uvc_device_t *dev, + uvc_device_handle_t **devh) { + return uvc_open2(dev, devh, 0); +} + +/** @brief Open a UVC device, specifying the camera number, zero-based + * @ingroup device + * + * @param dev Device to open + * @param [out] devh Handle on opened device + * @param camera_number Camera to open + * @return Error opening device or SUCCESS + */ + +uvc_error_t uvc_open2( + uvc_device_t *dev, + uvc_device_handle_t **devh, + int camera_number) { + uvc_error_t ret; + struct libusb_device_handle *usb_devh; + uvc_device_handle_t *internal_devh; + struct libusb_device_descriptor desc; + + UVC_ENTER(); + + ret = libusb_open(dev->usb_dev, &usb_devh); + UVC_DEBUG("libusb_open() = %d", ret); + + if (ret != UVC_SUCCESS) { + UVC_EXIT(ret); + return ret; + } + + uvc_ref_device(dev); + + internal_devh = calloc(1, sizeof(*internal_devh)); + internal_devh->dev = dev; + internal_devh->usb_devh = usb_devh; + + ret = uvc_get_device_info2(dev, &(internal_devh->info), camera_number); + + if (ret != UVC_SUCCESS) + goto fail; + + UVC_DEBUG("claiming control interface %d", internal_devh->info->ctrl_if.bInterfaceNumber); + ret = uvc_claim_if(internal_devh, internal_devh->info->ctrl_if.bInterfaceNumber); + if (ret != UVC_SUCCESS) + goto fail; + + libusb_get_device_descriptor(dev->usb_dev, &desc); + internal_devh->is_isight = (desc.idVendor == 0x05ac && desc.idProduct == 0x8501); + + if (internal_devh->info->ctrl_if.bEndpointAddress) { + internal_devh->status_xfer = libusb_alloc_transfer(0); + if (!internal_devh->status_xfer) { + ret = UVC_ERROR_NO_MEM; + goto fail; + } + + libusb_fill_interrupt_transfer(internal_devh->status_xfer, + usb_devh, + internal_devh->info->ctrl_if.bEndpointAddress, + internal_devh->status_buf, + sizeof(internal_devh->status_buf), + _uvc_status_callback, + internal_devh, + 0); + ret = libusb_submit_transfer(internal_devh->status_xfer); + UVC_DEBUG("libusb_submit_transfer() = %d", ret); + + if (ret) { + fprintf(stderr, + "uvc: device has a status interrupt endpoint, but unable to read from it\n"); + goto fail; + } + } + + if (dev->ctx->own_usb_ctx && dev->ctx->open_devices == NULL) { + /* Since this is our first device, we need to spawn the event handler thread */ + uvc_start_handler_thread(dev->ctx); + } + + DL_APPEND(dev->ctx->open_devices, internal_devh); + *devh = internal_devh; + + UVC_EXIT(ret); + + return ret; + + fail: + if ( internal_devh->info ) { + uvc_release_if(internal_devh, internal_devh->info->ctrl_if.bInterfaceNumber); + } + libusb_close(usb_devh); + uvc_unref_device(dev); + uvc_free_devh(internal_devh); + + UVC_EXIT(ret); + + return ret; +} + +/** + * @internal + * @brief Parses the complete device descriptor for a device. Defaults to using the first camera interface found on a device. + * @ingroup device + * @note Free *info with uvc_free_device_info when you're done + * + * @param dev Device to parse descriptor for + * @param info Where to store a pointer to the new info struct + * + */ +uvc_error_t uvc_get_device_info(uvc_device_t *dev, + uvc_device_info_t **info) { + return uvc_get_device_info2(dev, info, 0); +} + +/** + * @internal + * @brief Parses the complete device descriptor for a device + * @ingroup device + * @note Free *info with uvc_free_device_info when you're done + * + * @param dev Device to parse descriptor for + * @param info Where to store a pointer to the new info struct + * @param camera_number Index of the camera to open, 0-based + */ + +uvc_error_t uvc_get_device_info2(uvc_device_t *dev, + uvc_device_info_t **info, + int camera_number) { + uvc_error_t ret; + uvc_device_info_t *internal_info; + + UVC_ENTER(); + + internal_info = calloc(1, sizeof(*internal_info)); + if (!internal_info) { + UVC_EXIT(UVC_ERROR_NO_MEM); + return UVC_ERROR_NO_MEM; + } + + if (libusb_get_config_descriptor(dev->usb_dev, + 0, + &(internal_info->config)) != 0) { + free(internal_info); + UVC_EXIT(UVC_ERROR_IO); + return UVC_ERROR_IO; + } + + if (camera_number*2 > internal_info->config->bNumInterfaces) + { + free(internal_info); + UVC_EXIT(UVC_ERROR_NO_DEVICE); + return UVC_ERROR_NO_DEVICE; + } + + //Which camera interface to pick up + internal_info->camera_number = camera_number; + + + ret = uvc_scan_control(dev, internal_info); + if (ret != UVC_SUCCESS) { + uvc_free_device_info(internal_info); + UVC_EXIT(ret); + return ret; + } + + *info = internal_info; + + UVC_EXIT(ret); + return ret; +} + +/** + * @internal + * @brief Frees the device descriptor for a device + * @ingroup device + * + * @param info Which device info block to free + */ +void uvc_free_device_info(uvc_device_info_t *info) { + uvc_input_terminal_t *input_term, *input_term_tmp; + uvc_processing_unit_t *proc_unit, *proc_unit_tmp; + uvc_extension_unit_t *ext_unit, *ext_unit_tmp; + + uvc_streaming_interface_t *stream_if, *stream_if_tmp; + uvc_format_desc_t *format, *format_tmp; + uvc_frame_desc_t *frame, *frame_tmp; + + UVC_ENTER(); + + DL_FOREACH_SAFE(info->ctrl_if.input_term_descs, input_term, input_term_tmp) { + DL_DELETE(info->ctrl_if.input_term_descs, input_term); + free(input_term); + } + + DL_FOREACH_SAFE(info->ctrl_if.processing_unit_descs, proc_unit, proc_unit_tmp) { + DL_DELETE(info->ctrl_if.processing_unit_descs, proc_unit); + free(proc_unit); + } + + DL_FOREACH_SAFE(info->ctrl_if.extension_unit_descs, ext_unit, ext_unit_tmp) { + DL_DELETE(info->ctrl_if.extension_unit_descs, ext_unit); + free(ext_unit); + } + + DL_FOREACH_SAFE(info->stream_ifs, stream_if, stream_if_tmp) { + DL_FOREACH_SAFE(stream_if->format_descs, format, format_tmp) { + DL_FOREACH_SAFE(format->frame_descs, frame, frame_tmp) { + if (frame->intervals) + free(frame->intervals); + + DL_DELETE(format->frame_descs, frame); + free(frame); + } + + DL_DELETE(stream_if->format_descs, format); + free(format); + } + + DL_DELETE(info->stream_ifs, stream_if); + free(stream_if); + } + + if (info->config) + libusb_free_config_descriptor(info->config); + + free(info); + + UVC_EXIT_VOID(); +} + +/** + * @brief Get a descriptor that contains the general information about + * a device + * @ingroup device + * + * Free *desc with uvc_free_device_descriptor when you're done. + * + * @param dev Device to fetch information about + * @param[out] desc Descriptor structure + * @return Error if unable to fetch information, else SUCCESS + */ +uvc_error_t uvc_get_device_descriptor( + uvc_device_t *dev, + uvc_device_descriptor_t **desc) { + uvc_device_descriptor_t *desc_internal; + struct libusb_device_descriptor usb_desc; + struct libusb_device_handle *usb_devh; + uvc_error_t ret; + + UVC_ENTER(); + + ret = libusb_get_device_descriptor(dev->usb_dev, &usb_desc); + + if (ret != UVC_SUCCESS) { + UVC_EXIT(ret); + return ret; + } + + desc_internal = calloc(1, sizeof(*desc_internal)); + desc_internal->idVendor = usb_desc.idVendor; + desc_internal->idProduct = usb_desc.idProduct; + + if (libusb_open(dev->usb_dev, &usb_devh) == 0) { + unsigned char buf[64]; + + int bytes = libusb_get_string_descriptor_ascii( + usb_devh, usb_desc.iSerialNumber, buf, sizeof(buf)); + + if (bytes > 0) + desc_internal->serialNumber = strdup((const char*) buf); + + bytes = libusb_get_string_descriptor_ascii( + usb_devh, usb_desc.iManufacturer, buf, sizeof(buf)); + + if (bytes > 0) + desc_internal->manufacturer = strdup((const char*) buf); + + bytes = libusb_get_string_descriptor_ascii( + usb_devh, usb_desc.iProduct, buf, sizeof(buf)); + + if (bytes > 0) + desc_internal->product = strdup((const char*) buf); + + libusb_close(usb_devh); + } else { + UVC_DEBUG("can't open device %04x:%04x, not fetching serial etc.", + usb_desc.idVendor, usb_desc.idProduct); + } + + *desc = desc_internal; + + UVC_EXIT(ret); + return ret; +} + +/** + * @brief Frees a device descriptor created with uvc_get_device_descriptor + * @ingroup device + * + * @param desc Descriptor to free + */ +void uvc_free_device_descriptor( + uvc_device_descriptor_t *desc) { + UVC_ENTER(); + + if (desc->serialNumber) + free((void*) desc->serialNumber); + + if (desc->manufacturer) + free((void*) desc->manufacturer); + + if (desc->product) + free((void*) desc->product); + + free(desc); + + UVC_EXIT_VOID(); +} + +/** + * @brief Get a list of the UVC devices attached to the system + * @ingroup device + * + * @note Free the list with uvc_free_device_list when you're done. + * + * @param ctx UVC context in which to list devices + * @param list List of uvc_device structures + * @return Error if unable to list devices, else SUCCESS + */ +uvc_error_t uvc_get_device_list( + uvc_context_t *ctx, + uvc_device_t ***list) { + uvc_error_t ret; + struct libusb_device **usb_dev_list; + struct libusb_device *usb_dev; + int num_usb_devices; + + uvc_device_t **list_internal; + int num_uvc_devices; + + /* per device */ + int dev_idx; + struct libusb_device_handle *usb_devh; + struct libusb_config_descriptor *config; + struct libusb_device_descriptor desc; + uint8_t got_interface; + + /* per interface */ + int interface_idx; + const struct libusb_interface *interface; + + /* per altsetting */ + int altsetting_idx; + const struct libusb_interface_descriptor *if_desc; + + UVC_ENTER(); + + num_usb_devices = libusb_get_device_list(ctx->usb_ctx, &usb_dev_list); + + if (num_usb_devices < 0) { + UVC_EXIT(UVC_ERROR_IO); + return UVC_ERROR_IO; + } + + list_internal = malloc(sizeof(*list_internal)); + *list_internal = NULL; + + num_uvc_devices = 0; + dev_idx = -1; + + while ((usb_dev = usb_dev_list[++dev_idx]) != NULL) { + usb_devh = NULL; + got_interface = 0; + + if (libusb_get_config_descriptor(usb_dev, 0, &config) != 0) + continue; + + if ( libusb_get_device_descriptor ( usb_dev, &desc ) != LIBUSB_SUCCESS ) + continue; + + // Special case for Imaging Source cameras + if ( 0x199e == desc.idVendor && 0x8101 == desc.idProduct ) { + got_interface = 1; + } else { + + for (interface_idx = 0; + !got_interface && interface_idx < config->bNumInterfaces; + ++interface_idx) { + interface = &config->interface[interface_idx]; + + for (altsetting_idx = 0; + !got_interface && altsetting_idx < interface->num_altsetting; + ++altsetting_idx) { + if_desc = &interface->altsetting[altsetting_idx]; + + /* Video, Streaming */ + if (if_desc->bInterfaceClass == 14 && if_desc->bInterfaceSubClass == 2) { + got_interface = 1; + } + } + } + } + + libusb_free_config_descriptor(config); + + if (got_interface) { + uvc_device_t *uvc_dev = malloc(sizeof(*uvc_dev)); + uvc_dev->ctx = ctx; + uvc_dev->ref = 0; + uvc_dev->usb_dev = usb_dev; + uvc_ref_device(uvc_dev); + + num_uvc_devices++; + list_internal = realloc(list_internal, (num_uvc_devices + 1) * sizeof(*list_internal)); + + list_internal[num_uvc_devices - 1] = uvc_dev; + list_internal[num_uvc_devices] = NULL; + + UVC_DEBUG(" UVC: %d", dev_idx); + } else { + UVC_DEBUG("non-UVC: %d", dev_idx); + } + } + + libusb_free_device_list(usb_dev_list, 1); + + *list = list_internal; + + UVC_EXIT(UVC_SUCCESS); + return UVC_SUCCESS; +} + +/** + * @brief Frees a list of device structures created with uvc_get_device_list. + * @ingroup device + * + * @param list Device list to free + * @param unref_devices Decrement the reference counter for each device + * in the list, and destroy any entries that end up with zero references + */ +void uvc_free_device_list(uvc_device_t **list, uint8_t unref_devices) { + uvc_device_t *dev; + int dev_idx = 0; + + UVC_ENTER(); + + if (unref_devices) { + while ((dev = list[dev_idx++]) != NULL) { + uvc_unref_device(dev); + } + } + + free(list); + + UVC_EXIT_VOID(); +} + +/** + * @brief Get the uvc_device_t corresponding to an open device + * @ingroup device + * + * @note Unref the uvc_device_t when you're done with it + * + * @param devh Device handle to an open UVC device + */ +uvc_device_t *uvc_get_device(uvc_device_handle_t *devh) { + uvc_ref_device(devh->dev); + return devh->dev; +} + +/** + * @brief Get the underlying libusb device handle for an open device + * @ingroup device + * + * This can be used to access other interfaces on the same device, e.g. + * a webcam microphone. + * + * @note The libusb device handle is only valid while the UVC device is open; + * it will be invalidated upon calling uvc_close. + * + * @param devh UVC device handle to an open device + */ +libusb_device_handle *uvc_get_libusb_handle(uvc_device_handle_t *devh) { + return devh->usb_devh; +} + +/** + * @brief Get input terminal descriptors for the open device. + * + * @note Do not modify the returned structure. + * @note The returned structure is part of a linked list. Iterate through + * it by using the 'next' pointers. + * + * @param devh Device handle to an open UVC device + */ +const uvc_input_terminal_t *uvc_get_input_terminals(uvc_device_handle_t *devh) { + return devh->info->ctrl_if.input_term_descs; +} + +/** + * @brief Get output terminal descriptors for the open device. + * + * @note Do not modify the returned structure. + * @note The returned structure is part of a linked list. Iterate through + * it by using the 'next' pointers. + * + * @param devh Device handle to an open UVC device + */ +const uvc_output_terminal_t *uvc_get_output_terminals(uvc_device_handle_t *devh) { + return NULL; /* @todo */ +} + +/** + * @brief Get processing unit descriptors for the open device. + * + * @note Do not modify the returned structure. + * @note The returned structure is part of a linked list. Iterate through + * it by using the 'next' pointers. + * + * @param devh Device handle to an open UVC device + */ +const uvc_processing_unit_t *uvc_get_processing_units(uvc_device_handle_t *devh) { + return devh->info->ctrl_if.processing_unit_descs; +} + +/** + * @brief Get extension unit descriptors for the open device. + * + * @note Do not modify the returned structure. + * @note The returned structure is part of a linked list. Iterate through + * it by using the 'next' pointers. + * + * @param devh Device handle to an open UVC device + */ +const uvc_extension_unit_t *uvc_get_extension_units(uvc_device_handle_t *devh) { + return devh->info->ctrl_if.extension_unit_descs; +} + +/** + * @brief Increment the reference count for a device + * @ingroup device + * + * @param dev Device to reference + */ +void uvc_ref_device(uvc_device_t *dev) { + UVC_ENTER(); + + dev->ref++; + libusb_ref_device(dev->usb_dev); + + UVC_EXIT_VOID(); +} + +/** + * @brief Decrement the reference count for a device + * @ingropu device + * @note If the count reaches zero, the device will be discarded + * + * @param dev Device to unreference + */ +void uvc_unref_device(uvc_device_t *dev) { + UVC_ENTER(); + + libusb_unref_device(dev->usb_dev); + dev->ref--; + + if (dev->ref == 0) + free(dev); + + UVC_EXIT_VOID(); +} + +/** @internal + * Claim a UVC interface, detaching the kernel driver if necessary. + * @ingroup device + * + * @param devh UVC device handle + * @param idx UVC interface index + */ +uvc_error_t uvc_claim_if(uvc_device_handle_t *devh, int idx) { + int ret; + + UVC_ENTER(); + + /* Tell libusb to detach any active kernel drivers. libusb will keep track of whether + * it found a kernel driver for this interface. */ + ret = libusb_detach_kernel_driver(devh->usb_devh, idx); + + if (ret == UVC_SUCCESS || ret == LIBUSB_ERROR_NOT_FOUND || ret == LIBUSB_ERROR_NOT_SUPPORTED) { + UVC_DEBUG("claiming interface %d", idx); + ret = libusb_claim_interface(devh->usb_devh, idx); + } else { + UVC_DEBUG("not claiming interface %d: unable to detach kernel driver (%s)", + idx, uvc_strerror(ret)); + } + + UVC_EXIT(ret); + return ret; +} + +/** @internal + * Release a UVC interface. + * @ingroup device + * + * @param devh UVC device handle + * @param idx UVC interface index + */ +uvc_error_t uvc_release_if(uvc_device_handle_t *devh, int idx) { + int ret; + + UVC_ENTER(); + UVC_DEBUG("releasing interface %d", idx); + /* libusb_release_interface *should* reset the alternate setting to the first available, + but sometimes (e.g. on Darwin) it doesn't. Thus, we do it explicitly here. + This is needed to de-initialize certain cameras. */ + libusb_set_interface_alt_setting(devh->usb_devh, idx, 0); + ret = libusb_release_interface(devh->usb_devh, idx); + + if (UVC_SUCCESS == ret) { + /* Reattach any kernel drivers that were disabled when we claimed this interface */ + ret = libusb_attach_kernel_driver(devh->usb_devh, idx); + + if (ret == UVC_SUCCESS) { + UVC_DEBUG("reattached kernel driver to interface %d", idx); + } else if (ret == LIBUSB_ERROR_NOT_FOUND || ret == LIBUSB_ERROR_NOT_SUPPORTED) { + ret = UVC_SUCCESS; /* NOT_FOUND and NOT_SUPPORTED are OK: nothing to do */ + } else { + UVC_DEBUG("error reattaching kernel driver to interface %d: %s", + idx, uvc_strerror(ret)); + } + } + + UVC_EXIT(ret); + return ret; +} + +/** @internal + * Find a device's VideoControl interface and process its descriptor + * @ingroup device + */ +uvc_error_t uvc_scan_control(uvc_device_t *dev, uvc_device_info_t *info) { + const struct libusb_interface_descriptor *if_desc; + uvc_error_t parse_ret, ret; + int interface_idx; + const unsigned char *buffer; + size_t buffer_left, block_size; + + UVC_ENTER(); + + ret = UVC_SUCCESS; + if_desc = NULL; + + //Start looking for the control interface at the given camera number + + for (interface_idx = info->camera_number*2; interface_idx < info->config->bNumInterfaces; ++interface_idx) { + if_desc = &info->config->interface[interface_idx].altsetting[0]; + + if (if_desc->bInterfaceClass == 14 && if_desc->bInterfaceSubClass == 1) // Video, Control + break; + + // Another TIS camera hack. + if ( if_desc->bInterfaceClass == 255 && if_desc->bInterfaceSubClass == 1 ) { + uvc_device_descriptor_t* dev_desc; + int haveTISCamera = 0; + uvc_get_device_descriptor ( dev, &dev_desc ); + if ( dev_desc->idVendor == 0x199e && dev_desc->idProduct == 0x8101 ) { + haveTISCamera = 1; + } + uvc_free_device_descriptor ( dev_desc ); + if ( haveTISCamera ) { + break; + } + } + + if_desc = NULL; + } + + if (if_desc == NULL) { + UVC_EXIT(UVC_ERROR_INVALID_DEVICE); + return UVC_ERROR_INVALID_DEVICE; + } + + info->ctrl_if.bInterfaceNumber = interface_idx; + if (if_desc->bNumEndpoints != 0) { + info->ctrl_if.bEndpointAddress = if_desc->endpoint[0].bEndpointAddress; + } + + buffer = if_desc->extra; + buffer_left = if_desc->extra_length; + + while (buffer_left >= 3) { // parseX needs to see buf[0,2] = length,type + block_size = buffer[0]; + parse_ret = uvc_parse_vc(dev, info, buffer, block_size); + + if (parse_ret != UVC_SUCCESS) { + ret = parse_ret; + break; + } + + buffer_left -= block_size; + buffer += block_size; + } + + UVC_EXIT(ret); + return ret; +} + +/** @internal + * @brief Parse a VideoControl header. + * @ingroup device + */ +uvc_error_t uvc_parse_vc_header(uvc_device_t *dev, + uvc_device_info_t *info, + const unsigned char *block, size_t block_size) { + size_t i; + uvc_error_t scan_ret, ret = UVC_SUCCESS; + + UVC_ENTER(); + + /* + int uvc_version; + uvc_version = (block[4] >> 4) * 1000 + (block[4] & 0x0f) * 100 + + (block[3] >> 4) * 10 + (block[3] & 0x0f); + */ + + info->ctrl_if.bcdUVC = SW_TO_SHORT(&block[3]); + + switch (info->ctrl_if.bcdUVC) { + case 0x0100: + case 0x010a: + case 0x0110: + break; + default: + UVC_EXIT(UVC_ERROR_NOT_SUPPORTED); + return UVC_ERROR_NOT_SUPPORTED; + } + + for (i = 12; i < block_size; ++i) { + scan_ret = uvc_scan_streaming(dev, info, block[i]); + if (scan_ret != UVC_SUCCESS) { + ret = scan_ret; + break; + } + } + + UVC_EXIT(ret); + return ret; +} + +/** @internal + * @brief Parse a VideoControl input terminal. + * @ingroup device + */ +uvc_error_t uvc_parse_vc_input_terminal(uvc_device_t *dev, + uvc_device_info_t *info, + const unsigned char *block, size_t block_size) { + uvc_input_terminal_t *term; + size_t i; + + UVC_ENTER(); + + /* only supporting camera-type input terminals */ + if (SW_TO_SHORT(&block[4]) != UVC_ITT_CAMERA) { + UVC_EXIT(UVC_SUCCESS); + return UVC_SUCCESS; + } + + term = calloc(1, sizeof(*term)); + + term->bTerminalID = block[3]; + term->wTerminalType = SW_TO_SHORT(&block[4]); + term->wObjectiveFocalLengthMin = SW_TO_SHORT(&block[8]); + term->wObjectiveFocalLengthMax = SW_TO_SHORT(&block[10]); + term->wOcularFocalLength = SW_TO_SHORT(&block[12]); + + for (i = 14 + block[14]; i >= 15; --i) + term->bmControls = block[i] + (term->bmControls << 8); + + DL_APPEND(info->ctrl_if.input_term_descs, term); + + UVC_EXIT(UVC_SUCCESS); + return UVC_SUCCESS; +} + +/** @internal + * @brief Parse a VideoControl processing unit. + * @ingroup device + */ +uvc_error_t uvc_parse_vc_processing_unit(uvc_device_t *dev, + uvc_device_info_t *info, + const unsigned char *block, size_t block_size) { + uvc_processing_unit_t *unit; + size_t i; + + UVC_ENTER(); + + unit = calloc(1, sizeof(*unit)); + unit->bUnitID = block[3]; + unit->bSourceID = block[4]; + + for (i = 7 + block[7]; i >= 8; --i) + unit->bmControls = block[i] + (unit->bmControls << 8); + + DL_APPEND(info->ctrl_if.processing_unit_descs, unit); + + UVC_EXIT(UVC_SUCCESS); + return UVC_SUCCESS; +} + +/** @internal + * @brief Parse a VideoControl extension unit. + * @ingroup device + */ +uvc_error_t uvc_parse_vc_extension_unit(uvc_device_t *dev, + uvc_device_info_t *info, + const unsigned char *block, size_t block_size) { + uvc_extension_unit_t *unit = calloc(1, sizeof(*unit)); + const uint8_t *start_of_controls; + int size_of_controls, num_in_pins; + int i; + + UVC_ENTER(); + + unit->bUnitID = block[3]; + memcpy(unit->guidExtensionCode, &block[4], 16); + + num_in_pins = block[21]; + size_of_controls = block[22 + num_in_pins]; + start_of_controls = &block[23 + num_in_pins]; + + for (i = size_of_controls - 1; i >= 0; --i) + unit->bmControls = start_of_controls[i] + (unit->bmControls << 8); + + DL_APPEND(info->ctrl_if.extension_unit_descs, unit); + + UVC_EXIT(UVC_SUCCESS); + return UVC_SUCCESS; +} + +/** @internal + * Process a single VideoControl descriptor block + * @ingroup device + */ +uvc_error_t uvc_parse_vc( + uvc_device_t *dev, + uvc_device_info_t *info, + const unsigned char *block, size_t block_size) { + int descriptor_subtype; + uvc_error_t ret = UVC_SUCCESS; + + UVC_ENTER(); + + if (block[1] != 36) { // not a CS_INTERFACE descriptor?? + UVC_EXIT(UVC_SUCCESS); + return UVC_SUCCESS; // UVC_ERROR_INVALID_DEVICE; + } + + descriptor_subtype = block[2]; + + switch (descriptor_subtype) { + case UVC_VC_HEADER: + ret = uvc_parse_vc_header(dev, info, block, block_size); + break; + case UVC_VC_INPUT_TERMINAL: + ret = uvc_parse_vc_input_terminal(dev, info, block, block_size); + break; + case UVC_VC_OUTPUT_TERMINAL: + break; + case UVC_VC_SELECTOR_UNIT: + break; + case UVC_VC_PROCESSING_UNIT: + ret = uvc_parse_vc_processing_unit(dev, info, block, block_size); + break; + case UVC_VC_EXTENSION_UNIT: + ret = uvc_parse_vc_extension_unit(dev, info, block, block_size); + break; + default: + ret = UVC_ERROR_INVALID_DEVICE; + } + + UVC_EXIT(ret); + return ret; +} + +/** @internal + * Process a VideoStreaming interface + * @ingroup device + */ +uvc_error_t uvc_scan_streaming(uvc_device_t *dev, + uvc_device_info_t *info, + int interface_idx) { + const struct libusb_interface_descriptor *if_desc; + const unsigned char *buffer; + size_t buffer_left, block_size; + uvc_error_t ret, parse_ret; + uvc_streaming_interface_t *stream_if; + + UVC_ENTER(); + + ret = UVC_SUCCESS; + + if_desc = &(info->config->interface[interface_idx].altsetting[0]); + buffer = if_desc->extra; + buffer_left = if_desc->extra_length; + + stream_if = calloc(1, sizeof(*stream_if)); + stream_if->parent = info; + stream_if->bInterfaceNumber = if_desc->bInterfaceNumber; + DL_APPEND(info->stream_ifs, stream_if); + + while (buffer_left >= 3) { + block_size = buffer[0]; + parse_ret = uvc_parse_vs(dev, info, stream_if, buffer, block_size); + + if (parse_ret != UVC_SUCCESS) { + ret = parse_ret; + break; + } + + buffer_left -= block_size; + buffer += block_size; + } + + UVC_EXIT(ret); + return ret; +} + +/** @internal + * @brief Parse a VideoStreaming header block. + * @ingroup device + */ +uvc_error_t uvc_parse_vs_input_header(uvc_streaming_interface_t *stream_if, + const unsigned char *block, + size_t block_size) { + UVC_ENTER(); + + stream_if->bEndpointAddress = block[6] & 0x8f; + stream_if->bTerminalLink = block[8]; + + UVC_EXIT(UVC_SUCCESS); + return UVC_SUCCESS; +} + +/** @internal + * @brief Parse a VideoStreaming uncompressed format block. + * @ingroup device + */ +uvc_error_t uvc_parse_vs_format_uncompressed(uvc_streaming_interface_t *stream_if, + const unsigned char *block, + size_t block_size) { + UVC_ENTER(); + + uvc_format_desc_t *format = calloc(1, sizeof(*format)); + + format->parent = stream_if; + format->bDescriptorSubtype = block[2]; + format->bFormatIndex = block[3]; + //format->bmCapabilities = block[4]; + //format->bmFlags = block[5]; + memcpy(format->guidFormat, &block[5], 16); + format->bBitsPerPixel = block[21]; + format->bDefaultFrameIndex = block[22]; + format->bAspectRatioX = block[23]; + format->bAspectRatioY = block[24]; + format->bmInterlaceFlags = block[25]; + format->bCopyProtect = block[26]; + + DL_APPEND(stream_if->format_descs, format); + + UVC_EXIT(UVC_SUCCESS); + return UVC_SUCCESS; +} + +/** @internal + * @brief Parse a VideoStreaming frame format block. + * @ingroup device + */ +uvc_error_t uvc_parse_vs_frame_format(uvc_streaming_interface_t *stream_if, + const unsigned char *block, + size_t block_size) { + UVC_ENTER(); + + uvc_format_desc_t *format = calloc(1, sizeof(*format)); + + format->parent = stream_if; + format->bDescriptorSubtype = block[2]; + format->bFormatIndex = block[3]; + format->bNumFrameDescriptors = block[4]; + memcpy(format->guidFormat, &block[5], 16); + format->bBitsPerPixel = block[21]; + format->bDefaultFrameIndex = block[22]; + format->bAspectRatioX = block[23]; + format->bAspectRatioY = block[24]; + format->bmInterlaceFlags = block[25]; + format->bCopyProtect = block[26]; + format->bVariableSize = block[27]; + + DL_APPEND(stream_if->format_descs, format); + + UVC_EXIT(UVC_SUCCESS); + return UVC_SUCCESS; +} + +/** @internal + * @brief Parse a VideoStreaming MJPEG format block. + * @ingroup device + */ +uvc_error_t uvc_parse_vs_format_mjpeg(uvc_streaming_interface_t *stream_if, + const unsigned char *block, + size_t block_size) { + UVC_ENTER(); + + uvc_format_desc_t *format = calloc(1, sizeof(*format)); + + format->parent = stream_if; + format->bDescriptorSubtype = block[2]; + format->bFormatIndex = block[3]; + memcpy(format->fourccFormat, "MJPG", 4); + format->bmFlags = block[5]; + format->bBitsPerPixel = 0; + format->bDefaultFrameIndex = block[6]; + format->bAspectRatioX = block[7]; + format->bAspectRatioY = block[8]; + format->bmInterlaceFlags = block[9]; + format->bCopyProtect = block[10]; + + DL_APPEND(stream_if->format_descs, format); + + UVC_EXIT(UVC_SUCCESS); + return UVC_SUCCESS; +} + +/** @internal + * @brief Parse a VideoStreaming uncompressed frame block. + * @ingroup device + */ +uvc_error_t uvc_parse_vs_frame_frame(uvc_streaming_interface_t *stream_if, + const unsigned char *block, + size_t block_size) { + uvc_format_desc_t *format; + uvc_frame_desc_t *frame; + + const unsigned char *p; + int i; + + UVC_ENTER(); + + format = stream_if->format_descs->prev; + frame = calloc(1, sizeof(*frame)); + + frame->parent = format; + + frame->bDescriptorSubtype = block[2]; + frame->bFrameIndex = block[3]; + frame->bmCapabilities = block[4]; + frame->wWidth = block[5] + (block[6] << 8); + frame->wHeight = block[7] + (block[8] << 8); + frame->dwMinBitRate = DW_TO_INT(&block[9]); + frame->dwMaxBitRate = DW_TO_INT(&block[13]); + frame->dwDefaultFrameInterval = DW_TO_INT(&block[17]); + frame->bFrameIntervalType = block[21]; + frame->dwBytesPerLine = DW_TO_INT(&block[22]); + + if (block[21] == 0) { + frame->dwMinFrameInterval = DW_TO_INT(&block[26]); + frame->dwMaxFrameInterval = DW_TO_INT(&block[30]); + frame->dwFrameIntervalStep = DW_TO_INT(&block[34]); + } else { + frame->intervals = calloc(block[21] + 1, sizeof(frame->intervals[0])); + p = &block[26]; + + for (i = 0; i < block[21]; ++i) { + frame->intervals[i] = DW_TO_INT(p); + p += 4; + } + frame->intervals[block[21]] = 0; + } + + DL_APPEND(format->frame_descs, frame); + + UVC_EXIT(UVC_SUCCESS); + return UVC_SUCCESS; +} + +/** @internal + * @brief Parse a VideoStreaming uncompressed frame block. + * @ingroup device + */ +uvc_error_t uvc_parse_vs_frame_uncompressed(uvc_streaming_interface_t *stream_if, + const unsigned char *block, + size_t block_size) { + uvc_format_desc_t *format; + uvc_frame_desc_t *frame; + + const unsigned char *p; + int i; + + UVC_ENTER(); + + format = stream_if->format_descs->prev; + frame = calloc(1, sizeof(*frame)); + + frame->parent = format; + + frame->bDescriptorSubtype = block[2]; + frame->bFrameIndex = block[3]; + frame->bmCapabilities = block[4]; + frame->wWidth = block[5] + (block[6] << 8); + frame->wHeight = block[7] + (block[8] << 8); + frame->dwMinBitRate = DW_TO_INT(&block[9]); + frame->dwMaxBitRate = DW_TO_INT(&block[13]); + frame->dwMaxVideoFrameBufferSize = DW_TO_INT(&block[17]); + frame->dwDefaultFrameInterval = DW_TO_INT(&block[21]); + frame->bFrameIntervalType = block[25]; + + if (block[25] == 0) { + frame->dwMinFrameInterval = DW_TO_INT(&block[26]); + frame->dwMaxFrameInterval = DW_TO_INT(&block[30]); + frame->dwFrameIntervalStep = DW_TO_INT(&block[34]); + } else { + frame->intervals = calloc(block[25] + 1, sizeof(frame->intervals[0])); + p = &block[26]; + + for (i = 0; i < block[25]; ++i) { + frame->intervals[i] = DW_TO_INT(p); + p += 4; + } + frame->intervals[block[25]] = 0; + } + + DL_APPEND(format->frame_descs, frame); + + UVC_EXIT(UVC_SUCCESS); + return UVC_SUCCESS; +} + +/** @internal + * Process a single VideoStreaming descriptor block + * @ingroup device + */ +uvc_error_t uvc_parse_vs( + uvc_device_t *dev, + uvc_device_info_t *info, + uvc_streaming_interface_t *stream_if, + const unsigned char *block, size_t block_size) { + uvc_error_t ret; + int descriptor_subtype; + + UVC_ENTER(); + + ret = UVC_SUCCESS; + descriptor_subtype = block[2]; + + switch (descriptor_subtype) { + case UVC_VS_INPUT_HEADER: + ret = uvc_parse_vs_input_header(stream_if, block, block_size); + break; + case UVC_VS_FORMAT_UNCOMPRESSED: + ret = uvc_parse_vs_format_uncompressed(stream_if, block, block_size); + break; + case UVC_VS_FORMAT_MJPEG: + ret = uvc_parse_vs_format_mjpeg(stream_if, block, block_size); + break; + case UVC_VS_FRAME_UNCOMPRESSED: + case UVC_VS_FRAME_MJPEG: + ret = uvc_parse_vs_frame_uncompressed(stream_if, block, block_size); + break; + case UVC_VS_FORMAT_FRAME_BASED: + ret = uvc_parse_vs_frame_format ( stream_if, block, block_size ); + break; + case UVC_VS_FRAME_FRAME_BASED: + ret = uvc_parse_vs_frame_frame ( stream_if, block, block_size ); + break; + default: + /** @todo handle JPEG and maybe still frames or even DV... */ + fprintf ( stderr, "unsupported descriptor subtype: %d\n", + descriptor_subtype ); + break; + } + + UVC_EXIT(ret); + return ret; +} + +/** @internal + * @brief Free memory associated with a UVC device + * @pre Streaming must be stopped, and threads must have died + */ +void uvc_free_devh(uvc_device_handle_t *devh) { + UVC_ENTER(); + + if (devh->info) + uvc_free_device_info(devh->info); + + if (devh->status_xfer) + libusb_free_transfer(devh->status_xfer); + + free(devh); + + UVC_EXIT_VOID(); +} + +/** @brief Close a device + * + * @ingroup device + * + * Ends any stream that's in progress. + * + * The device handle and frame structures will be invalidated. + */ +void uvc_close(uvc_device_handle_t *devh) { + UVC_ENTER(); + uvc_context_t *ctx = devh->dev->ctx; + + if (devh->streams) + uvc_stop_streaming(devh); + + uvc_release_if(devh, devh->info->ctrl_if.bInterfaceNumber); + + /* If we are managing the libusb context and this is the last open device, + * then we need to cancel the handler thread. When we call libusb_close, + * it'll cause a return from the thread's libusb_handle_events call, after + * which the handler thread will check the flag we set and then exit. */ + if (ctx->own_usb_ctx && ctx->open_devices == devh && devh->next == NULL) { + ctx->kill_handler_thread = 1; + libusb_close(devh->usb_devh); + pthread_join(ctx->handler_thread, NULL); + } else { + libusb_close(devh->usb_devh); + } + + DL_DELETE(ctx->open_devices, devh); + + uvc_unref_device(devh->dev); + + uvc_free_devh(devh); + + UVC_EXIT_VOID(); +} + +/** @internal + * @brief Get number of open devices + */ +size_t uvc_num_devices(uvc_context_t *ctx) { + size_t count = 0; + + uvc_device_handle_t *devh; + + UVC_ENTER(); + + DL_FOREACH(ctx->open_devices, devh) { + count++; + } + + UVC_EXIT((int) count); + return count; +} + +void uvc_process_status_xfer(uvc_device_handle_t *devh, struct libusb_transfer *transfer) { + enum uvc_status_class status_class; + uint8_t originator = 0, selector = 0, event = 0; + enum uvc_status_attribute attribute = UVC_STATUS_ATTRIBUTE_UNKNOWN; + void *data = NULL; + size_t data_len = 0; + + UVC_ENTER(); + + /* printf("Got transfer of aLen = %d\n", transfer->actual_length); */ + + if (transfer->actual_length < 4) { + UVC_DEBUG("Short read of status update (%d bytes)", transfer->actual_length); + UVC_EXIT_VOID(); + return; + } + + originator = transfer->buffer[1]; + + switch (transfer->buffer[0] & 0x0f) { + case 1: { /* VideoControl interface */ + int found_entity = 0; + struct uvc_input_terminal *input_terminal; + struct uvc_processing_unit *processing_unit; + + if (transfer->actual_length < 5) { + UVC_DEBUG("Short read of VideoControl status update (%d bytes)", + transfer->actual_length); + UVC_EXIT_VOID(); + return; + } + + event = transfer->buffer[2]; + selector = transfer->buffer[3]; + + if (originator == 0) { + UVC_DEBUG("Unhandled update from VC interface"); + UVC_EXIT_VOID(); + return; /* @todo VideoControl virtual entity interface updates */ + } + + if (event != 0) { + UVC_DEBUG("Unhandled VC event %d", (int) event); + UVC_EXIT_VOID(); + return; + } + + /* printf("bSelector: %d\n", selector); */ + + DL_FOREACH(devh->info->ctrl_if.input_term_descs, input_terminal) { + if (input_terminal->bTerminalID == originator) { + status_class = UVC_STATUS_CLASS_CONTROL_CAMERA; + found_entity = 1; + break; + } + } + + if (!found_entity) { + DL_FOREACH(devh->info->ctrl_if.processing_unit_descs, processing_unit) { + if (processing_unit->bUnitID == originator) { + status_class = UVC_STATUS_CLASS_CONTROL_PROCESSING; + found_entity = 1; + break; + } + } + } + + if (!found_entity) { + UVC_DEBUG("Got status update for unknown VideoControl entity %d", + (int) originator); + UVC_EXIT_VOID(); + return; + } + + attribute = transfer->buffer[4]; + data = transfer->buffer + 5; + data_len = transfer->actual_length - 5; + break; + } + case 2: /* VideoStreaming interface */ + UVC_DEBUG("Unhandled update from VideoStreaming interface"); + UVC_EXIT_VOID(); + return; /* @todo VideoStreaming updates */ + } + + UVC_DEBUG("Event: class=%d, event=%d, selector=%d, attribute=%d, data_len=%zd", + status_class, event, selector, attribute, data_len); + + if(devh->status_cb) { + UVC_DEBUG("Running user-supplied status callback"); + devh->status_cb(status_class, + event, + selector, + attribute, + data, data_len, + devh->status_user_ptr); + } + + UVC_EXIT_VOID(); +} + +/** @internal + * @brief Process asynchronous status updates from the device. + */ +void LIBUSB_CALL _uvc_status_callback(struct libusb_transfer *transfer) { + UVC_ENTER(); + + uvc_device_handle_t *devh = (uvc_device_handle_t *) transfer->user_data; + + switch (transfer->status) { + case LIBUSB_TRANSFER_ERROR: + case LIBUSB_TRANSFER_CANCELLED: + case LIBUSB_TRANSFER_NO_DEVICE: + UVC_DEBUG("Dimitri - not true - not processing/resubmitting, status = %d", transfer->status); + //break; + UVC_EXIT_VOID(); + return; + case LIBUSB_TRANSFER_COMPLETED: + uvc_process_status_xfer(devh, transfer); + break; + case LIBUSB_TRANSFER_TIMED_OUT: + case LIBUSB_TRANSFER_STALL: + case LIBUSB_TRANSFER_OVERFLOW: + UVC_DEBUG("retrying transfer, status = %d", transfer->status); + break; + } + + uvc_error_t ret = libusb_submit_transfer(transfer); + UVC_DEBUG("libusb_submit_transfer() = %d", ret); + + UVC_EXIT_VOID(); +} + +/** @brief Set a callback function to receive status updates + * + * @ingroup device + */ +void uvc_set_status_callback(uvc_device_handle_t *devh, + uvc_status_callback_t cb, + void *user_ptr) { + UVC_ENTER(); + + devh->status_cb = cb; + devh->status_user_ptr = user_ptr; + + UVC_EXIT_VOID(); +} + + +/** + * @brief Get format descriptions for the open device. + * + * @note Do not modify the returned structure. + * + * @param devh Device handle to an open UVC device + */ +const uvc_format_desc_t *uvc_get_format_descs(uvc_device_handle_t *devh) { + return devh->info->stream_ifs->format_descs; +} + diff --git a/third_party/libuvc/src/diag.c b/third_party/libuvc/src/diag.c new file mode 100644 index 0000000000..218f135da8 --- /dev/null +++ b/third_party/libuvc/src/diag.c @@ -0,0 +1,262 @@ +/********************************************************************* +* Software License Agreement (BSD License) +* +* Copyright (C) 2010-2012 Ken Tossell +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of the author nor other contributors may be +* used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*********************************************************************/ +/** + * @defgroup diag Diagnostics + * @brief Interpretation of devices, error codes and negotiated stream parameters + */ + +#include "libuvc/libuvc.h" +#include "libuvc/libuvc_internal.h" + +/** @internal */ +typedef struct _uvc_error_msg { + uvc_error_t err; + const char *msg; +} _uvc_error_msg_t; + +static const _uvc_error_msg_t uvc_error_msgs[] = { + {UVC_SUCCESS, "Success"}, + {UVC_ERROR_IO, "I/O error"}, + {UVC_ERROR_INVALID_PARAM, "Invalid parameter"}, + {UVC_ERROR_ACCESS, "Access denied"}, + {UVC_ERROR_NO_DEVICE, "No such device"}, + {UVC_ERROR_NOT_FOUND, "Not found"}, + {UVC_ERROR_BUSY, "Busy"}, + {UVC_ERROR_TIMEOUT, "Timeout"}, + {UVC_ERROR_OVERFLOW, "Overflow"}, + {UVC_ERROR_PIPE, "Pipe"}, + {UVC_ERROR_INTERRUPTED, "Interrupted"}, + {UVC_ERROR_NO_MEM, "Out of memory"}, + {UVC_ERROR_NOT_SUPPORTED, "Not supported"}, + {UVC_ERROR_INVALID_DEVICE, "Invalid device"}, + {UVC_ERROR_INVALID_MODE, "Invalid mode"}, + {UVC_ERROR_CALLBACK_EXISTS, "Callback exists"} +}; + +/** @brief Print a message explaining an error in the UVC driver + * @ingroup diag + * + * @param err UVC error code + * @param msg Optional custom message, prepended to output + */ +void uvc_perror(uvc_error_t err, const char *msg) { + if (msg && *msg) { + fputs(msg, stderr); + fputs(": ", stderr); + } + + fprintf(stderr, "%s (%d)\n", uvc_strerror(err), err); +} + +/** @brief Return a string explaining an error in the UVC driver + * @ingroup diag + * + * @param err UVC error code + * @return error message + */ +const char* uvc_strerror(uvc_error_t err) { + size_t idx; + + for (idx = 0; idx < sizeof(uvc_error_msgs) / sizeof(*uvc_error_msgs); ++idx) { + if (uvc_error_msgs[idx].err == err) { + return uvc_error_msgs[idx].msg; + } + } + + return "Unknown error"; +} + +/** @brief Print the values in a stream control block + * @ingroup diag + * + * @param devh UVC device + * @param stream Output stream (stderr if NULL) + */ +void uvc_print_stream_ctrl(uvc_stream_ctrl_t *ctrl, FILE *stream) { + if (stream == NULL) + stream = stderr; + + fprintf(stream, "bmHint: %04x\n", ctrl->bmHint); + fprintf(stream, "bFormatIndex: %d\n", ctrl->bFormatIndex); + fprintf(stream, "bFrameIndex: %d\n", ctrl->bFrameIndex); + fprintf(stream, "dwFrameInterval: %u\n", ctrl->dwFrameInterval); + fprintf(stream, "wKeyFrameRate: %d\n", ctrl->wKeyFrameRate); + fprintf(stream, "wPFrameRate: %d\n", ctrl->wPFrameRate); + fprintf(stream, "wCompQuality: %d\n", ctrl->wCompQuality); + fprintf(stream, "wCompWindowSize: %d\n", ctrl->wCompWindowSize); + fprintf(stream, "wDelay: %d\n", ctrl->wDelay); + fprintf(stream, "dwMaxVideoFrameSize: %u\n", ctrl->dwMaxVideoFrameSize); + fprintf(stream, "dwMaxPayloadTransferSize: %u\n", ctrl->dwMaxPayloadTransferSize); + fprintf(stream, "bInterfaceNumber: %d\n", ctrl->bInterfaceNumber); +} + +static const char *_uvc_name_for_format_subtype(uint8_t subtype) { + switch (subtype) { + case UVC_VS_FORMAT_UNCOMPRESSED: + return "UncompressedFormat"; + case UVC_VS_FORMAT_MJPEG: + return "MJPEGFormat"; + case UVC_VS_FORMAT_FRAME_BASED: + return "FrameFormat"; + default: + return "Unknown"; + } +} + +/** @brief Print camera capabilities and configuration. + * @ingroup diag + * + * @param devh UVC device + * @param stream Output stream (stderr if NULL) + */ +void uvc_print_diag(uvc_device_handle_t *devh, FILE *stream) { + if (stream == NULL) + stream = stderr; + + if (devh->info->ctrl_if.bcdUVC) { + uvc_streaming_interface_t *stream_if; + int stream_idx = 0; + + uvc_device_descriptor_t *desc; + uvc_get_device_descriptor(devh->dev, &desc); + + fprintf(stream, "DEVICE CONFIGURATION (%04x:%04x/%s) ---\n", + desc->idVendor, desc->idProduct, + desc->serialNumber ? desc->serialNumber : "[none]"); + + uvc_free_device_descriptor(desc); + + fprintf(stream, "Status: %s\n", devh->streams ? "streaming" : "idle"); + + fprintf(stream, "VideoControl:\n" + "\tbcdUVC: 0x%04x\n", + devh->info->ctrl_if.bcdUVC); + + DL_FOREACH(devh->info->stream_ifs, stream_if) { + uvc_format_desc_t *fmt_desc; + + ++stream_idx; + + fprintf(stream, "VideoStreaming(%d):\n" + "\tbEndpointAddress: %d\n\tFormats:\n", + stream_idx, stream_if->bEndpointAddress); + + DL_FOREACH(stream_if->format_descs, fmt_desc) { + uvc_frame_desc_t *frame_desc; + int i; + + switch (fmt_desc->bDescriptorSubtype) { + case UVC_VS_FORMAT_UNCOMPRESSED: + case UVC_VS_FORMAT_MJPEG: + case UVC_VS_FORMAT_FRAME_BASED: + fprintf(stream, + "\t\%s(%d)\n" + "\t\t bits per pixel: %d\n" + "\t\t GUID: ", + _uvc_name_for_format_subtype(fmt_desc->bDescriptorSubtype), + fmt_desc->bFormatIndex, + fmt_desc->bBitsPerPixel); + + for (i = 0; i < 16; ++i) + fprintf(stream, "%02x", fmt_desc->guidFormat[i]); + + fprintf(stream, " (%4s)\n", fmt_desc->fourccFormat ); + + fprintf(stream, + "\t\t default frame: %d\n" + "\t\t aspect ratio: %dx%d\n" + "\t\t interlace flags: %02x\n" + "\t\t copy protect: %02x\n", + fmt_desc->bDefaultFrameIndex, + fmt_desc->bAspectRatioX, + fmt_desc->bAspectRatioY, + fmt_desc->bmInterlaceFlags, + fmt_desc->bCopyProtect); + + DL_FOREACH(fmt_desc->frame_descs, frame_desc) { + uint32_t *interval_ptr; + + fprintf(stream, + "\t\t\tFrameDescriptor(%d)\n" + "\t\t\t capabilities: %02x\n" + "\t\t\t size: %dx%d\n" + "\t\t\t bit rate: %d-%d\n" + "\t\t\t max frame size: %d\n" + "\t\t\t default interval: 1/%d\n", + frame_desc->bFrameIndex, + frame_desc->bmCapabilities, + frame_desc->wWidth, + frame_desc->wHeight, + frame_desc->dwMinBitRate, + frame_desc->dwMaxBitRate, + frame_desc->dwMaxVideoFrameBufferSize, + 10000000 / frame_desc->dwDefaultFrameInterval); + if (frame_desc->intervals) { + for (interval_ptr = frame_desc->intervals; + *interval_ptr; + ++interval_ptr) { + fprintf(stream, + "\t\t\t interval[%d]: 1/%d\n", + (int) (interval_ptr - frame_desc->intervals), + 10000000 / *interval_ptr); + } + } else { + fprintf(stream, + "\t\t\t min interval[%d] = 1/%d\n" + "\t\t\t max interval[%d] = 1/%d\n", + frame_desc->dwMinFrameInterval, + 10000000 / frame_desc->dwMinFrameInterval, + frame_desc->dwMaxFrameInterval, + 10000000 / frame_desc->dwMaxFrameInterval); + if (frame_desc->dwFrameIntervalStep) + fprintf(stream, + "\t\t\t interval step[%d] = 1/%d\n", + frame_desc->dwFrameIntervalStep, + 10000000 / frame_desc->dwFrameIntervalStep); + } + } + break; + default: + fprintf(stream, "\t-UnknownFormat (%d)\n", + fmt_desc->bDescriptorSubtype ); + } + } + } + + fprintf(stream, "END DEVICE CONFIGURATION\n"); + } else { + fprintf(stream, "uvc_print_diag: Device not configured!\n"); + } +} + diff --git a/third_party/libuvc/src/example.c b/third_party/libuvc/src/example.c new file mode 100644 index 0000000000..6785cca28f --- /dev/null +++ b/third_party/libuvc/src/example.c @@ -0,0 +1,215 @@ +#include "libuvc/libuvc.h" +#include +#include +#include + +/* This callback function runs once per frame. Use it to perform any + * quick processing you need, or have it put the frame into your application's + * input queue. If this function takes too long, you'll start losing frames. */ +void cb_rgb(uvc_frame_t *frame, void *ptr) { + uvc_frame_t *bgr; + uvc_error_t ret; + + /* We'll convert the image from YUV/JPEG to BGR, so allocate space */ + bgr = uvc_allocate_frame(frame->width * frame->height * 3); + if (!bgr) { + printf("unable to allocate bgr frame!"); + return; + } + + /* Do the BGR conversion */ + ret = uvc_any2bgr(frame, bgr); + if (ret) { + uvc_perror(ret, "uvc_any2bgr"); + uvc_free_frame(bgr); + return; + } + + /* Call a user function: + * + * my_type *my_obj = (*my_type) ptr; + * my_user_function(ptr, bgr); + * my_other_function(ptr, bgr->data, bgr->width, bgr->height); + */ + + /* Call a C++ method: + * + * my_type *my_obj = (*my_type) ptr; + * my_obj->my_func(bgr); + */ + + /* Use opencv.highgui to display the image: */ + IplImage* cvImg; + cvImg = cvCreateImageHeader( + cvSize(bgr->width, bgr->height), + IPL_DEPTH_8U, + 3); + + cvSetData(cvImg, bgr->data, bgr->width * 3); + + cvNamedWindow("Test", CV_WINDOW_AUTOSIZE); + cvShowImage("Test", cvImg); + cvWaitKey(10); + + cvReleaseImageHeader(&cvImg); + + + uvc_free_frame(bgr); +} + +/*Convert a YUYV -advertised image to a gray16 image for OpenCV */ + +uvc_error_t uvc_yuyv2gray16(uvc_frame_t *in) { + if (in->frame_format != UVC_FRAME_FORMAT_YUYV) + return UVC_ERROR_INVALID_PARAM; + + in->frame_format = UVC_FRAME_FORMAT_GRAY16; + return UVC_SUCCESS; +} + + +//Callback for the 16-bit gray depthmap +void cb_gray(uvc_frame_t *frame, void *ptr) { + uvc_frame_t *gray; + uvc_error_t ret; + + gray = frame; + + gray->frame_format = UVC_FRAME_FORMAT_GRAY16; + + /* Use opencv.highgui to display the image: */ + IplImage* cvImg; + cvImg = cvCreateImageHeader( + cvSize(gray->width, gray->height), + IPL_DEPTH_16U, + 1); + + cvSetData(cvImg, gray->data, gray->width * 2); + + cvNamedWindow("Test Depth", CV_WINDOW_AUTOSIZE); + cvShowImage("Test Depth", cvImg); + cvWaitKey(10); + + cvReleaseImageHeader(&cvImg); +} + +int main(int argc, char **argv) { + uvc_context_t *ctx; + uvc_device_t *dev; + uvc_device_handle_t *devh_d; + uvc_device_handle_t *devh_rgb; + uvc_stream_ctrl_t ctrl_rgb; + uvc_stream_ctrl_t ctrl_d; + + uvc_error_t res; + + /* Initialize a UVC service context. Libuvc will set up its own libusb + * context. Replace NULL with a libusb_context pointer to run libuvc + * from an existing libusb context. */ + res = uvc_init(&ctx, NULL); + + if (res < 0) { + uvc_perror(res, "uvc_init"); + return res; + } + + puts("UVC initialized"); + + + /* Locates the first attached UVC device, stores in dev */ + res = uvc_find_device( + ctx, &dev, + 0x8086, 0x0a66, NULL); /* filter devices: vendor_id, product_id, "serial_num" */ + + if (res < 0) { + uvc_perror(res, "uvc_find_device"); /* no devices found */ + } else { + puts("Device found"); + + /* Try to open the device: requires exclusive access */ + res = uvc_open2(dev, &devh_rgb, 0); //Try to open camera 0 (RGB) + + + if (res < 0) { + uvc_perror(res, "uvc_open"); /* unable to open device */ + } else { + puts("RGB Device opened"); + + /* Print out a message containing all the information that libuvc + * knows about the device */ + uvc_print_diag(devh_rgb, stderr); + + + printf("Opening depth camera\n"); + res = uvc_open2(dev, &devh_d, 1); //Try to open camera 1 (RGB) + uvc_print_diag(devh_d, stderr); + + + /* Try to negotiate a 640x480 30 fps YUYV stream profile */ + res = uvc_get_stream_ctrl_format_size( + devh_rgb, &ctrl_rgb, /* result stored in ctrl */ + UVC_FRAME_FORMAT_YUYV, /* YUV 422, aka YUV 4:2:2. try _COMPRESSED */ + 640, 480, 30 /* width, height, fps */ + ); + /* Print out the result */ + uvc_print_stream_ctrl(&ctrl_rgb, stderr); + + /* Try to negotiate a 640x480 30 fps YUYV stream profile */ + res = uvc_get_stream_ctrl_format_size( + devh_d, &ctrl_d, /* result stored in ctrl */ + UVC_FRAME_FORMAT_YUYV, /* YUV 422, aka YUV 4:2:2. try _COMPRESSED */ + 640, 480, 30 /* width, height, fps */ + ); + + /* Print out the result */ + uvc_print_stream_ctrl(&ctrl_d, stderr); + + if (res < 0) { + uvc_perror(res, "get_mode"); /* device doesn't provide a matching stream */ + } else { + /* Start the video stream. The library will call user function cb: + * cb(frame, (void*) 12345) + */ + + + res = uvc_start_streaming(devh_rgb, &ctrl_rgb, cb_rgb, 12345, 0); + + + res = uvc_start_streaming(devh_d, &ctrl_d, cb_gray, 12345, 0); + + if (res < 0) { + uvc_perror(res, "start_streaming"); /* unable to start stream */ + } else { + puts("Streaming..."); + + //uvc_set_ae_mode(devh, 1); /* e.g., turn on auto exposure */ + + sleep(30); /* stream for 10 seconds */ + + /* End the stream. Blocks until last callback is serviced */ + uvc_stop_streaming(devh_rgb); + uvc_stop_streaming(devh_d); + + puts("Done streaming."); + } + } + + /* Release our handle on the device */ + uvc_close(devh_d); + uvc_close(devh_rgb); + + puts("Device closed"); + } + + /* Release the device descriptor */ + uvc_unref_device(dev); + } + + /* Close the UVC context. This closes and cleans up any existing device handles, + * and it closes the libusb context if one was not provided. */ + uvc_exit(ctx); + puts("UVC exited"); + + return 0; +} + diff --git a/third_party/libuvc/src/frame-mjpeg.c b/third_party/libuvc/src/frame-mjpeg.c new file mode 100644 index 0000000000..8adeb3be3f --- /dev/null +++ b/third_party/libuvc/src/frame-mjpeg.c @@ -0,0 +1,187 @@ +/********************************************************************* +* Software License Agreement (BSD License) +* +* Copyright (C) 2014 Robert Xiao +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of the author nor other contributors may be +* used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*********************************************************************/ + +/** + * @defgroup frame Frame processing + */ +#include "libuvc/libuvc.h" +#include "libuvc/libuvc_internal.h" +#include +#include + +extern uvc_error_t uvc_ensure_frame_size(uvc_frame_t *frame, size_t need_bytes); + +struct error_mgr { + struct jpeg_error_mgr super; + jmp_buf jmp; +}; + +static void _error_exit(j_common_ptr dinfo) { + struct error_mgr *myerr = (struct error_mgr *)dinfo->err; + (*dinfo->err->output_message)(dinfo); + longjmp(myerr->jmp, 1); +} + +/* ISO/IEC 10918-1:1993(E) K.3.3. Default Huffman tables used by MJPEG UVC devices + which don't specify a Huffman table in the JPEG stream. */ +static const unsigned char dc_lumi_len[] = + {0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}; +static const unsigned char dc_lumi_val[] = + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + +static const unsigned char dc_chromi_len[] = + {0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}; +static const unsigned char dc_chromi_val[] = + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + +static const unsigned char ac_lumi_len[] = + {0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d}; +static const unsigned char ac_lumi_val[] = + {0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, + 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, + 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, + 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, + 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, + 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa}; +static const unsigned char ac_chromi_len[] = + {0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77}; +static const unsigned char ac_chromi_val[] = + {0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, + 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, + 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, + 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, + 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, + 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, + 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, + 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, + 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, + 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, + 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa}; + +#define COPY_HUFF_TABLE(dinfo,tbl,name) do { \ + if(dinfo->tbl == NULL) dinfo->tbl = jpeg_alloc_huff_table((j_common_ptr)dinfo); \ + memcpy(dinfo->tbl->bits, name##_len, sizeof(name##_len)); \ + memset(dinfo->tbl->huffval, 0, sizeof(dinfo->tbl->huffval)); \ + memcpy(dinfo->tbl->huffval, name##_val, sizeof(name##_val)); \ +} while(0) + +static void insert_huff_tables(j_decompress_ptr dinfo) { + COPY_HUFF_TABLE(dinfo, dc_huff_tbl_ptrs[0], dc_lumi); + COPY_HUFF_TABLE(dinfo, dc_huff_tbl_ptrs[1], dc_chromi); + COPY_HUFF_TABLE(dinfo, ac_huff_tbl_ptrs[0], ac_lumi); + COPY_HUFF_TABLE(dinfo, ac_huff_tbl_ptrs[1], ac_chromi); +} + +/** @brief Convert an MJPEG frame to RGB + * @ingroup frame + * + * @param in MJPEG frame + * @param out RGB frame + */ +uvc_error_t uvc_mjpeg2rgb(uvc_frame_t *in, uvc_frame_t *out) { + struct jpeg_decompress_struct dinfo; + struct error_mgr jerr; + size_t lines_read; + + if (in->frame_format != UVC_FRAME_FORMAT_MJPEG) + return UVC_ERROR_INVALID_PARAM; + + if (uvc_ensure_frame_size(out, in->width * in->height * 3) < 0) + return UVC_ERROR_NO_MEM; + + out->width = in->width; + out->height = in->height; + out->frame_format = UVC_FRAME_FORMAT_RGB; + out->step = in->width * 3; + out->sequence = in->sequence; + out->capture_time = in->capture_time; + out->source = in->source; + + dinfo.err = jpeg_std_error(&jerr.super); + jerr.super.error_exit = _error_exit; + + if (setjmp(jerr.jmp)) { + goto fail; + } + + jpeg_create_decompress(&dinfo); + jpeg_mem_src(&dinfo, in->data, in->data_bytes); + jpeg_read_header(&dinfo, TRUE); + + if (dinfo.dc_huff_tbl_ptrs[0] == NULL) { + /* This frame is missing the Huffman tables: fill in the standard ones */ + insert_huff_tables(&dinfo); + } + + dinfo.out_color_space = JCS_RGB; + dinfo.dct_method = JDCT_IFAST; + + jpeg_start_decompress(&dinfo); + + lines_read = 0; + while (dinfo.output_scanline < dinfo.output_height) { + unsigned char *buffer[1] = { out->data + lines_read * out->step }; + int num_scanlines; + + num_scanlines = jpeg_read_scanlines(&dinfo, buffer, 1); + lines_read += num_scanlines; + } + + jpeg_finish_decompress(&dinfo); + jpeg_destroy_decompress(&dinfo); + return 0; + +fail: + jpeg_destroy_decompress(&dinfo); + return UVC_ERROR_OTHER; +} diff --git a/third_party/libuvc/src/frame.c b/third_party/libuvc/src/frame.c new file mode 100644 index 0000000000..975d47c803 --- /dev/null +++ b/third_party/libuvc/src/frame.c @@ -0,0 +1,371 @@ +/********************************************************************* +* Software License Agreement (BSD License) +* +* Copyright (C) 2010-2012 Ken Tossell +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of the author nor other contributors may be +* used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*********************************************************************/ +/** + * @defgroup frame Frame processing + * @brief Tools for managing frame buffers and converting between image formats + */ +#include "libuvc/libuvc.h" +#include "libuvc/libuvc_internal.h" + +/** @internal */ +uvc_error_t uvc_ensure_frame_size(uvc_frame_t *frame, size_t need_bytes) { + if (frame->library_owns_data) { + if (!frame->data || frame->data_bytes != need_bytes) { + frame->data_bytes = need_bytes; + frame->data = realloc(frame->data, frame->data_bytes); + } + if (!frame->data) + return UVC_ERROR_NO_MEM; + return UVC_SUCCESS; + } else { + if (!frame->data || frame->data_bytes < need_bytes) + return UVC_ERROR_NO_MEM; + return UVC_SUCCESS; + } +} + +/** @brief Allocate a frame structure + * @ingroup frame + * + * @param data_bytes Number of bytes to allocate, or zero + * @return New frame, or NULL on error + */ +uvc_frame_t *uvc_allocate_frame(size_t data_bytes) { + uvc_frame_t *frame = malloc(sizeof(*frame)); + + if (!frame) + return NULL; + + memset(frame, 0, sizeof(*frame)); + + frame->library_owns_data = 1; + + if (data_bytes > 0) { + frame->data_bytes = data_bytes; + frame->data = malloc(data_bytes); + + if (!frame->data) { + free(frame); + return NULL; + } + } + + return frame; +} + +/** @brief Free a frame structure + * @ingroup frame + * + * @param frame Frame to destroy + */ +void uvc_free_frame(uvc_frame_t *frame) { + if (frame->data_bytes > 0 && frame->library_owns_data) + free(frame->data); + + free(frame); +} + +static inline unsigned char sat(int i) { + return (unsigned char)( i >= 255 ? 255 : (i < 0 ? 0 : i)); +} + +/** @brief Duplicate a frame, preserving color format + * @ingroup frame + * + * @param in Original frame + * @param out Duplicate frame + */ +uvc_error_t uvc_duplicate_frame(uvc_frame_t *in, uvc_frame_t *out) { + if (uvc_ensure_frame_size(out, in->data_bytes) < 0) + return UVC_ERROR_NO_MEM; + + out->width = in->width; + out->height = in->height; + out->frame_format = in->frame_format; + out->step = in->step; + out->sequence = in->sequence; + out->capture_time = in->capture_time; + out->source = in->source; + + memcpy(out->data, in->data, in->data_bytes); + + return UVC_SUCCESS; +} + +#define YUYV2RGB_2(pyuv, prgb) { \ + float r = 1.402f * ((pyuv)[3]-128); \ + float g = -0.34414f * ((pyuv)[1]-128) - 0.71414f * ((pyuv)[3]-128); \ + float b = 1.772f * ((pyuv)[1]-128); \ + (prgb)[0] = sat(pyuv[0] + r); \ + (prgb)[1] = sat(pyuv[0] + g); \ + (prgb)[2] = sat(pyuv[0] + b); \ + (prgb)[3] = sat(pyuv[2] + r); \ + (prgb)[4] = sat(pyuv[2] + g); \ + (prgb)[5] = sat(pyuv[2] + b); \ + } +#define IYUYV2RGB_2(pyuv, prgb) { \ + int r = (22987 * ((pyuv)[3] - 128)) >> 14; \ + int g = (-5636 * ((pyuv)[1] - 128) - 11698 * ((pyuv)[3] - 128)) >> 14; \ + int b = (29049 * ((pyuv)[1] - 128)) >> 14; \ + (prgb)[0] = sat(*(pyuv) + r); \ + (prgb)[1] = sat(*(pyuv) + g); \ + (prgb)[2] = sat(*(pyuv) + b); \ + (prgb)[3] = sat((pyuv)[2] + r); \ + (prgb)[4] = sat((pyuv)[2] + g); \ + (prgb)[5] = sat((pyuv)[2] + b); \ + } +#define IYUYV2RGB_16(pyuv, prgb) IYUYV2RGB_8(pyuv, prgb); IYUYV2RGB_8(pyuv + 16, prgb + 24); +#define IYUYV2RGB_8(pyuv, prgb) IYUYV2RGB_4(pyuv, prgb); IYUYV2RGB_4(pyuv + 8, prgb + 12); +#define IYUYV2RGB_4(pyuv, prgb) IYUYV2RGB_2(pyuv, prgb); IYUYV2RGB_2(pyuv + 4, prgb + 6); + +/** @brief Convert a frame from YUYV to RGB + * @ingroup frame + * + * @param in YUYV frame + * @param out RGB frame + */ +uvc_error_t uvc_yuyv2rgb(uvc_frame_t *in, uvc_frame_t *out) { + if (in->frame_format != UVC_FRAME_FORMAT_YUYV) + return UVC_ERROR_INVALID_PARAM; + + if (uvc_ensure_frame_size(out, in->width * in->height * 3) < 0) + return UVC_ERROR_NO_MEM; + + out->width = in->width; + out->height = in->height; + out->frame_format = UVC_FRAME_FORMAT_RGB; + out->step = in->width * 3; + out->sequence = in->sequence; + out->capture_time = in->capture_time; + out->source = in->source; + + uint8_t *pyuv = in->data; + uint8_t *prgb = out->data; + uint8_t *prgb_end = prgb + out->data_bytes; + + while (prgb < prgb_end) { + IYUYV2RGB_8(pyuv, prgb); + + prgb += 3 * 8; + pyuv += 2 * 8; + } + + return UVC_SUCCESS; +} + +#define IYUYV2BGR_2(pyuv, pbgr) { \ + int r = (22987 * ((pyuv)[3] - 128)) >> 14; \ + int g = (-5636 * ((pyuv)[1] - 128) - 11698 * ((pyuv)[3] - 128)) >> 14; \ + int b = (29049 * ((pyuv)[1] - 128)) >> 14; \ + (pbgr)[0] = sat(*(pyuv) + b); \ + (pbgr)[1] = sat(*(pyuv) + g); \ + (pbgr)[2] = sat(*(pyuv) + r); \ + (pbgr)[3] = sat((pyuv)[2] + b); \ + (pbgr)[4] = sat((pyuv)[2] + g); \ + (pbgr)[5] = sat((pyuv)[2] + r); \ + } +#define IYUYV2BGR_16(pyuv, pbgr) IYUYV2BGR_8(pyuv, pbgr); IYUYV2BGR_8(pyuv + 16, pbgr + 24); +#define IYUYV2BGR_8(pyuv, pbgr) IYUYV2BGR_4(pyuv, pbgr); IYUYV2BGR_4(pyuv + 8, pbgr + 12); +#define IYUYV2BGR_4(pyuv, pbgr) IYUYV2BGR_2(pyuv, pbgr); IYUYV2BGR_2(pyuv + 4, pbgr + 6); + +/** @brief Convert a frame from YUYV to BGR + * @ingroup frame + * + * @param in YUYV frame + * @param out BGR frame + */ +uvc_error_t uvc_yuyv2bgr(uvc_frame_t *in, uvc_frame_t *out) { + if (in->frame_format != UVC_FRAME_FORMAT_YUYV) + return UVC_ERROR_INVALID_PARAM; + + if (uvc_ensure_frame_size(out, in->width * in->height * 3) < 0) + return UVC_ERROR_NO_MEM; + + out->width = in->width; + out->height = in->height; + out->frame_format = UVC_FRAME_FORMAT_BGR; + out->step = in->width * 3; + out->sequence = in->sequence; + out->capture_time = in->capture_time; + out->source = in->source; + + uint8_t *pyuv = in->data; + uint8_t *pbgr = out->data; + uint8_t *pbgr_end = pbgr + out->data_bytes; + + while (pbgr < pbgr_end) { + IYUYV2BGR_8(pyuv, pbgr); + + pbgr += 3 * 8; + pyuv += 2 * 8; + } + + return UVC_SUCCESS; +} + +#define IUYVY2RGB_2(pyuv, prgb) { \ + int r = (22987 * ((pyuv)[2] - 128)) >> 14; \ + int g = (-5636 * ((pyuv)[0] - 128) - 11698 * ((pyuv)[2] - 128)) >> 14; \ + int b = (29049 * ((pyuv)[0] - 128)) >> 14; \ + (prgb)[0] = sat((pyuv)[1] + r); \ + (prgb)[1] = sat((pyuv)[1] + g); \ + (prgb)[2] = sat((pyuv)[1] + b); \ + (prgb)[3] = sat((pyuv)[3] + r); \ + (prgb)[4] = sat((pyuv)[3] + g); \ + (prgb)[5] = sat((pyuv)[3] + b); \ + } +#define IUYVY2RGB_16(pyuv, prgb) IUYVY2RGB_8(pyuv, prgb); IUYVY2RGB_8(pyuv + 16, prgb + 24); +#define IUYVY2RGB_8(pyuv, prgb) IUYVY2RGB_4(pyuv, prgb); IUYVY2RGB_4(pyuv + 8, prgb + 12); +#define IUYVY2RGB_4(pyuv, prgb) IUYVY2RGB_2(pyuv, prgb); IUYVY2RGB_2(pyuv + 4, prgb + 6); + +/** @brief Convert a frame from UYVY to RGB + * @ingroup frame + * @param ini UYVY frame + * @param out RGB frame + */ +uvc_error_t uvc_uyvy2rgb(uvc_frame_t *in, uvc_frame_t *out) { + if (in->frame_format != UVC_FRAME_FORMAT_UYVY) + return UVC_ERROR_INVALID_PARAM; + + if (uvc_ensure_frame_size(out, in->width * in->height * 3) < 0) + return UVC_ERROR_NO_MEM; + + out->width = in->width; + out->height = in->height; + out->frame_format = UVC_FRAME_FORMAT_RGB; + out->step = in->width *3; + out->sequence = in->sequence; + out->capture_time = in->capture_time; + out->source = in->source; + + uint8_t *pyuv = in->data; + uint8_t *prgb = out->data; + uint8_t *prgb_end = prgb + out->data_bytes; + + while (prgb < prgb_end) { + IUYVY2RGB_8(pyuv, prgb); + + prgb += 3 * 8; + pyuv += 2 * 8; + } + + return UVC_SUCCESS; +} + +#define IUYVY2BGR_2(pyuv, pbgr) { \ + int r = (22987 * ((pyuv)[2] - 128)) >> 14; \ + int g = (-5636 * ((pyuv)[0] - 128) - 11698 * ((pyuv)[2] - 128)) >> 14; \ + int b = (29049 * ((pyuv)[0] - 128)) >> 14; \ + (pbgr)[0] = sat((pyuv)[1] + b); \ + (pbgr)[1] = sat((pyuv)[1] + g); \ + (pbgr)[2] = sat((pyuv)[1] + r); \ + (pbgr)[3] = sat((pyuv)[3] + b); \ + (pbgr)[4] = sat((pyuv)[3] + g); \ + (pbgr)[5] = sat((pyuv)[3] + r); \ + } +#define IUYVY2BGR_16(pyuv, pbgr) IUYVY2BGR_8(pyuv, pbgr); IUYVY2BGR_8(pyuv + 16, pbgr + 24); +#define IUYVY2BGR_8(pyuv, pbgr) IUYVY2BGR_4(pyuv, pbgr); IUYVY2BGR_4(pyuv + 8, pbgr + 12); +#define IUYVY2BGR_4(pyuv, pbgr) IUYVY2BGR_2(pyuv, pbgr); IUYVY2BGR_2(pyuv + 4, pbgr + 6); + +/** @brief Convert a frame from UYVY to BGR + * @ingroup frame + * @param ini UYVY frame + * @param out BGR frame + */ +uvc_error_t uvc_uyvy2bgr(uvc_frame_t *in, uvc_frame_t *out) { + if (in->frame_format != UVC_FRAME_FORMAT_UYVY) + return UVC_ERROR_INVALID_PARAM; + + if (uvc_ensure_frame_size(out, in->width * in->height * 3) < 0) + return UVC_ERROR_NO_MEM; + + out->width = in->width; + out->height = in->height; + out->frame_format = UVC_FRAME_FORMAT_BGR; + out->step = in->width *3; + out->sequence = in->sequence; + out->capture_time = in->capture_time; + out->source = in->source; + + uint8_t *pyuv = in->data; + uint8_t *pbgr = out->data; + uint8_t *pbgr_end = pbgr + out->data_bytes; + + while (pbgr < pbgr_end) { + IUYVY2BGR_8(pyuv, pbgr); + + pbgr += 3 * 8; + pyuv += 2 * 8; + } + + return UVC_SUCCESS; +} + +/** @brief Convert a frame to RGB + * @ingroup frame + * + * @param in non-RGB frame + * @param out RGB frame + */ +uvc_error_t uvc_any2rgb(uvc_frame_t *in, uvc_frame_t *out) { + switch (in->frame_format) { + case UVC_FRAME_FORMAT_YUYV: + return uvc_yuyv2rgb(in, out); + case UVC_FRAME_FORMAT_UYVY: + return uvc_uyvy2rgb(in, out); + case UVC_FRAME_FORMAT_RGB: + return uvc_duplicate_frame(in, out); + default: + return UVC_ERROR_NOT_SUPPORTED; + } +} + +/** @brief Convert a frame to BGR + * @ingroup frame + * + * @param in non-BGR frame + * @param out BGR frame + */ +uvc_error_t uvc_any2bgr(uvc_frame_t *in, uvc_frame_t *out) { + switch (in->frame_format) { + case UVC_FRAME_FORMAT_YUYV: + return uvc_yuyv2bgr(in, out); + case UVC_FRAME_FORMAT_UYVY: + return uvc_uyvy2bgr(in, out); + case UVC_FRAME_FORMAT_BGR: + return uvc_duplicate_frame(in, out); + default: + return UVC_ERROR_NOT_SUPPORTED; + } +} diff --git a/third_party/libuvc/src/init.c b/third_party/libuvc/src/init.c new file mode 100644 index 0000000000..1c0b35c641 --- /dev/null +++ b/third_party/libuvc/src/init.c @@ -0,0 +1,163 @@ +/********************************************************************* +* Software License Agreement (BSD License) +* +* Copyright (C) 2010-2012 Ken Tossell +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of the author nor other contributors may be +* used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*********************************************************************/ +/** +\mainpage libuvc: a cross-platform library for USB video devices + +\b libuvc is a library that supports enumeration, control and streaming +for USB Video Class (UVC) devices, such as consumer webcams. + +\section features Features +\li UVC device \ref device "discovery and management" API +\li \ref streaming "Video streaming" (device to host) with asynchronous/callback and synchronous/polling modes +\li Read/write access to standard \ref ctrl "device settings" +\li \ref frame "Conversion" between various formats: RGB, YUV, JPEG, etc. +\li Tested on Mac and Linux, portable to Windows and some BSDs + +\section roadmap Roadmap +\li Bulk-mode image capture +\li One-shot image capture +\li Improved support for standard settings +\li Support for "extended" (vendor-defined) settings + +\section misc Misc. +\p The source code can be found at https://github.com/ktossell/libuvc. To build +the library, install libusb 1.0+ and run: + +\code +$ git clone https://github.com/ktossell/libuvc.git +$ cd libuvc +$ mkdir build +$ cd build +$ cmake -DCMAKE_BUILD_TYPE=Release .. +$ make && make install +\endcode + +\section Example +In this example, libuvc is used to acquire images in a 30 fps, 640x480 +YUV stream from a UVC device such as a standard webcam. + +\include example.c + +*/ + +/** + * @defgroup init Library initialization/deinitialization + * @brief Setup routines used to construct UVC access contexts + */ +#include "libuvc/libuvc.h" +#include "libuvc/libuvc_internal.h" + +/** @internal + * @brief Event handler thread + * There's one of these per UVC context. + * @todo We shouldn't run this if we don't own the USB context + */ +void *_uvc_handle_events(void *arg) { + uvc_context_t *ctx = (uvc_context_t *) arg; + + while (!ctx->kill_handler_thread) + libusb_handle_events(ctx->usb_ctx); + return NULL; +} + +/** @brief Initializes the UVC context + * @ingroup init + * + * @note If you provide your own USB context, you must handle + * libusb event processing using a function such as libusb_handle_events. + * + * @param[out] pctx The location where the context reference should be stored. + * @param[in] usb_ctx Optional USB context to use + * @return Error opening context or UVC_SUCCESS + */ +uvc_error_t uvc_init(uvc_context_t **pctx, struct libusb_context *usb_ctx) { + uvc_error_t ret = UVC_SUCCESS; + uvc_context_t *ctx = calloc(1, sizeof(*ctx)); + + if (usb_ctx == NULL) { + ret = libusb_init(&ctx->usb_ctx); + ctx->own_usb_ctx = 1; + if (ret != UVC_SUCCESS) { + free(ctx); + ctx = NULL; + } + } else { + ctx->own_usb_ctx = 0; + ctx->usb_ctx = usb_ctx; + } + + if (ctx != NULL) + *pctx = ctx; + + return ret; +} + +/** + * @brief Closes the UVC context, shutting down any active cameras. + * @ingroup init + * + * @note This function invalides any existing references to the context's + * cameras. + * + * If no USB context was provided to #uvc_init, the UVC-specific USB + * context will be destroyed. + * + * @param ctx UVC context to shut down + */ +void uvc_exit(uvc_context_t *ctx) { + uvc_device_handle_t *devh; + + DL_FOREACH(ctx->open_devices, devh) { + uvc_close(devh); + } + + if (ctx->own_usb_ctx) + libusb_exit(ctx->usb_ctx); + + free(ctx); +} + +/** + * @internal + * @brief Spawns a handler thread for the context + * @ingroup init + * + * This should be called at the end of a successful uvc_open if no devices + * are already open (and being handled). + */ +void uvc_start_handler_thread(uvc_context_t *ctx) { + if (ctx->own_usb_ctx) + pthread_create(&ctx->handler_thread, NULL, _uvc_handle_events, (void*) ctx); +} + diff --git a/third_party/libuvc/src/misc.c b/third_party/libuvc/src/misc.c new file mode 100644 index 0000000000..8ce850fdc4 --- /dev/null +++ b/third_party/libuvc/src/misc.c @@ -0,0 +1,58 @@ +/********************************************************************* +* Software License Agreement (BSD License) +* +* Copyright (C) 2010-2012 Ken Tossell +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of the author nor other contributors may be +* used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*********************************************************************/ +#include +#include + +#if __APPLE__ +char *strndup(const char *s, size_t n) { + size_t src_n = 0; + const char *sp = s; + char *d; + + while (*sp++) + src_n++; + + if (src_n < n) + n = src_n; + + d = malloc(n + 1); + + memcpy(d, s, n); + + d[n] = '\0'; + + return d; +} +#endif + diff --git a/third_party/libuvc/src/stream.c b/third_party/libuvc/src/stream.c new file mode 100644 index 0000000000..8abb0ccb19 --- /dev/null +++ b/third_party/libuvc/src/stream.c @@ -0,0 +1,1249 @@ +/********************************************************************* +* Software License Agreement (BSD License) +* +* Copyright (C) 2010-2012 Ken Tossell +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of the author nor other contributors may be +* used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*********************************************************************/ +/** + * @defgroup streaming Streaming control functions + * @brief Tools for creating, managing and consuming video streams + */ + +#include "libuvc/libuvc.h" +#include "libuvc/libuvc_internal.h" + +#ifdef _MSC_VER + +#define DELTA_EPOCH_IN_MICROSECS 116444736000000000Ui64 + +// gettimeofday - get time of day for Windows; +// A gettimeofday implementation for Microsoft Windows; +// Public domain code, author "ponnada"; +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag = 0; + if (NULL != tv) + { + GetSystemTimeAsFileTime(&ft); + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + tmpres /= 10; + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long)(tmpres / 1000000UL); + tv->tv_usec = (long)(tmpres % 1000000UL); + } + return 0; +} +#endif // _MSC_VER +uvc_frame_desc_t *uvc_find_frame_desc_stream(uvc_stream_handle_t *strmh, + uint16_t format_id, uint16_t frame_id); +uvc_frame_desc_t *uvc_find_frame_desc(uvc_device_handle_t *devh, + uint16_t format_id, uint16_t frame_id); +void *_uvc_user_caller(void *arg); +void _uvc_populate_frame(uvc_stream_handle_t *strmh); + +struct format_table_entry { + enum uvc_frame_format format; + uint8_t abstract_fmt; + uint8_t guid[16]; + int children_count; + enum uvc_frame_format *children; +}; + +struct format_table_entry *_get_format_entry(enum uvc_frame_format format) { + #define ABS_FMT(_fmt, _num, ...) \ + case _fmt: { \ + static enum uvc_frame_format _fmt##_children[] = __VA_ARGS__; \ + static struct format_table_entry _fmt##_entry = { \ + _fmt, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, _num, _fmt##_children }; \ + return &_fmt##_entry; } + + #define FMT(_fmt, ...) \ + case _fmt: { \ + static struct format_table_entry _fmt##_entry = { \ + _fmt, 0, __VA_ARGS__, 0, NULL }; \ + return &_fmt##_entry; } + + switch(format) { + /* Define new formats here */ + ABS_FMT(UVC_FRAME_FORMAT_ANY, 2, + {UVC_FRAME_FORMAT_UNCOMPRESSED, UVC_FRAME_FORMAT_COMPRESSED}) + + ABS_FMT(UVC_FRAME_FORMAT_UNCOMPRESSED, 3, + {UVC_FRAME_FORMAT_Y12I, UVC_FRAME_FORMAT_Y16, UVC_FRAME_FORMAT_Y8, UVC_FRAME_FORMAT_Z16, UVC_FRAME_FORMAT_YUYV, UVC_FRAME_FORMAT_UYVY, UVC_FRAME_FORMAT_GRAY8}) + FMT(UVC_FRAME_FORMAT_Y12I, + {'Y', '1', '2', 'I', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) + FMT(UVC_FRAME_FORMAT_Y16, + {'Y', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) + FMT(UVC_FRAME_FORMAT_Y8, + {'Y', '8', ' ', ' ', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) + FMT(UVC_FRAME_FORMAT_Z16, + {'Z', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) + FMT(UVC_FRAME_FORMAT_YUYV, + {'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) + FMT(UVC_FRAME_FORMAT_UYVY, + {'U', 'Y', 'V', 'Y', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) + FMT(UVC_FRAME_FORMAT_GRAY8, + {'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) + FMT(UVC_FRAME_FORMAT_BY8, + {'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) + + FMT(UVC_FRAME_FORMAT_INVI, + {'I', 'N', 'V', 'I', 0xdb, 0x57, 0x49, 0x5e, 0x8e, 0x3f, 0xf4, 0x79, 0x53, 0x2b, 0x94, 0x6f}) + FMT(UVC_FRAME_FORMAT_RELI, + {'R', 'E', 'L', 'I', 0x14, 0x13, 0x43, 0xf9, 0xa7, 0x5a, 0xee, 0x6b, 0xbf, 0x01, 0x2e, 0x23}) + FMT(UVC_FRAME_FORMAT_INVR, + {'I', 'N', 'V', 'R', 0x90, 0x2d, 0x58, 0x4a, 0x92, 0x0b, 0x77, 0x3f, 0x1f, 0x2c, 0x55, 0x6b}) + FMT(UVC_FRAME_FORMAT_INVZ, + {'I', 'N', 'V', 'Z', 0x90, 0x2d, 0x58, 0x4a, 0x92, 0x0b, 0x77, 0x3f, 0x1f, 0x2c, 0x55, 0x6b}) + FMT(UVC_FRAME_FORMAT_INRI, + {'I','N','R','I', 0x90, 0x2d, 0x58, 0x4a, 0x92, 0x0b, 0x77, 0x3f, 0x1f, 0x2c, 0x55, 0x6b}) + + ABS_FMT(UVC_FRAME_FORMAT_COMPRESSED, 1, + {UVC_FRAME_FORMAT_MJPEG}) + FMT(UVC_FRAME_FORMAT_MJPEG, + {'M', 'J', 'P', 'G'}) + + default: + return NULL; + } + + #undef ABS_FMT + #undef FMT +} + +static uint8_t _uvc_frame_format_matches_guid(enum uvc_frame_format fmt, uint8_t guid[16]) { + struct format_table_entry *format; + int child_idx; + + format = _get_format_entry(fmt); + if (!format) + return 0; + + if (!format->abstract_fmt && !memcmp(guid, format->guid, 16)) + return 1; + + for (child_idx = 0; child_idx < format->children_count; child_idx++) { + if (_uvc_frame_format_matches_guid(format->children[child_idx], guid)) + return 1; + } + + return 0; +} + +static enum uvc_frame_format uvc_frame_format_for_guid(uint8_t guid[16]) { + struct format_table_entry *format; + enum uvc_frame_format fmt; + + for (fmt = 0; fmt < UVC_FRAME_FORMAT_COUNT; ++fmt) { + format = _get_format_entry(fmt); + if (!format || format->abstract_fmt) + continue; + if (!memcmp(format->guid, guid, 16)) + return format->format; + } + + return UVC_FRAME_FORMAT_UNKNOWN; +} + +/** @internal + * Run a streaming control query + * @param[in] devh UVC device + * @param[in,out] ctrl Control block + * @param[in] probe Whether this is a probe query or a commit query + * @param[in] req Query type + */ +uvc_error_t uvc_query_stream_ctrl( + uvc_device_handle_t *devh, + uvc_stream_ctrl_t *ctrl, + uint8_t probe, + enum uvc_req_code req) { + uint8_t buf[34]; + size_t len; + uvc_error_t err; + + memset(buf, 0, sizeof(buf)); + + if (devh->info->ctrl_if.bcdUVC >= 0x0110) + len = 34; + else + len = 26; + + /* prepare for a SET transfer */ + if (req == UVC_SET_CUR) { + SHORT_TO_SW(ctrl->bmHint, buf); + buf[2] = ctrl->bFormatIndex; + buf[3] = ctrl->bFrameIndex; + INT_TO_DW(ctrl->dwFrameInterval, buf + 4); + SHORT_TO_SW(ctrl->wKeyFrameRate, buf + 8); + SHORT_TO_SW(ctrl->wPFrameRate, buf + 10); + SHORT_TO_SW(ctrl->wCompQuality, buf + 12); + SHORT_TO_SW(ctrl->wCompWindowSize, buf + 14); + SHORT_TO_SW(ctrl->wDelay, buf + 16); + INT_TO_DW(ctrl->dwMaxVideoFrameSize, buf + 18); + INT_TO_DW(ctrl->dwMaxPayloadTransferSize, buf + 22); + + if (len == 34) { + INT_TO_DW ( ctrl->dwClockFrequency, buf + 26 ); + buf[30] = ctrl->bmFramingInfo; + buf[31] = ctrl->bPreferredVersion; + buf[32] = ctrl->bMinVersion; + buf[33] = ctrl->bMaxVersion; + /** @todo support UVC 1.1 */ + } + } + + /* do the transfer */ + err = libusb_control_transfer( + devh->usb_devh, + req == UVC_SET_CUR ? 0x21 : 0xA1, + req, + probe ? (UVC_VS_PROBE_CONTROL << 8) : (UVC_VS_COMMIT_CONTROL << 8), + ctrl->bInterfaceNumber, + buf, len, 0 + ); + + if (err <= 0) { + return err; + } + + /* now decode following a GET transfer */ + if (req != UVC_SET_CUR) { + ctrl->bmHint = SW_TO_SHORT(buf); + ctrl->bFormatIndex = buf[2]; + ctrl->bFrameIndex = buf[3]; + ctrl->dwFrameInterval = DW_TO_INT(buf + 4); + ctrl->wKeyFrameRate = SW_TO_SHORT(buf + 8); + ctrl->wPFrameRate = SW_TO_SHORT(buf + 10); + ctrl->wCompQuality = SW_TO_SHORT(buf + 12); + ctrl->wCompWindowSize = SW_TO_SHORT(buf + 14); + ctrl->wDelay = SW_TO_SHORT(buf + 16); + ctrl->dwMaxVideoFrameSize = DW_TO_INT(buf + 18); + ctrl->dwMaxPayloadTransferSize = DW_TO_INT(buf + 22); + + if (len == 34) { + ctrl->dwClockFrequency = DW_TO_INT ( buf + 26 ); + ctrl->bmFramingInfo = buf[30]; + ctrl->bPreferredVersion = buf[31]; + ctrl->bMinVersion = buf[32]; + ctrl->bMaxVersion = buf[33]; + /** @todo support UVC 1.1 */ + } + + /* fix up block for cameras that fail to set dwMax* */ + if (ctrl->dwMaxVideoFrameSize == 0) { + uvc_frame_desc_t *frame = uvc_find_frame_desc(devh, ctrl->bFormatIndex, ctrl->bFrameIndex); + + if (frame) { + ctrl->dwMaxVideoFrameSize = frame->dwMaxVideoFrameBufferSize; + } + } + } + + return UVC_SUCCESS; +} + +/** @brief Reconfigure stream with a new stream format. + * @ingroup streaming + * + * This may be executed whether or not the stream is running. + * + * @param[in] strmh Stream handle + * @param[in] ctrl Control block, processed using {uvc_probe_stream_ctrl} or + * {uvc_get_stream_ctrl_format_size} + */ +uvc_error_t uvc_stream_ctrl(uvc_stream_handle_t *strmh, uvc_stream_ctrl_t *ctrl) { + uvc_error_t ret; + + if (strmh->stream_if->bInterfaceNumber != ctrl->bInterfaceNumber) + return UVC_ERROR_INVALID_PARAM; + + /* @todo Allow the stream to be modified without restarting the stream */ + if (strmh->running) + return UVC_ERROR_BUSY; + + ret = uvc_query_stream_ctrl(strmh->devh, ctrl, 0, UVC_SET_CUR); + if (ret != UVC_SUCCESS) + return ret; + + strmh->cur_ctrl = *ctrl; + return UVC_SUCCESS; +} + +/** @internal + * @brief Find the descriptor for a specific frame configuration + * @param stream_if Stream interface + * @param format_id Index of format class descriptor + * @param frame_id Index of frame descriptor + */ +static uvc_frame_desc_t *_uvc_find_frame_desc_stream_if(uvc_streaming_interface_t *stream_if, + uint16_t format_id, uint16_t frame_id) { + + uvc_format_desc_t *format = NULL; + uvc_frame_desc_t *frame = NULL; + + DL_FOREACH(stream_if->format_descs, format) { + if (format->bFormatIndex == format_id) { + DL_FOREACH(format->frame_descs, frame) { + if (frame->bFrameIndex == frame_id) + return frame; + } + } + } + + return NULL; +} + +uvc_frame_desc_t *uvc_find_frame_desc_stream(uvc_stream_handle_t *strmh, + uint16_t format_id, uint16_t frame_id) { + return _uvc_find_frame_desc_stream_if(strmh->stream_if, format_id, frame_id); +} + +/** @internal + * @brief Find the descriptor for a specific frame configuration + * @param devh UVC device + * @param format_id Index of format class descriptor + * @param frame_id Index of frame descriptor + */ +uvc_frame_desc_t *uvc_find_frame_desc(uvc_device_handle_t *devh, + uint16_t format_id, uint16_t frame_id) { + + uvc_streaming_interface_t *stream_if; + uvc_frame_desc_t *frame; + + DL_FOREACH(devh->info->stream_ifs, stream_if) { + frame = _uvc_find_frame_desc_stream_if(stream_if, format_id, frame_id); + if (frame) + return frame; + } + + return NULL; +} + +/** Get a negotiated streaming control block for some common parameters. + * @ingroup streaming + * + * @param[in] devh Device handle + * @param[in,out] ctrl Control block + * @param[in] format_class Type of streaming format + * @param[in] width Desired frame width + * @param[in] height Desired frame height + * @param[in] fps Frame rate, frames per second + */ +uvc_error_t uvc_get_stream_ctrl_format_size( + uvc_device_handle_t *devh, + uvc_stream_ctrl_t *ctrl, + enum uvc_frame_format cf, + int width, int height, + int fps) { + uvc_streaming_interface_t *stream_if; + + /* find a matching frame descriptor and interval */ + DL_FOREACH(devh->info->stream_ifs, stream_if) { + uvc_format_desc_t *format; + + DL_FOREACH(stream_if->format_descs, format) { + uvc_frame_desc_t *frame; + + if (!_uvc_frame_format_matches_guid(cf, format->guidFormat)) + continue; + + DL_FOREACH(format->frame_descs, frame) { + if (frame->wWidth != width || frame->wHeight != height) + continue; + + uint32_t *interval; + + if (frame->intervals) { + for (interval = frame->intervals; *interval; ++interval) { + // allow a fps rate of zero to mean "accept first rate available" + if (10000000 / *interval == (unsigned int) fps || fps == 0) { + + /* get the max values -- we need the interface number to be able + to do this */ + ctrl->bInterfaceNumber = stream_if->bInterfaceNumber; + uvc_query_stream_ctrl( devh, ctrl, 1, UVC_GET_MAX); + + ctrl->bmHint = (1 << 0); /* don't negotiate interval */ + ctrl->bFormatIndex = format->bFormatIndex; + ctrl->bFrameIndex = frame->bFrameIndex; + ctrl->dwFrameInterval = *interval; + + goto found; + } + } + } else { + uint32_t interval_100ns = 10000000 / fps; + uint32_t interval_offset = interval_100ns - frame->dwMinFrameInterval; + + if (interval_100ns >= frame->dwMinFrameInterval + && interval_100ns <= frame->dwMaxFrameInterval + && !(interval_offset + && (interval_offset % frame->dwFrameIntervalStep))) { + + /* get the max values -- we need the interface number to be able + to do this */ + ctrl->bInterfaceNumber = stream_if->bInterfaceNumber; + uvc_query_stream_ctrl( devh, ctrl, 1, UVC_GET_MAX); + + ctrl->bmHint = (1 << 0); + ctrl->bFormatIndex = format->bFormatIndex; + ctrl->bFrameIndex = frame->bFrameIndex; + ctrl->dwFrameInterval = interval_100ns; + + goto found; + } + } + } + } + } + + return UVC_ERROR_INVALID_MODE; + +found: + return uvc_probe_stream_ctrl(devh, ctrl); +} + +/** @internal + * Negotiate streaming parameters with the device + * + * @param[in] devh UVC device + * @param[in,out] ctrl Control block + */ +uvc_error_t uvc_probe_stream_ctrl( + uvc_device_handle_t *devh, + uvc_stream_ctrl_t *ctrl) { + + uvc_claim_if(devh, ctrl->bInterfaceNumber); + + uvc_query_stream_ctrl( + devh, ctrl, 1, UVC_SET_CUR + ); + + uvc_query_stream_ctrl( + devh, ctrl, 1, UVC_GET_CUR + ); + + /** @todo make sure that worked */ + return UVC_SUCCESS; +} + +/** @internal + * @brief Swap the working buffer with the presented buffer and notify consumers + */ +void _uvc_swap_buffers(uvc_stream_handle_t *strmh) { + uint8_t *tmp_buf; + + pthread_mutex_lock(&strmh->cb_mutex); + + /* swap the buffers */ + tmp_buf = strmh->holdbuf; + strmh->hold_bytes = strmh->got_bytes; + strmh->holdbuf = strmh->outbuf; + strmh->outbuf = tmp_buf; + strmh->hold_last_scr = strmh->last_scr; + strmh->hold_pts = strmh->pts; + strmh->hold_seq = strmh->seq; + + pthread_cond_broadcast(&strmh->cb_cond); + pthread_mutex_unlock(&strmh->cb_mutex); + + strmh->seq++; + strmh->got_bytes = 0; + strmh->last_scr = 0; + strmh->pts = 0; +} + +/** @internal + * @brief Process a payload transfer + * + * Processes stream, places frames into buffer, signals listeners + * (such as user callback thread and any polling thread) on new frame + * + * @param payload Contents of the payload transfer, either a packet (isochronous) or a full + * transfer (bulk mode) + * @param payload_len Length of the payload transfer + */ +void _uvc_process_payload(uvc_stream_handle_t *strmh, uint8_t *payload, size_t payload_len) { + size_t header_len; + uint8_t header_info; + size_t data_len; + struct libusb_iso_packet_descriptor *pkt; + + /* magic numbers for identifying header packets from some iSight cameras */ + static uint8_t isight_tag[] = { + 0x11, 0x22, 0x33, 0x44, + 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xfa, 0xce + }; + + /* ignore empty payload transfers */ + if (payload_len == 0) + return; + + /* Certain iSight cameras have strange behavior: They send header + * information in a packet with no image data, and then the following + * packets have only image data, with no more headers until the next frame. + * + * The iSight header: len(1), flags(1 or 2), 0x11223344(4), + * 0xdeadbeefdeadface(8), ??(16) + */ + + if (strmh->devh->is_isight && + (payload_len < 14 || memcmp(isight_tag, payload + 2, sizeof(isight_tag))) && + (payload_len < 15 || memcmp(isight_tag, payload + 3, sizeof(isight_tag)))) { + /* The payload transfer doesn't have any iSight magic, so it's all image data */ + header_len = 0; + data_len = payload_len; + } else { + header_len = payload[0]; + + if (header_len > payload_len) { + UVC_DEBUG("bogus packet: actual_len=%zd, header_len=%zd\n", payload_len, header_len); + return; + } + + if (strmh->devh->is_isight) + data_len = 0; + else + data_len = payload_len - header_len; + } + + if (header_len < 2) { + header_info = 0; + } else { + /** @todo we should be checking the end-of-header bit */ + size_t variable_offset = 2; + + header_info = payload[1]; + + if (header_info & 0x40) { + UVC_DEBUG("bad packet: error bit set"); + return; + } + + if (strmh->fid != (header_info & 1) && strmh->got_bytes != 0) { + /* The frame ID bit was flipped, but we have image data sitting + around from prior transfers. This means the camera didn't send + an EOF for the last transfer of the previous frame. */ + _uvc_swap_buffers(strmh); + } + + strmh->fid = header_info & 1; + + if (header_info & (1 << 2)) { + strmh->pts = DW_TO_INT(payload + variable_offset); + variable_offset += 4; + } + + if (header_info & (1 << 3)) { + /** @todo read the SOF token counter */ + strmh->last_scr = DW_TO_INT(payload + variable_offset); + variable_offset += 6; + } + } + + if (data_len > 0) { + memcpy(strmh->outbuf + strmh->got_bytes, payload + header_len, data_len); + strmh->got_bytes += data_len; + + if (header_info & (1 << 1)) { + /* The EOF bit is set, so publish the complete frame */ + _uvc_swap_buffers(strmh); + } + } +} + +/** @internal + * @brief Stream transfer callback + * + * Processes stream, places frames into buffer, signals listeners + * (such as user callback thread and any polling thread) on new frame + * + * @param transfer Active transfer + */ +void LIBUSB_CALL _uvc_stream_callback(struct libusb_transfer *transfer) { + uvc_stream_handle_t *strmh = transfer->user_data; + + int resubmit = 1; + + switch (transfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + if (transfer->num_iso_packets == 0) { + /* This is a bulk mode transfer, so it just has one payload transfer */ + _uvc_process_payload(strmh, transfer->buffer, transfer->actual_length); + } else { + /* This is an isochronous mode transfer, so each packet has a payload transfer */ + int packet_id; + + for (packet_id = 0; packet_id < transfer->num_iso_packets; ++packet_id) { + uint8_t *pktbuf; + struct libusb_iso_packet_descriptor *pkt; + + pkt = transfer->iso_packet_desc + packet_id; + + if (pkt->status != 0) { + UVC_DEBUG("bad packet (isochronous transfer); status: %d", pkt->status); + continue; + } + + pktbuf = libusb_get_iso_packet_buffer_simple(transfer, packet_id); + + _uvc_process_payload(strmh, pktbuf, pkt->actual_length); + + } + } + break; + case LIBUSB_TRANSFER_CANCELLED: + case LIBUSB_TRANSFER_ERROR: + case LIBUSB_TRANSFER_NO_DEVICE: { + int i; + UVC_DEBUG("not retrying transfer, status = %d", transfer->status); + pthread_mutex_lock(&strmh->cb_mutex); + + /* Mark transfer as deleted. */ + for(i=0; i < LIBUVC_NUM_TRANSFER_BUFS; i++) { + if(strmh->transfers[i] == transfer) { + UVC_DEBUG("Freeing transfer %d (%p)", i, transfer); + free(transfer->buffer); + libusb_free_transfer(transfer); + strmh->transfers[i] = NULL; + break; + } + } + if(i == LIBUVC_NUM_TRANSFER_BUFS ) { + UVC_DEBUG("transfer %p not found; not freeing!", transfer); + } + + resubmit = 0; + + pthread_cond_broadcast(&strmh->cb_cond); + pthread_mutex_unlock(&strmh->cb_mutex); + + break; + } + case LIBUSB_TRANSFER_TIMED_OUT: + case LIBUSB_TRANSFER_STALL: + case LIBUSB_TRANSFER_OVERFLOW: + UVC_DEBUG("retrying transfer, status = %d", transfer->status); + break; + } + + if ( strmh->running && resubmit ) + libusb_submit_transfer(transfer); +} + +/** Begin streaming video from the camera into the callback function. + * @ingroup streaming + * + * @param devh UVC device + * @param ctrl Control block, processed using {uvc_probe_stream_ctrl} or + * {uvc_get_stream_ctrl_format_size} + * @param cb User callback function. See {uvc_frame_callback_t} for restrictions. + * @param flags Stream setup flags, currently undefined. Set this to zero. The lower bit + * is reserved for backward compatibility. + */ +uvc_error_t uvc_start_streaming( + uvc_device_handle_t *devh, + uvc_stream_ctrl_t *ctrl, + uvc_frame_callback_t *cb, + void *user_ptr, + uint8_t flags +) { + uvc_error_t ret; + uvc_stream_handle_t *strmh; + + ret = uvc_stream_open_ctrl(devh, &strmh, ctrl); + if (ret != UVC_SUCCESS) + return ret; + + ret = uvc_stream_start(strmh, cb, user_ptr, flags); + if (ret != UVC_SUCCESS) { + UVC_DEBUG("FAILED TO START STREAM: %i", ret); + uvc_stream_close(strmh); + return ret; + } + + return UVC_SUCCESS; +} + +/** Begin streaming video from the camera into the callback function. + * @ingroup streaming + * + * @deprecated The stream type (bulk vs. isochronous) will be determined by the + * type of interface associated with the uvc_stream_ctrl_t parameter, regardless + * of whether the caller requests isochronous streaming. Please switch to + * uvc_start_streaming(). + * + * @param devh UVC device + * @param ctrl Control block, processed using {uvc_probe_stream_ctrl} or + * {uvc_get_stream_ctrl_format_size} + * @param cb User callback function. See {uvc_frame_callback_t} for restrictions. + */ +uvc_error_t uvc_start_iso_streaming( + uvc_device_handle_t *devh, + uvc_stream_ctrl_t *ctrl, + uvc_frame_callback_t *cb, + void *user_ptr +) { + return uvc_start_streaming(devh, ctrl, cb, user_ptr, 0); +} + +static uvc_stream_handle_t *_uvc_get_stream_by_interface(uvc_device_handle_t *devh, int interface_idx) { + uvc_stream_handle_t *strmh; + + DL_FOREACH(devh->streams, strmh) { + if (strmh->stream_if->bInterfaceNumber == interface_idx) + return strmh; + } + + return NULL; +} + +static uvc_streaming_interface_t *_uvc_get_stream_if(uvc_device_handle_t *devh, int interface_idx) { + uvc_streaming_interface_t *stream_if; + + DL_FOREACH(devh->info->stream_ifs, stream_if) { + if (stream_if->bInterfaceNumber == interface_idx) + return stream_if; + } + + return NULL; +} + +/** Open a new video stream. + * @ingroup streaming + * + * @param devh UVC device + * @param ctrl Control block, processed using {uvc_probe_stream_ctrl} or + * {uvc_get_stream_ctrl_format_size} + */ +uvc_error_t uvc_stream_open_ctrl(uvc_device_handle_t *devh, uvc_stream_handle_t **strmhp, uvc_stream_ctrl_t *ctrl) { + /* Chosen frame and format descriptors */ + uvc_stream_handle_t *strmh = NULL; + uvc_streaming_interface_t *stream_if; + uvc_error_t ret; + + UVC_ENTER(); + + if (_uvc_get_stream_by_interface(devh, ctrl->bInterfaceNumber) != NULL) { + ret = UVC_ERROR_BUSY; /* Stream is already opened */ + goto fail; + } + + stream_if = _uvc_get_stream_if(devh, ctrl->bInterfaceNumber); + if (!stream_if) { + ret = UVC_ERROR_INVALID_PARAM; + goto fail; + } + + strmh = calloc(1, sizeof(*strmh)); + if (!strmh) { + ret = UVC_ERROR_NO_MEM; + goto fail; + } + strmh->devh = devh; + strmh->stream_if = stream_if; + strmh->frame.library_owns_data = 1; + + ret = uvc_claim_if(strmh->devh, strmh->stream_if->bInterfaceNumber); + if (ret != UVC_SUCCESS) + goto fail; + + ret = uvc_stream_ctrl(strmh, ctrl); + if (ret != UVC_SUCCESS) + goto fail; + + // Set up the streaming status and data space + strmh->running = 0; + /** @todo take only what we need */ + strmh->outbuf = malloc( LIBUVC_XFER_BUF_SIZE ); + strmh->holdbuf = malloc( LIBUVC_XFER_BUF_SIZE ); + + pthread_mutex_init(&strmh->cb_mutex, NULL); + pthread_cond_init(&strmh->cb_cond, NULL); + + DL_APPEND(devh->streams, strmh); + + *strmhp = strmh; + + UVC_EXIT(0); + return UVC_SUCCESS; + +fail: + if(strmh) + free(strmh); + UVC_EXIT(ret); + return ret; +} + +/** Begin streaming video from the stream into the callback function. + * @ingroup streaming + * + * @param strmh UVC stream + * @param cb User callback function. See {uvc_frame_callback_t} for restrictions. + * @param flags Stream setup flags, currently undefined. Set this to zero. The lower bit + * is reserved for backward compatibility. + */ +uvc_error_t uvc_stream_start( + uvc_stream_handle_t *strmh, + uvc_frame_callback_t *cb, + void *user_ptr, + uint8_t flags +) { + /* USB interface we'll be using */ + const struct libusb_interface *interface; + int interface_id; + char isochronous; + uvc_frame_desc_t *frame_desc; + uvc_format_desc_t *format_desc; + uvc_stream_ctrl_t *ctrl; + uvc_error_t ret; + /* Total amount of data per transfer */ + size_t total_transfer_size; + struct libusb_transfer *transfer; + int transfer_id; + + ctrl = &strmh->cur_ctrl; + + UVC_ENTER(); + + if (strmh->running) { + UVC_EXIT(UVC_ERROR_BUSY); + return UVC_ERROR_BUSY; + } + + strmh->running = 1; + strmh->seq = 0; + strmh->fid = 0; + strmh->pts = 0; + strmh->last_scr = 0; + + frame_desc = uvc_find_frame_desc_stream(strmh, ctrl->bFormatIndex, ctrl->bFrameIndex); + if (!frame_desc) { + ret = UVC_ERROR_INVALID_PARAM; + goto fail; + } + format_desc = frame_desc->parent; + + strmh->frame_format = uvc_frame_format_for_guid(format_desc->guidFormat); + if (strmh->frame_format == UVC_FRAME_FORMAT_UNKNOWN) { + ret = UVC_ERROR_NOT_SUPPORTED; + goto fail; + } + + // Get the interface that provides the chosen format and frame configuration + interface_id = strmh->stream_if->bInterfaceNumber; + interface = &strmh->devh->info->config->interface[interface_id]; + + /* A VS interface uses isochronous transfers iff it has multiple altsettings. + * (UVC 1.5: 2.4.3. VideoStreaming Interface) */ + isochronous = interface->num_altsetting > 1; + + if (isochronous) { + /* For isochronous streaming, we choose an appropriate altsetting for the endpoint + * and set up several transfers */ + const struct libusb_interface_descriptor *altsetting; + const struct libusb_endpoint_descriptor *endpoint; + /* The greatest number of bytes that the device might provide, per packet, in this + * configuration */ + size_t config_bytes_per_packet; + /* Number of packets per transfer */ + size_t packets_per_transfer; + /* Size of packet transferable from the chosen endpoint */ + size_t endpoint_bytes_per_packet; + /* Index of the altsetting */ + int alt_idx, ep_idx; + + config_bytes_per_packet = strmh->cur_ctrl.dwMaxPayloadTransferSize; + + /* Go through the altsettings and find one whose packets are at least + * as big as our format's maximum per-packet usage. Assume that the + * packet sizes are increasing. */ + for (alt_idx = 0; alt_idx < interface->num_altsetting; alt_idx++) { + altsetting = interface->altsetting + alt_idx; + endpoint_bytes_per_packet = 0; + + /* Find the endpoint with the number specified in the VS header */ + for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) { + endpoint = altsetting->endpoint + ep_idx; + + if (endpoint->bEndpointAddress == format_desc->parent->bEndpointAddress) { + endpoint_bytes_per_packet = endpoint->wMaxPacketSize; + // wMaxPacketSize: [unused:2 (multiplier-1):3 size:11] + endpoint_bytes_per_packet = (endpoint_bytes_per_packet & 0x07ff) * + (((endpoint_bytes_per_packet >> 11) & 3) + 1); + break; + } + } + + if (endpoint_bytes_per_packet >= config_bytes_per_packet) { + /* Transfers will be at most one frame long: Divide the maximum frame size + * by the size of the endpoint and round up */ + packets_per_transfer = (ctrl->dwMaxVideoFrameSize + + endpoint_bytes_per_packet - 1) / endpoint_bytes_per_packet; + + /* But keep a reasonable limit: Otherwise we start dropping data */ + if (packets_per_transfer > 32) + packets_per_transfer = 32; + + total_transfer_size = packets_per_transfer * endpoint_bytes_per_packet; + break; + } + } + + /* If we searched through all the altsettings and found nothing usable */ + if (alt_idx == interface->num_altsetting) { + ret = UVC_ERROR_INVALID_MODE; + goto fail; + } + + /* Select the altsetting */ + ret = libusb_set_interface_alt_setting(strmh->devh->usb_devh, + altsetting->bInterfaceNumber, + altsetting->bAlternateSetting); + if (ret != UVC_SUCCESS) { + UVC_DEBUG("libusb_set_interface_alt_setting failed"); + goto fail; + } + + /* Set up the transfers */ + for (transfer_id = 0; transfer_id < LIBUVC_NUM_TRANSFER_BUFS; ++transfer_id) { + transfer = libusb_alloc_transfer(packets_per_transfer); + strmh->transfers[transfer_id] = transfer; + strmh->transfer_bufs[transfer_id] = malloc(total_transfer_size); + + libusb_fill_iso_transfer( + transfer, strmh->devh->usb_devh, format_desc->parent->bEndpointAddress, + strmh->transfer_bufs[transfer_id], + total_transfer_size, packets_per_transfer, _uvc_stream_callback, (void*) strmh, 5000); + + libusb_set_iso_packet_lengths(transfer, endpoint_bytes_per_packet); + } + } else { + for (transfer_id = 0; transfer_id < LIBUVC_NUM_TRANSFER_BUFS; + ++transfer_id) { + transfer = libusb_alloc_transfer(0); + strmh->transfers[transfer_id] = transfer; + strmh->transfer_bufs[transfer_id] = malloc ( + strmh->cur_ctrl.dwMaxPayloadTransferSize ); + libusb_fill_bulk_transfer ( transfer, strmh->devh->usb_devh, + format_desc->parent->bEndpointAddress, + strmh->transfer_bufs[transfer_id], + strmh->cur_ctrl.dwMaxPayloadTransferSize, _uvc_stream_callback, + ( void* ) strmh, 5000 ); + } + } + + strmh->user_cb = cb; + strmh->user_ptr = user_ptr; + + /* If the user wants it, set up a thread that calls the user's function + * with the contents of each frame. + */ + if (cb) { + pthread_create(&strmh->cb_thread, NULL, _uvc_user_caller, (void*) strmh); + } + + for (transfer_id = 0; transfer_id < LIBUVC_NUM_TRANSFER_BUFS; + transfer_id++) { + ret = libusb_submit_transfer(strmh->transfers[transfer_id]); + if (ret != UVC_SUCCESS) { + printf("Transfer number: %d\n", transfer_id); + UVC_DEBUG("libusb_submit_transfer failed"); + break; + } + } + + UVC_EXIT(ret); + return ret; +fail: + strmh->running = 0; + UVC_EXIT(ret); + return ret; +} + +/** Begin streaming video from the stream into the callback function. + * @ingroup streaming + * + * @deprecated The stream type (bulk vs. isochronous) will be determined by the + * type of interface associated with the uvc_stream_ctrl_t parameter, regardless + * of whether the caller requests isochronous streaming. Please switch to + * uvc_stream_start(). + * + * @param strmh UVC stream + * @param cb User callback function. See {uvc_frame_callback_t} for restrictions. + */ +uvc_error_t uvc_stream_start_iso( + uvc_stream_handle_t *strmh, + uvc_frame_callback_t *cb, + void *user_ptr +) { + return uvc_stream_start(strmh, cb, user_ptr, 0); +} + +/** @internal + * @brief User callback runner thread + * @note There should be at most one of these per currently streaming device + * @param arg Device handle + */ +void *_uvc_user_caller(void *arg) { + uvc_stream_handle_t *strmh = (uvc_stream_handle_t *) arg; + + uint32_t last_seq = 0; + + do { + pthread_mutex_lock(&strmh->cb_mutex); + + while (strmh->running && last_seq == strmh->hold_seq) { + pthread_cond_wait(&strmh->cb_cond, &strmh->cb_mutex); + } + + if (!strmh->running) { + pthread_mutex_unlock(&strmh->cb_mutex); + break; + } + + last_seq = strmh->hold_seq; + _uvc_populate_frame(strmh); + + pthread_mutex_unlock(&strmh->cb_mutex); + + strmh->user_cb(&strmh->frame, strmh->user_ptr); + } while(1); + + return NULL; // return value ignored +} + +/** @internal + * @brief Populate the fields of a frame to be handed to user code + * must be called with stream cb lock held! + */ +void _uvc_populate_frame(uvc_stream_handle_t *strmh) { + size_t alloc_size = strmh->cur_ctrl.dwMaxVideoFrameSize; + uvc_frame_t *frame = &strmh->frame; + uvc_frame_desc_t *frame_desc; + + /** @todo this stuff that hits the main config cache should really happen + * in start() so that only one thread hits these data. all of this stuff + * is going to be reopen_on_change anyway + */ + + frame_desc = uvc_find_frame_desc(strmh->devh, strmh->cur_ctrl.bFormatIndex, + strmh->cur_ctrl.bFrameIndex); + + frame->frame_format = strmh->frame_format; + + frame->width = frame_desc->wWidth; + frame->height = frame_desc->wHeight; + + switch (frame->frame_format) { + case UVC_FRAME_FORMAT_YUYV: + frame->step = frame->width * 2; + break; + case UVC_FRAME_FORMAT_MJPEG: + frame->step = 0; + break; + default: + frame->step = 0; + break; + } + + /* copy the image data from the hold buffer to the frame (unnecessary extra buf?) */ + if (frame->data_bytes < strmh->hold_bytes) { + frame->data = realloc(frame->data, strmh->hold_bytes); + frame->data_bytes = strmh->hold_bytes; + } + memcpy(frame->data, strmh->holdbuf, frame->data_bytes); + + /** @todo set the frame time */ +} + +/** Poll for a frame + * @ingroup streaming + * + * @param devh UVC device + * @param[out] frame Location to store pointer to captured frame (NULL on error) + * @param timeout_us >0: Wait at most N microseconds; 0: Wait indefinitely; -1: return immediately + */ +uvc_error_t uvc_stream_get_frame(uvc_stream_handle_t *strmh, + uvc_frame_t **frame, + int32_t timeout_us) { + time_t add_secs; + time_t add_nsecs; + struct timespec ts; + struct timeval tv; + + if (!strmh->running) + return UVC_ERROR_INVALID_PARAM; + + if (strmh->user_cb) + return UVC_ERROR_CALLBACK_EXISTS; + + pthread_mutex_lock(&strmh->cb_mutex); + + if (strmh->last_polled_seq < strmh->hold_seq) { + _uvc_populate_frame(strmh); + *frame = &strmh->frame; + strmh->last_polled_seq = strmh->hold_seq; + } else if (timeout_us != -1) { + if (timeout_us == 0) { + pthread_cond_wait(&strmh->cb_cond, &strmh->cb_mutex); + } else { + add_secs = timeout_us / 1000000; + add_nsecs = (timeout_us % 1000000) * 1000; + ts.tv_sec = 0; + ts.tv_nsec = 0; + +#if _POSIX_TIMERS > 0 + clock_gettime(CLOCK_REALTIME, &ts); +#else + gettimeofday(&tv, NULL); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; +#endif + + ts.tv_sec += add_secs; + ts.tv_nsec += add_nsecs; + + pthread_cond_timedwait(&strmh->cb_cond, &strmh->cb_mutex, &ts); + } + + if (strmh->last_polled_seq < strmh->hold_seq) { + _uvc_populate_frame(strmh); + *frame = &strmh->frame; + strmh->last_polled_seq = strmh->hold_seq; + } else { + *frame = NULL; + } + } else { + *frame = NULL; + } + + pthread_mutex_unlock(&strmh->cb_mutex); + + return UVC_SUCCESS; +} + +/** @brief Stop streaming video + * @ingroup streaming + * + * Closes all streams, ends threads and cancels pollers + * + * @param devh UVC device + */ +void uvc_stop_streaming(uvc_device_handle_t *devh) { + uvc_stream_handle_t *strmh, *strmh_tmp; + + DL_FOREACH_SAFE(devh->streams, strmh, strmh_tmp) { + uvc_stream_close(strmh); + } +} + +/** @brief Stop stream. + * @ingroup streaming + * + * Stops stream, ends threads and cancels pollers + * + * @param devh UVC device + */ +uvc_error_t uvc_stream_stop(uvc_stream_handle_t *strmh) { + int i; + + if (!strmh->running) + return UVC_ERROR_INVALID_PARAM; + + strmh->running = 0; + + pthread_mutex_lock(&strmh->cb_mutex); + + for(i=0; i < LIBUVC_NUM_TRANSFER_BUFS; i++) { + if(strmh->transfers[i] != NULL) { + int res = libusb_cancel_transfer(strmh->transfers[i]); + if(res < 0 && res != LIBUSB_ERROR_NOT_FOUND ) { + free(strmh->transfers[i]->buffer); + libusb_free_transfer(strmh->transfers[i]); + strmh->transfers[i] = NULL; + } + } + } + + /* Wait for transfers to complete/cancel */ + do { + for(i=0; i < LIBUVC_NUM_TRANSFER_BUFS; i++) { + if(strmh->transfers[i] != NULL) + break; + } + if(i == LIBUVC_NUM_TRANSFER_BUFS ) + break; + pthread_cond_wait(&strmh->cb_cond, &strmh->cb_mutex); + } while(1); + // Kick the user thread awake + pthread_cond_broadcast(&strmh->cb_cond); + pthread_mutex_unlock(&strmh->cb_mutex); + + /** @todo stop the actual stream, camera side? */ + + if (strmh->user_cb) { + /* wait for the thread to stop (triggered by + * LIBUSB_TRANSFER_CANCELLED transfer) */ + pthread_join(strmh->cb_thread, NULL); + } + + return UVC_SUCCESS; +} + +/** @brief Close stream. + * @ingroup streaming + * + * Closes stream, frees handle and all streaming resources. + * + * @param strmh UVC stream handle + */ +void uvc_stream_close(uvc_stream_handle_t *strmh) { + if (strmh->running) + uvc_stream_stop(strmh); + + uvc_release_if(strmh->devh, strmh->stream_if->bInterfaceNumber); + + if (strmh->frame.data) + free(strmh->frame.data); + + free(strmh->outbuf); + free(strmh->holdbuf); + + pthread_cond_destroy(&strmh->cb_cond); + pthread_mutex_destroy(&strmh->cb_mutex); + + DL_DELETE(strmh->devh->streams, strmh); + free(strmh); +} diff --git a/third_party/libuvc/src/test.c b/third_party/libuvc/src/test.c new file mode 100644 index 0000000000..e48cfc96fe --- /dev/null +++ b/third_party/libuvc/src/test.c @@ -0,0 +1,153 @@ +/********************************************************************* +* Software License Agreement (BSD License) +* +* Copyright (C) 2010-2012 Ken Tossell +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of the author nor other contributors may be +* used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*********************************************************************/ +#include +#include + +#include "libuvc/libuvc.h" + +void cb(uvc_frame_t *frame, void *ptr) { + uvc_frame_t *bgr; + uvc_error_t ret; + IplImage* cvImg; + + printf("callback! length = %u, ptr = %d\n", frame->data_bytes, (int) ptr); + + bgr = uvc_allocate_frame(frame->width * frame->height * 3); + if (!bgr) { + printf("unable to allocate bgr frame!"); + return; + } + + ret = uvc_any2bgr(frame, bgr); + if (ret) { + uvc_perror(ret, "uvc_any2bgr"); + uvc_free_frame(bgr); + return; + } + + cvImg = cvCreateImageHeader( + cvSize(bgr->width, bgr->height), + IPL_DEPTH_8U, + 3); + + cvSetData(cvImg, bgr->data, bgr->width * 3); + + cvNamedWindow("Test", CV_WINDOW_AUTOSIZE); + cvShowImage("Test", cvImg); + cvWaitKey(10); + + cvReleaseImageHeader(&cvImg); + + uvc_free_frame(bgr); +} + +int main(int argc, char **argv) { + uvc_context_t *ctx; + uvc_error_t res; + uvc_device_t *dev; + uvc_device_handle_t *devh; + uvc_stream_ctrl_t ctrl; + + res = uvc_init(&ctx, NULL); + + if (res < 0) { + uvc_perror(res, "uvc_init"); + return res; + } + + puts("UVC initialized"); + + res = uvc_find_device( + ctx, &dev, + 0, 0, NULL); + + if (res < 0) { + uvc_perror(res, "uvc_find_device"); + } else { + puts("Device found"); + + res = uvc_open(dev, &devh); + + if (res < 0) { + uvc_perror(res, "uvc_open"); + } else { + puts("Device opened"); + + uvc_print_diag(devh, stderr); + + res = uvc_get_stream_ctrl_format_size( + devh, &ctrl, UVC_FRAME_FORMAT_YUYV, 640, 480, 30 + ); + + uvc_print_stream_ctrl(&ctrl, stderr); + + if (res < 0) { + uvc_perror(res, "get_mode"); + } else { + res = uvc_start_streaming(devh, &ctrl, cb, 12345, 0); + + if (res < 0) { + uvc_perror(res, "start_streaming"); + } else { + puts("Streaming for 10 seconds..."); + uvc_error_t resAEMODE = uvc_set_ae_mode(devh, 1); + uvc_perror(resAEMODE, "set_ae_mode"); + int i; + for (i = 1; i <= 10; i++) { + /* uvc_error_t resPT = uvc_set_pantilt_abs(devh, i * 20 * 3600, 0); */ + /* uvc_perror(resPT, "set_pt_abs"); */ + uvc_error_t resEXP = uvc_set_exposure_abs(devh, 20 + i * 5); + uvc_perror(resEXP, "set_exp_abs"); + + sleep(1); + } + sleep(10); + uvc_stop_streaming(devh); + puts("Done streaming."); + } + } + + uvc_close(devh); + puts("Device closed"); + } + + uvc_unref_device(dev); + } + + uvc_exit(ctx); + puts("UVC exited"); + + return 0; +} +