<commit_before>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#include "../../../core/Setup.h"

#if defined(__ANDROID__) && OUZEL_COMPILE_OPENGL

#include <algorithm>
#include "OGLRenderDeviceAndroid.hpp"
#include "../EGLErrorCategory.hpp"
#include "../../../core/Engine.hpp"
#include "../../../core/Window.hpp"
#include "../../../core/android/NativeWindowAndroid.hpp"
#include "../../../utils/Log.hpp"
#include "../../../utils/Utils.hpp"

namespace ouzel::graphics::opengl::android
{
    namespace
    {
        const egl::ErrorCategory eglErrorCategory{};

        std::vector<EGLConfig> chooseConfig(EGLDisplay display,
                                            bool depth,
                                            bool stencil,
                                            std::uint32_t sampleCount,
                                            EGLint version)
        {
            const EGLint attributeList[] = {
                EGL_RED_SIZE, 8,
                EGL_GREEN_SIZE, 8,
                EGL_BLUE_SIZE, 8,
                EGL_ALPHA_SIZE, 8,
                EGL_DEPTH_SIZE, depth ? 24 : 0,
                EGL_STENCIL_SIZE, stencil ? 8 : 0,
                EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
#if OUZEL_OPENGLES
                EGL_RENDERABLE_TYPE, (version >= 3) ? EGL_OPENGL_ES3_BIT :
                                     (version == 2) ? EGL_OPENGL_ES2_BIT :
                                     EGL_OPENGL_ES_BIT,
#else
                EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
#endif
                EGL_SAMPLE_BUFFERS, (sampleCount > 1) ? 1 : 0,
                EGL_SAMPLES, static_cast<EGLint>(sampleCount),
                EGL_NONE
            };

            EGLint configCount;
            if (!eglChooseConfig(display, attributeList, nullptr, 0, &configCount))
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to choose EGL config");

            if (configCount == 0) return {};

            std::vector<EGLConfig> configs(configCount);
            if (!eglChooseConfig(display, attributeList, configs.data(), configCount, &configCount))
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to choose EGL config");

            return configs;
        }
    }

    RenderDevice::RenderDevice(const Settings& settings,
                               core::Window& initWindow,
                               const std::function<void(const Event&)>& initCallback):
        opengl::RenderDevice(settings, initWindow, initCallback)
    {
        embedded = true;

        display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

        if (display == EGL_NO_DISPLAY)
            throw std::runtime_error("Failed to get display");

        if (!eglInitialize(display, nullptr, nullptr))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to initialize EGL");

        const auto eglVersionPtr = eglQueryString(display, EGL_VERSION);
        if (!eglVersionPtr)
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get EGL version");
        logger.log(Log::Level::all) << "EGL version: " << eglVersionPtr;

        const auto eglExtensionsPtr = eglQueryString(display, EGL_EXTENSIONS);
        if (!eglExtensionsPtr)
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get EGL extensions");
        const auto eglExtensions = explodeString(eglExtensionsPtr, ' ');
        logger.log(Log::Level::all) << "Supported EGL extensions: " << eglExtensions;

        auto windowAndroid = static_cast<core::android::NativeWindow*>(window.getNativeWindow());

        if (std::find(eglExtensions.begin(), eglExtensions.end(), "EGL_KHR_create_context") != eglExtensions.end())
        {
            const auto configs = chooseConfig(display, settings.depth, settings.stencil, settings.sampleCount, 3);

            if (!configs.empty())
            {
                if (!eglBindAPI(EGL_OPENGL_ES_API))
                    throw std::system_error(eglGetError(), eglErrorCategory, "Failed to bind OpenGL ES API");

                EGLint format;
                if (!eglGetConfigAttrib(display, configs[0], EGL_NATIVE_VISUAL_ID, &format))
                    throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get config attribute");

                ANativeWindow_setBuffersGeometry(windowAndroid->getNativeWindow(), 0, 0, format);

                surface = eglCreateWindowSurface(display, configs[0], windowAndroid->getNativeWindow(), nullptr);
                if (surface == EGL_NO_SURFACE)
                    throw std::system_error(eglGetError(), eglErrorCategory, "Failed to create EGL window surface");

                const EGLint contextAttributes[] = {
                    EGL_CONTEXT_MAJOR_VERSION, 3,
                    EGL_CONTEXT_MINOR_VERSION, 0,
                    EGL_CONTEXT_FLAGS_KHR, settings.debugRenderer ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0,
                    EGL_NONE
                };

                context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, contextAttributes);

                if (context != EGL_NO_CONTEXT)
                {
                    apiVersion = ApiVersion(3, 0);
                    logger.log(Log::Level::info) << "EGL OpenGL ES " << 3 << " context created";
                }
                else // TODO: use RAII for surface
                    eglDestroySurface(display, surface);
            }
        }

        if (context == EGL_NO_CONTEXT)
        {
            const auto configs = chooseConfig(display, settings.depth, settings.stencil, settings.sampleCount, 2);
            if (configs.empty())
                throw std::runtime_error("No EGL config found");

            if (!eglBindAPI(EGL_OPENGL_ES_API))
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to bind OpenGL ES API");

            EGLint format;
            if (!eglGetConfigAttrib(display, configs[0], EGL_NATIVE_VISUAL_ID, &format))
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get config attribute");

            auto windowAndroid = static_cast<core::android::NativeWindow*>(window.getNativeWindow());

            ANativeWindow_setBuffersGeometry(windowAndroid->getNativeWindow(), 0, 0, format);

            surface = eglCreateWindowSurface(display, configs[0], windowAndroid->getNativeWindow(), nullptr);
            if (surface == EGL_NO_SURFACE)
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to create EGL window surface");

            const EGLint contextAttributes[] = {
                EGL_CONTEXT_CLIENT_VERSION, 2,
                EGL_NONE
            };

            context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, contextAttributes);
            if (context == EGL_NO_CONTEXT)
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to create EGL context");

            apiVersion = ApiVersion(2, 0);
            logger.log(Log::Level::info) << "EGL OpenGL ES " << 2 << " context created";
        }

        if (!eglMakeCurrent(display, surface, surface, context))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to set current EGL context");

        if (!eglSwapInterval(display, settings.verticalSync ? 1 : 0))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to set EGL frame interval");

        EGLint surfaceWidth;
        EGLint surfaceHeight;

        if (!eglQuerySurface(display, surface, EGL_WIDTH, &surfaceWidth) ||
            !eglQuerySurface(display, surface, EGL_HEIGHT, &surfaceHeight))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get query window size");

        init(surfaceWidth, surfaceHeight);

        if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to unset EGL context");

        running = true;
        renderThread = thread::Thread(&RenderDevice::renderMain, this);
    }

    RenderDevice::~RenderDevice()
    {
        running = false;
        CommandBuffer commandBuffer;
        commandBuffer.pushCommand(std::make_unique<PresentCommand>());
        submitCommandBuffer(std::move(commandBuffer));

        if (renderThread.isJoinable()) renderThread.join();

        if (display != EGL_NO_DISPLAY)
        {
            if (context != EGL_NO_CONTEXT)
            {
                eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
                eglDestroyContext(display, context);
            }

            if (surface != EGL_NO_SURFACE)
                eglDestroySurface(display, surface);

            eglTerminate(display);
        }
    }

    void RenderDevice::reload()
    {
        running = false;
        CommandBuffer commandBuffer;
        commandBuffer.pushCommand(std::make_unique<PresentCommand>());
        submitCommandBuffer(std::move(commandBuffer));

        if (renderThread.isJoinable()) renderThread.join();

        const auto eglExtensionsPtr = eglQueryString(display, EGL_EXTENSIONS);
        if (!eglExtensionsPtr)
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get EGL extensions");
        const auto eglExtensions = explodeString(eglExtensionsPtr, ' ');
        logger.log(Log::Level::all) << "Supported EGL extensions: " << eglExtensions;

        auto windowAndroid = static_cast<core::android::NativeWindow*>(window.getNativeWindow());

        if (std::find(eglExtensions.begin(), eglExtensions.end(), "EGL_KHR_create_context") != eglExtensions.end())
        {
            const auto configs = chooseConfig(display, settings.depth, settings.stencil, settings.sampleCount, 3);

            if (!configs.empty())
            {
                if (!eglBindAPI(EGL_OPENGL_ES_API))
                    throw std::system_error(eglGetError(), eglErrorCategory, "Failed to bind OpenGL ES API");

                EGLint format;
                if (!eglGetConfigAttrib(display, configs[0], EGL_NATIVE_VISUAL_ID, &format))
                    throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get config attribute");

                ANativeWindow_setBuffersGeometry(windowAndroid->getNativeWindow(), 0, 0, format);

                surface = eglCreateWindowSurface(display, configs[0], windowAndroid->getNativeWindow(), nullptr);
                if (surface == EGL_NO_SURFACE)
                    throw std::system_error(eglGetError(), eglErrorCategory, "Failed to create EGL window surface");

                const EGLint contextAttributes[] = {
                    EGL_CONTEXT_MAJOR_VERSION, 3,
                    EGL_CONTEXT_MINOR_VERSION, 0,
                    EGL_CONTEXT_FLAGS_KHR, settings.debugRenderer ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0,
                    EGL_NONE
                };

                context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, contextAttributes);

                if (context != EGL_NO_CONTEXT)
                {
                    apiVersion = ApiVersion(3, 0);
                    logger.log(Log::Level::info) << "EGL OpenGL ES " << 3 << " context created";
                }
                else // TODO: use RAII for surface
                    eglDestroySurface(display, surface);
            }
        }

        if (context == EGL_NO_CONTEXT)
        {
            const auto configs = chooseConfig(display, settings.depth, settings.stencil, settings.sampleCount, 2);
            if (configs.empty())
                throw std::runtime_error("No EGL config found");

            if (!eglBindAPI(EGL_OPENGL_ES_API))
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to bind OpenGL ES API");

            EGLint format;
            if (!eglGetConfigAttrib(display, configs[0], EGL_NATIVE_VISUAL_ID, &format))
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get config attribute");

            auto windowAndroid = static_cast<core::android::NativeWindow*>(window.getNativeWindow());

            ANativeWindow_setBuffersGeometry(windowAndroid->getNativeWindow(), 0, 0, format);

            surface = eglCreateWindowSurface(display, configs[0], windowAndroid->getNativeWindow(), nullptr);
            if (surface == EGL_NO_SURFACE)
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to create EGL window surface");

            const EGLint contextAttributes[] = {
                EGL_CONTEXT_CLIENT_VERSION, 2,
                EGL_NONE
            };

            context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, contextAttributes);
            if (context == EGL_NO_CONTEXT)
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to create EGL context");

            apiVersion = ApiVersion(2, 0);
            logger.log(Log::Level::info) << "EGL OpenGL ES " << 2 << " context created";
        }

        if (!eglMakeCurrent(display, surface, surface, context))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to set current EGL context");

        if (!eglSwapInterval(display, verticalSync ? 1 : 0))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to set EGL frame interval");

        EGLint surfaceWidth;
        EGLint surfaceHeight;

        if (!eglQuerySurface(display, surface, EGL_WIDTH, &surfaceWidth) ||
            !eglQuerySurface(display, surface, EGL_HEIGHT, &surfaceHeight))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get query window size");

        frameBufferWidth = surfaceWidth;
        frameBufferHeight = surfaceHeight;

        stateCache = StateCache();

        glDisableProc(GL_DITHER);
        glDepthFuncProc(GL_LEQUAL);

        if (const auto error = glGetErrorProc(); error != GL_NO_ERROR)
            throw std::system_error(makeErrorCode(error), "Failed to set depth function");

        if (glGenVertexArraysProc) glGenVertexArraysProc(1, &vertexArrayId);

        for (const auto& resource : resources)
            if (resource) resource->invalidate();

        for (const auto& resource : resources)
            if (resource) resource->restore();

        if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to unset EGL context");

        running = true;
        renderThread = thread::Thread(&RenderDevice::renderMain, this);
    }

    void RenderDevice::destroy()
    {
        running = false;
        CommandBuffer commandBuffer;
        commandBuffer.pushCommand(std::make_unique<PresentCommand>());
        submitCommandBuffer(std::move(commandBuffer));

        if (renderThread.isJoinable()) renderThread.join();

        if (context)
        {
            if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
                logger.log(Log::Level::error) << "Failed to unset EGL context";

            if (!eglDestroyContext(display, context))
                logger.log(Log::Level::error) << "Failed to destroy EGL context";

            context = nullptr;
        }

        if (surface)
        {
            if (!eglDestroySurface(display, surface))
                logger.log(Log::Level::error) << "Failed to destroy EGL surface";

            surface = nullptr;
        }
    }

    void RenderDevice::present()
    {
        if (eglSwapBuffers(display, surface) != EGL_TRUE)
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to swap buffers");
    }

    void RenderDevice::renderMain()
    {
        thread::setCurrentThreadName("Render");

        if (!eglMakeCurrent(display, surface, surface, context))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to set current EGL context");

        while (running)
        {
            try
            {
                process();
            }
            catch (const std::exception& e)
            {
                logger.log(Log::Level::error) << e.what();
            }
        }

        if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to unset EGL context");
    }
}

#endif
<commit_msg>Fix the Android build<commit_after>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#include "../../../core/Setup.h"

#if defined(__ANDROID__) && OUZEL_COMPILE_OPENGL

#include <algorithm>
#include "OGLRenderDeviceAndroid.hpp"
#include "../EGLErrorCategory.hpp"
#include "../../../core/Engine.hpp"
#include "../../../core/Window.hpp"
#include "../../../core/android/NativeWindowAndroid.hpp"
#include "../../../utils/Log.hpp"
#include "../../../utils/Utils.hpp"

namespace ouzel::graphics::opengl::android
{
    namespace
    {
        const egl::ErrorCategory eglErrorCategory{};

        std::vector<EGLConfig> chooseConfig(EGLDisplay display,
                                            bool depth,
                                            bool stencil,
                                            std::uint32_t sampleCount,
                                            EGLint version)
        {
            const EGLint attributeList[] = {
                EGL_RED_SIZE, 8,
                EGL_GREEN_SIZE, 8,
                EGL_BLUE_SIZE, 8,
                EGL_ALPHA_SIZE, 8,
                EGL_DEPTH_SIZE, depth ? 24 : 0,
                EGL_STENCIL_SIZE, stencil ? 8 : 0,
                EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
#if OUZEL_OPENGLES
                EGL_RENDERABLE_TYPE, (version >= 3) ? EGL_OPENGL_ES3_BIT :
                                     (version == 2) ? EGL_OPENGL_ES2_BIT :
                                     EGL_OPENGL_ES_BIT,
#else
                EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
#endif
                EGL_SAMPLE_BUFFERS, (sampleCount > 1) ? 1 : 0,
                EGL_SAMPLES, static_cast<EGLint>(sampleCount),
                EGL_NONE
            };

            EGLint configCount;
            if (!eglChooseConfig(display, attributeList, nullptr, 0, &configCount))
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to choose EGL config");

            if (configCount == 0) return {};

            std::vector<EGLConfig> configs(configCount);
            if (!eglChooseConfig(display, attributeList, configs.data(), configCount, &configCount))
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to choose EGL config");

            return configs;
        }
    }

    RenderDevice::RenderDevice(const Settings& settings,
                               core::Window& initWindow,
                               const std::function<void(const Event&)>& initCallback):
        opengl::RenderDevice(settings, initWindow, initCallback)
    {
        embedded = true;

        display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

        if (display == EGL_NO_DISPLAY)
            throw std::runtime_error("Failed to get display");

        if (!eglInitialize(display, nullptr, nullptr))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to initialize EGL");

        const auto eglVersionPtr = eglQueryString(display, EGL_VERSION);
        if (!eglVersionPtr)
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get EGL version");
        logger.log(Log::Level::all) << "EGL version: " << eglVersionPtr;

        const auto eglExtensionsPtr = eglQueryString(display, EGL_EXTENSIONS);
        if (!eglExtensionsPtr)
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get EGL extensions");
        const auto eglExtensions = explodeString(eglExtensionsPtr, ' ');
        logger.log(Log::Level::all) << "Supported EGL extensions: " << eglExtensions;

        auto windowAndroid = static_cast<core::android::NativeWindow*>(window.getNativeWindow());

        if (std::find(eglExtensions.begin(), eglExtensions.end(), "EGL_KHR_create_context") != eglExtensions.end())
        {
            const auto configs = chooseConfig(display, settings.depth, settings.stencil, settings.sampleCount, 3);

            if (!configs.empty())
            {
                if (!eglBindAPI(EGL_OPENGL_ES_API))
                    throw std::system_error(eglGetError(), eglErrorCategory, "Failed to bind OpenGL ES API");

                EGLint format;
                if (!eglGetConfigAttrib(display, configs[0], EGL_NATIVE_VISUAL_ID, &format))
                    throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get config attribute");

                ANativeWindow_setBuffersGeometry(windowAndroid->getNativeWindow(), 0, 0, format);

                surface = eglCreateWindowSurface(display, configs[0], windowAndroid->getNativeWindow(), nullptr);
                if (surface == EGL_NO_SURFACE)
                    throw std::system_error(eglGetError(), eglErrorCategory, "Failed to create EGL window surface");

                const EGLint contextAttributes[] = {
                    EGL_CONTEXT_MAJOR_VERSION, 3,
                    EGL_CONTEXT_MINOR_VERSION, 0,
                    EGL_CONTEXT_FLAGS_KHR, settings.debugRenderer ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0,
                    EGL_NONE
                };

                context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, contextAttributes);

                if (context != EGL_NO_CONTEXT)
                {
                    apiVersion = ApiVersion(3, 0);
                    logger.log(Log::Level::info) << "EGL OpenGL ES " << 3 << " context created";
                }
                else // TODO: use RAII for surface
                    eglDestroySurface(display, surface);
            }
        }

        if (context == EGL_NO_CONTEXT)
        {
            const auto configs = chooseConfig(display, settings.depth, settings.stencil, settings.sampleCount, 2);
            if (configs.empty())
                throw std::runtime_error("No EGL config found");

            if (!eglBindAPI(EGL_OPENGL_ES_API))
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to bind OpenGL ES API");

            EGLint format;
            if (!eglGetConfigAttrib(display, configs[0], EGL_NATIVE_VISUAL_ID, &format))
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get config attribute");

            auto windowAndroid = static_cast<core::android::NativeWindow*>(window.getNativeWindow());

            ANativeWindow_setBuffersGeometry(windowAndroid->getNativeWindow(), 0, 0, format);

            surface = eglCreateWindowSurface(display, configs[0], windowAndroid->getNativeWindow(), nullptr);
            if (surface == EGL_NO_SURFACE)
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to create EGL window surface");

            const EGLint contextAttributes[] = {
                EGL_CONTEXT_CLIENT_VERSION, 2,
                EGL_NONE
            };

            context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, contextAttributes);
            if (context == EGL_NO_CONTEXT)
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to create EGL context");

            apiVersion = ApiVersion(2, 0);
            logger.log(Log::Level::info) << "EGL OpenGL ES " << 2 << " context created";
        }

        if (!eglMakeCurrent(display, surface, surface, context))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to set current EGL context");

        if (!eglSwapInterval(display, settings.verticalSync ? 1 : 0))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to set EGL frame interval");

        EGLint surfaceWidth;
        EGLint surfaceHeight;

        if (!eglQuerySurface(display, surface, EGL_WIDTH, &surfaceWidth) ||
            !eglQuerySurface(display, surface, EGL_HEIGHT, &surfaceHeight))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get query window size");

        init(surfaceWidth, surfaceHeight);

        if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to unset EGL context");

        running = true;
        renderThread = thread::Thread(&RenderDevice::renderMain, this);
    }

    RenderDevice::~RenderDevice()
    {
        running = false;
        CommandBuffer commandBuffer;
        commandBuffer.pushCommand(std::make_unique<PresentCommand>());
        submitCommandBuffer(std::move(commandBuffer));

        if (renderThread.isJoinable()) renderThread.join();

        if (display != EGL_NO_DISPLAY)
        {
            if (context != EGL_NO_CONTEXT)
            {
                eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
                eglDestroyContext(display, context);
            }

            if (surface != EGL_NO_SURFACE)
                eglDestroySurface(display, surface);

            eglTerminate(display);
        }
    }

    void RenderDevice::reload()
    {
        running = false;
        CommandBuffer commandBuffer;
        commandBuffer.pushCommand(std::make_unique<PresentCommand>());
        submitCommandBuffer(std::move(commandBuffer));

        if (renderThread.isJoinable()) renderThread.join();

        const auto eglExtensionsPtr = eglQueryString(display, EGL_EXTENSIONS);
        if (!eglExtensionsPtr)
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get EGL extensions");
        const auto eglExtensions = explodeString(eglExtensionsPtr, ' ');
        logger.log(Log::Level::all) << "Supported EGL extensions: " << eglExtensions;

        auto windowAndroid = static_cast<core::android::NativeWindow*>(window.getNativeWindow());

        if (std::find(eglExtensions.begin(), eglExtensions.end(), "EGL_KHR_create_context") != eglExtensions.end())
        {
            const auto configs = chooseConfig(display, depth, stencil, sampleCount, 3);

            if (!configs.empty())
            {
                if (!eglBindAPI(EGL_OPENGL_ES_API))
                    throw std::system_error(eglGetError(), eglErrorCategory, "Failed to bind OpenGL ES API");

                EGLint format;
                if (!eglGetConfigAttrib(display, configs[0], EGL_NATIVE_VISUAL_ID, &format))
                    throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get config attribute");

                ANativeWindow_setBuffersGeometry(windowAndroid->getNativeWindow(), 0, 0, format);

                surface = eglCreateWindowSurface(display, configs[0], windowAndroid->getNativeWindow(), nullptr);
                if (surface == EGL_NO_SURFACE)
                    throw std::system_error(eglGetError(), eglErrorCategory, "Failed to create EGL window surface");

                const EGLint contextAttributes[] = {
                    EGL_CONTEXT_MAJOR_VERSION, 3,
                    EGL_CONTEXT_MINOR_VERSION, 0,
                    EGL_CONTEXT_FLAGS_KHR, debugRenderer ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0,
                    EGL_NONE
                };

                context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, contextAttributes);

                if (context != EGL_NO_CONTEXT)
                {
                    apiVersion = ApiVersion(3, 0);
                    logger.log(Log::Level::info) << "EGL OpenGL ES " << 3 << " context created";
                }
                else // TODO: use RAII for surface
                    eglDestroySurface(display, surface);
            }
        }

        if (context == EGL_NO_CONTEXT)
        {
            const auto configs = chooseConfig(display, depth, stencil, sampleCount, 2);
            if (configs.empty())
                throw std::runtime_error("No EGL config found");

            if (!eglBindAPI(EGL_OPENGL_ES_API))
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to bind OpenGL ES API");

            EGLint format;
            if (!eglGetConfigAttrib(display, configs[0], EGL_NATIVE_VISUAL_ID, &format))
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get config attribute");

            auto windowAndroid = static_cast<core::android::NativeWindow*>(window.getNativeWindow());

            ANativeWindow_setBuffersGeometry(windowAndroid->getNativeWindow(), 0, 0, format);

            surface = eglCreateWindowSurface(display, configs[0], windowAndroid->getNativeWindow(), nullptr);
            if (surface == EGL_NO_SURFACE)
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to create EGL window surface");

            const EGLint contextAttributes[] = {
                EGL_CONTEXT_CLIENT_VERSION, 2,
                EGL_NONE
            };

            context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, contextAttributes);
            if (context == EGL_NO_CONTEXT)
                throw std::system_error(eglGetError(), eglErrorCategory, "Failed to create EGL context");

            apiVersion = ApiVersion(2, 0);
            logger.log(Log::Level::info) << "EGL OpenGL ES " << 2 << " context created";
        }

        if (!eglMakeCurrent(display, surface, surface, context))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to set current EGL context");

        if (!eglSwapInterval(display, verticalSync ? 1 : 0))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to set EGL frame interval");

        EGLint surfaceWidth;
        EGLint surfaceHeight;

        if (!eglQuerySurface(display, surface, EGL_WIDTH, &surfaceWidth) ||
            !eglQuerySurface(display, surface, EGL_HEIGHT, &surfaceHeight))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to get query window size");

        frameBufferWidth = surfaceWidth;
        frameBufferHeight = surfaceHeight;

        stateCache = StateCache();

        glDisableProc(GL_DITHER);
        glDepthFuncProc(GL_LEQUAL);

        if (const auto error = glGetErrorProc(); error != GL_NO_ERROR)
            throw std::system_error(makeErrorCode(error), "Failed to set depth function");

        if (glGenVertexArraysProc) glGenVertexArraysProc(1, &vertexArrayId);

        for (const auto& resource : resources)
            if (resource) resource->invalidate();

        for (const auto& resource : resources)
            if (resource) resource->restore();

        if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to unset EGL context");

        running = true;
        renderThread = thread::Thread(&RenderDevice::renderMain, this);
    }

    void RenderDevice::destroy()
    {
        running = false;
        CommandBuffer commandBuffer;
        commandBuffer.pushCommand(std::make_unique<PresentCommand>());
        submitCommandBuffer(std::move(commandBuffer));

        if (renderThread.isJoinable()) renderThread.join();

        if (context)
        {
            if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
                logger.log(Log::Level::error) << "Failed to unset EGL context";

            if (!eglDestroyContext(display, context))
                logger.log(Log::Level::error) << "Failed to destroy EGL context";

            context = nullptr;
        }

        if (surface)
        {
            if (!eglDestroySurface(display, surface))
                logger.log(Log::Level::error) << "Failed to destroy EGL surface";

            surface = nullptr;
        }
    }

    void RenderDevice::present()
    {
        if (eglSwapBuffers(display, surface) != EGL_TRUE)
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to swap buffers");
    }

    void RenderDevice::renderMain()
    {
        thread::setCurrentThreadName("Render");

        if (!eglMakeCurrent(display, surface, surface, context))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to set current EGL context");

        while (running)
        {
            try
            {
                process();
            }
            catch (const std::exception& e)
            {
                logger.log(Log::Level::error) << e.what();
            }
        }

        if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
            throw std::system_error(eglGetError(), eglErrorCategory, "Failed to unset EGL context");
    }
}

#endif
<|endoftext|>


<commit_before>//===- BasicInliner.cpp - Basic function level inliner --------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines a simple function based inliner that does not use
// call graph information.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "basicinliner"
#include "llvm/Module.h"
#include "llvm/Function.h"
#include "llvm/Transforms/Utils/BasicInliner.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/SmallPtrSet.h"
#include <vector>

using namespace llvm;

static cl::opt<unsigned>
BasicInlineThreshold("basic-inline-threshold", cl::Hidden, cl::init(200),
   cl::desc("Control the amount of basic inlining to perform (default = 200)"));

namespace llvm {

  /// BasicInlinerImpl - BasicInliner implemantation class. This hides
  /// container info, used by basic inliner, from public interface.
  struct BasicInlinerImpl {

    BasicInlinerImpl(const BasicInlinerImpl&); // DO NOT IMPLEMENT
    void operator=(const BasicInlinerImpl&); // DO NO IMPLEMENT
  public:
    BasicInlinerImpl(TargetData *T) : TD(T) {}

    /// addFunction - Add function into the list of functions to process.
    /// All functions must be inserted using this interface before invoking
    /// inlineFunctions().
    void addFunction(Function *F) {
      Functions.push_back(F);
    }

    /// neverInlineFunction - Sometimes a function is never to be inlined
    /// because of one or other reason.
    void neverInlineFunction(Function *F) {
      NeverInline.insert(F);
    }

    /// inlineFuctions - Walk all call sites in all functions supplied by
    /// client. Inline as many call sites as possible. Delete completely
    /// inlined functions.
    void inlineFunctions();

  private:
    TargetData *TD;
    std::vector<Function *> Functions;
    SmallPtrSet<const Function *, 16> NeverInline;
    SmallPtrSet<Function *, 8> DeadFunctions;
    InlineCostAnalyzer CA;
  };

/// inlineFuctions - Walk all call sites in all functions supplied by
/// client. Inline as many call sites as possible. Delete completely
/// inlined functions.
void BasicInlinerImpl::inlineFunctions() {

  // Scan through and identify all call sites ahead of time so that we only
  // inline call sites in the original functions, not call sites that result
  // from inlining other functions.
  std::vector<CallSite> CallSites;

  for (std::vector<Function *>::iterator FI = Functions.begin(),
         FE = Functions.end(); FI != FE; ++FI) {
    Function *F = *FI;
    for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB)
      for (BasicBlock::iterator I = BB->begin(); I != BB->end(); ++I) {
        CallSite CS = CallSite::get(I);
        if (CS.getInstruction() && CS.getCalledFunction()
            && !CS.getCalledFunction()->isDeclaration())
          CallSites.push_back(CS);
      }
  }

  DEBUG(errs() << ": " << CallSites.size() << " call sites.\n");

  // Inline call sites.
  bool Changed = false;
  do {
    Changed = false;
    for (unsigned index = 0; index != CallSites.size() && !CallSites.empty();
         ++index) {
      CallSite CS = CallSites[index];
      if (Function *Callee = CS.getCalledFunction()) {

        // Eliminate calls that are never inlinable.
        if (Callee->isDeclaration() ||
            CS.getInstruction()->getParent()->getParent() == Callee) {
          CallSites.erase(CallSites.begin() + index);
          --index;
          continue;
        }
        InlineCost IC = CA.getInlineCost(CS, NeverInline);
        if (IC.isAlways()) {
          DEBUG(errs() << "  Inlining: cost=always"
                       <<", call: " << *CS.getInstruction());
        } else if (IC.isNever()) {
          DEBUG(errs() << "  NOT Inlining: cost=never"
                       <<", call: " << *CS.getInstruction());
          continue;
        } else {
          int Cost = IC.getValue();

          if (Cost >= (int) BasicInlineThreshold) {
            DEBUG(errs() << "  NOT Inlining: cost = " << Cost
                         << ", call: " <<  *CS.getInstruction());
            continue;
          } else {
            DEBUG(errs() << "  Inlining: cost = " << Cost
                         << ", call: " <<  *CS.getInstruction());
          }
        }

        // Inline
        if (InlineFunction(CS, NULL, TD)) {
          if (Callee->use_empty() && (Callee->hasLocalLinkage() ||
                                      Callee->hasAvailableExternallyLinkage()))
            DeadFunctions.insert(Callee);
          Changed = true;
          CallSites.erase(CallSites.begin() + index);
          --index;
        }
      }
    }
  } while (Changed);

  // Remove completely inlined functions from module.
  for(SmallPtrSet<Function *, 8>::iterator I = DeadFunctions.begin(),
        E = DeadFunctions.end(); I != E; ++I) {
    Function *D = *I;
    Module *M = D->getParent();
    M->getFunctionList().remove(D);
  }
}

BasicInliner::BasicInliner(TargetData *TD) {
  Impl = new BasicInlinerImpl(TD);
}

BasicInliner::~BasicInliner() {
  delete Impl;
}

/// addFunction - Add function into the list of functions to process.
/// All functions must be inserted using this interface before invoking
/// inlineFunctions().
void BasicInliner::addFunction(Function *F) {
  Impl->addFunction(F);
}

/// neverInlineFunction - Sometimes a function is never to be inlined because
/// of one or other reason.
void BasicInliner::neverInlineFunction(Function *F) {
  Impl->neverInlineFunction(F);
}

/// inlineFuctions - Walk all call sites in all functions supplied by
/// client. Inline as many call sites as possible. Delete completely
/// inlined functions.
void BasicInliner::inlineFunctions() {
  Impl->inlineFunctions();
}

}
<commit_msg>Change errs() to dbgs().<commit_after>//===- BasicInliner.cpp - Basic function level inliner --------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines a simple function based inliner that does not use
// call graph information.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "basicinliner"
#include "llvm/Module.h"
#include "llvm/Function.h"
#include "llvm/Transforms/Utils/BasicInliner.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/SmallPtrSet.h"
#include <vector>

using namespace llvm;

static cl::opt<unsigned>
BasicInlineThreshold("basic-inline-threshold", cl::Hidden, cl::init(200),
   cl::desc("Control the amount of basic inlining to perform (default = 200)"));

namespace llvm {

  /// BasicInlinerImpl - BasicInliner implemantation class. This hides
  /// container info, used by basic inliner, from public interface.
  struct BasicInlinerImpl {

    BasicInlinerImpl(const BasicInlinerImpl&); // DO NOT IMPLEMENT
    void operator=(const BasicInlinerImpl&); // DO NO IMPLEMENT
  public:
    BasicInlinerImpl(TargetData *T) : TD(T) {}

    /// addFunction - Add function into the list of functions to process.
    /// All functions must be inserted using this interface before invoking
    /// inlineFunctions().
    void addFunction(Function *F) {
      Functions.push_back(F);
    }

    /// neverInlineFunction - Sometimes a function is never to be inlined
    /// because of one or other reason.
    void neverInlineFunction(Function *F) {
      NeverInline.insert(F);
    }

    /// inlineFuctions - Walk all call sites in all functions supplied by
    /// client. Inline as many call sites as possible. Delete completely
    /// inlined functions.
    void inlineFunctions();

  private:
    TargetData *TD;
    std::vector<Function *> Functions;
    SmallPtrSet<const Function *, 16> NeverInline;
    SmallPtrSet<Function *, 8> DeadFunctions;
    InlineCostAnalyzer CA;
  };

/// inlineFuctions - Walk all call sites in all functions supplied by
/// client. Inline as many call sites as possible. Delete completely
/// inlined functions.
void BasicInlinerImpl::inlineFunctions() {

  // Scan through and identify all call sites ahead of time so that we only
  // inline call sites in the original functions, not call sites that result
  // from inlining other functions.
  std::vector<CallSite> CallSites;

  for (std::vector<Function *>::iterator FI = Functions.begin(),
         FE = Functions.end(); FI != FE; ++FI) {
    Function *F = *FI;
    for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB)
      for (BasicBlock::iterator I = BB->begin(); I != BB->end(); ++I) {
        CallSite CS = CallSite::get(I);
        if (CS.getInstruction() && CS.getCalledFunction()
            && !CS.getCalledFunction()->isDeclaration())
          CallSites.push_back(CS);
      }
  }

  DEBUG(dbgs() << ": " << CallSites.size() << " call sites.\n");

  // Inline call sites.
  bool Changed = false;
  do {
    Changed = false;
    for (unsigned index = 0; index != CallSites.size() && !CallSites.empty();
         ++index) {
      CallSite CS = CallSites[index];
      if (Function *Callee = CS.getCalledFunction()) {

        // Eliminate calls that are never inlinable.
        if (Callee->isDeclaration() ||
            CS.getInstruction()->getParent()->getParent() == Callee) {
          CallSites.erase(CallSites.begin() + index);
          --index;
          continue;
        }
        InlineCost IC = CA.getInlineCost(CS, NeverInline);
        if (IC.isAlways()) {
          DEBUG(dbgs() << "  Inlining: cost=always"
                       <<", call: " << *CS.getInstruction());
        } else if (IC.isNever()) {
          DEBUG(dbgs() << "  NOT Inlining: cost=never"
                       <<", call: " << *CS.getInstruction());
          continue;
        } else {
          int Cost = IC.getValue();

          if (Cost >= (int) BasicInlineThreshold) {
            DEBUG(dbgs() << "  NOT Inlining: cost = " << Cost
                         << ", call: " <<  *CS.getInstruction());
            continue;
          } else {
            DEBUG(dbgs() << "  Inlining: cost = " << Cost
                         << ", call: " <<  *CS.getInstruction());
          }
        }

        // Inline
        if (InlineFunction(CS, NULL, TD)) {
          if (Callee->use_empty() && (Callee->hasLocalLinkage() ||
                                      Callee->hasAvailableExternallyLinkage()))
            DeadFunctions.insert(Callee);
          Changed = true;
          CallSites.erase(CallSites.begin() + index);
          --index;
        }
      }
    }
  } while (Changed);

  // Remove completely inlined functions from module.
  for(SmallPtrSet<Function *, 8>::iterator I = DeadFunctions.begin(),
        E = DeadFunctions.end(); I != E; ++I) {
    Function *D = *I;
    Module *M = D->getParent();
    M->getFunctionList().remove(D);
  }
}

BasicInliner::BasicInliner(TargetData *TD) {
  Impl = new BasicInlinerImpl(TD);
}

BasicInliner::~BasicInliner() {
  delete Impl;
}

/// addFunction - Add function into the list of functions to process.
/// All functions must be inserted using this interface before invoking
/// inlineFunctions().
void BasicInliner::addFunction(Function *F) {
  Impl->addFunction(F);
}

/// neverInlineFunction - Sometimes a function is never to be inlined because
/// of one or other reason.
void BasicInliner::neverInlineFunction(Function *F) {
  Impl->neverInlineFunction(F);
}

/// inlineFuctions - Walk all call sites in all functions supplied by
/// client. Inline as many call sites as possible. Delete completely
/// inlined functions.
void BasicInliner::inlineFunctions() {
  Impl->inlineFunctions();
}

}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// @brief abstract line editor
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2004-2013 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "LineEditor.h"

#include <readline/readline.h>
#include <readline/history.h>

#include "BasicsC/tri-strings.h"

using namespace std;

// -----------------------------------------------------------------------------
// --SECTION--                                                  class LineEditor
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// --SECTION--                                      constructors and destructors
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief constructs a new editor
////////////////////////////////////////////////////////////////////////////////

LineEditor::LineEditor (std::string const& history)
  : _current(),
    _historyFilename(history),
    _state(STATE_NONE) {
  rl_initialize();

#if RL_READLINE_VERSION >= 0x0400
  rl_catch_signals = 0;
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief destructor
////////////////////////////////////////////////////////////////////////////////

LineEditor::~LineEditor () {
  close();
}

// -----------------------------------------------------------------------------
// --SECTION--                                                    public methods
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief line editor open
////////////////////////////////////////////////////////////////////////////////

bool LineEditor::open (bool autoComplete) {
  if (autoComplete) {

    // issue #289: do not append a space after completion
    rl_completion_append_character = '\0';

    // works only in Readline 4.2+
#if RL_READLINE_VERSION >= 0x0500
    // enable this to turn on the visual bell - evil!
    // rl_variable_bind("prefer-visible-bell", "1");

    // use this for single-line editing as in mongodb shell
    // rl_variable_bind("horizontal-scroll-mode", "1");

    // show matching parentheses
    rl_set_paren_blink_timeout(1 * 1000 * 1000);
    rl_variable_bind("blink-matching-paren", "1");

    // show selection list when completion is ambiguous. not setting this
    // variable will turn the selection list off at least on Ubuntu
    rl_variable_bind("show-all-if-ambiguous", "1");

    // use readline's built-in page-wise completer
    rl_variable_bind("page-completions", "1");
#endif

    rl_bind_key('\t', rl_complete);
  }

  using_history();
  stifle_history(MAX_HISTORY_ENTRIES);

  _state = STATE_OPENED;

  return read_history(historyPath().c_str()) == 0;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief line editor shutdown
////////////////////////////////////////////////////////////////////////////////

bool LineEditor::close () {
  if (_state != STATE_OPENED) {
    // avoid duplicate saving of history
    return true;
  }

  _state = STATE_CLOSED;

  bool res = writeHistory();

#if RL_READLINE_VERSION >= 0x0400
  // reset state of the terminal to what it was before readline()
  rl_cleanup_after_signal();
#endif

  return res;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get the history file path
////////////////////////////////////////////////////////////////////////////////

string LineEditor::historyPath () {
  string path;

  if (getenv("HOME")) {
    path.append(getenv("HOME"));
    path += '/';
  }

  path.append(_historyFilename);

  return path;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief add to history
////////////////////////////////////////////////////////////////////////////////

void LineEditor::addHistory (char const* str) {
  if (*str == '\0') {
    return;
  }

  history_set_pos(history_length-1);

  if (current_history()) {
    do {
      if (strcmp(current_history()->line, str) == 0) {
        remove_history(where_history());
        break;
      }
    }
    while (previous_history());
  }

  add_history(str);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief save history
////////////////////////////////////////////////////////////////////////////////

bool LineEditor::writeHistory () {
  return (write_history(historyPath().c_str()) == 0);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief line editor prompt
////////////////////////////////////////////////////////////////////////////////

char* LineEditor::prompt (char const* prompt) {
  string dotdot;
  char const* p = prompt;
  size_t len1 = strlen(prompt);
  size_t len2 = len1;
  size_t lineno = 0;

  if (len1 < 3) {
    dotdot = "> ";
    len2 = 2;
  }
  else {
    dotdot = string(len1 - 2, '.') + "> ";
  }

  char const* sep = "";

  while (true) {
    char* result = readline(p);

    p = dotdot.c_str();

    if (result == 0) {

      // give up, if the user pressed control-D on the top-most level
      if (_current.empty()) {
        return 0;
      }

      // otherwise clear current content
      _current.clear();
      break;
    }

    _current += sep;
    sep = "\n";
    ++lineno;

    // remove any prompt at the beginning of the line
    char* originalLine = result;
    bool c1 = strncmp(result, prompt, len1) == 0;
    bool c2 = strncmp(result, dotdot.c_str(), len2) == 0;

    while (c1 || c2) {
      if (c1) {
        result += len1;
      }
      else if (c2) {
        result += len2;
      }

      c1 = strncmp(result, prompt, len1) == 0;
      c2 = strncmp(result, dotdot.c_str(), len2) == 0;
    }

    // extend line and check
    _current += result;

    bool ok = isComplete(_current, lineno, strlen(result));

    // cannot use TRI_Free, because it was allocated by the system call readline
    TRI_SystemFree(originalLine);

    // stop if line is complete
    if (ok) {
      break;
    }
  }

  char* line = TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, _current.c_str());
  _current.clear();

  return line;
}

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End:
<commit_msg>testing readline...<commit_after>////////////////////////////////////////////////////////////////////////////////
/// @brief abstract line editor
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2004-2013 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "LineEditor.h"

#include <readline/readline.h>
#include <readline/history.h>

#include "BasicsC/tri-strings.h"

using namespace std;

// -----------------------------------------------------------------------------
// --SECTION--                                                  class LineEditor
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// --SECTION--                                      constructors and destructors
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief constructs a new editor
////////////////////////////////////////////////////////////////////////////////

LineEditor::LineEditor (std::string const& history)
  : _current(),
    _historyFilename(history),
    _state(STATE_NONE) {
  rl_initialize();

#ifndef __APPLE__
  rl_catch_signals = 0;
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief destructor
////////////////////////////////////////////////////////////////////////////////

LineEditor::~LineEditor () {
  close();
}

// -----------------------------------------------------------------------------
// --SECTION--                                                    public methods
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief line editor open
////////////////////////////////////////////////////////////////////////////////

bool LineEditor::open (bool autoComplete) {
  if (autoComplete) {

    // issue #289: do not append a space after completion
    rl_completion_append_character = '\0';

    // works only in Readline 4.2+
#if RL_READLINE_VERSION >= 0x0500
    // enable this to turn on the visual bell - evil!
    // rl_variable_bind("prefer-visible-bell", "1");

    // use this for single-line editing as in mongodb shell
    // rl_variable_bind("horizontal-scroll-mode", "1");

    // show matching parentheses
    rl_set_paren_blink_timeout(1 * 1000 * 1000);
    rl_variable_bind("blink-matching-paren", "1");

    // show selection list when completion is ambiguous. not setting this
    // variable will turn the selection list off at least on Ubuntu
    rl_variable_bind("show-all-if-ambiguous", "1");

    // use readline's built-in page-wise completer
    rl_variable_bind("page-completions", "1");
#endif

    rl_bind_key('\t', rl_complete);
  }

  using_history();
  stifle_history(MAX_HISTORY_ENTRIES);

  _state = STATE_OPENED;

  return read_history(historyPath().c_str()) == 0;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief line editor shutdown
////////////////////////////////////////////////////////////////////////////////

bool LineEditor::close () {
  if (_state != STATE_OPENED) {
    // avoid duplicate saving of history
    return true;
  }

  _state = STATE_CLOSED;

  bool res = writeHistory();

#ifndef __APPLE__
  // reset state of the terminal to what it was before readline()
  rl_cleanup_after_signal();
#endif

  return res;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get the history file path
////////////////////////////////////////////////////////////////////////////////

string LineEditor::historyPath () {
  string path;

  if (getenv("HOME")) {
    path.append(getenv("HOME"));
    path += '/';
  }

  path.append(_historyFilename);

  return path;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief add to history
////////////////////////////////////////////////////////////////////////////////

void LineEditor::addHistory (char const* str) {
  if (*str == '\0') {
    return;
  }

  history_set_pos(history_length-1);

  if (current_history()) {
    do {
      if (strcmp(current_history()->line, str) == 0) {
        remove_history(where_history());
        break;
      }
    }
    while (previous_history());
  }

  add_history(str);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief save history
////////////////////////////////////////////////////////////////////////////////

bool LineEditor::writeHistory () {
  return (write_history(historyPath().c_str()) == 0);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief line editor prompt
////////////////////////////////////////////////////////////////////////////////

char* LineEditor::prompt (char const* prompt) {
  string dotdot;
  char const* p = prompt;
  size_t len1 = strlen(prompt);
  size_t len2 = len1;
  size_t lineno = 0;

  if (len1 < 3) {
    dotdot = "> ";
    len2 = 2;
  }
  else {
    dotdot = string(len1 - 2, '.') + "> ";
  }

  char const* sep = "";

  while (true) {
    char* result = readline(p);

    p = dotdot.c_str();

    if (result == 0) {

      // give up, if the user pressed control-D on the top-most level
      if (_current.empty()) {
        return 0;
      }

      // otherwise clear current content
      _current.clear();
      break;
    }

    _current += sep;
    sep = "\n";
    ++lineno;

    // remove any prompt at the beginning of the line
    char* originalLine = result;
    bool c1 = strncmp(result, prompt, len1) == 0;
    bool c2 = strncmp(result, dotdot.c_str(), len2) == 0;

    while (c1 || c2) {
      if (c1) {
        result += len1;
      }
      else if (c2) {
        result += len2;
      }

      c1 = strncmp(result, prompt, len1) == 0;
      c2 = strncmp(result, dotdot.c_str(), len2) == 0;
    }

    // extend line and check
    _current += result;

    bool ok = isComplete(_current, lineno, strlen(result));

    // cannot use TRI_Free, because it was allocated by the system call readline
    TRI_SystemFree(originalLine);

    // stop if line is complete
    if (ok) {
      break;
    }
  }

  char* line = TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, _current.c_str());
  _current.clear();

  return line;
}

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End:
<|endoftext|>


<commit_before>#include <iostream>
#include <cstdlib>

using namespace std;

template <typename type>
type randUniform() {
    return rand() % ((type)100);
}

template <typename type>
struct LinkedList {
    type value;
    LinkedList* next;
    LinkedList() {
        value = randUniform();
        next = NULL;
    }
};

template <typename type>
LinkedList<type>* createLinkedList(LinkedList<type>* head, int count) {
    LinkedList<type>* head = new LinkedList<type>();
    for (size_t i = 1; i < count; i++) {
        LinkedList<type>* t = new LinkedList<type>();
        x->next = t;
        x = t;
    }
    return head;
}

template <typename type>
void visitLinkedList(LinkedList<type>* x) {
    if (x == NULL) return;
    cout << x->value << ", ";
    visitLinkedList(x->next);
}

template <typename type>
int countNode(LinkedList<type>* x) {
    if (x == NULL) return 0;
    return 1 + countNode(x->next)
}

template <typename type>
void sortLinkedList(LinkedList<type>* x) {
    LinkedList* t = x;
    for (; x != NULL; x = x->next) {
        for (LinkedList* y = x->next; y != NULL; y = y->next) {

        }
    }
}

int main() {
    const in N = 5;
    LinkedList<int>* head = createLinkedList(N);
    visitLinkedList(head);

    return 0;
}<commit_msg>update<commit_after>#include <iostream>
#include <cstdlib>

using namespace std;

template <typename type>
type randUniform() {
    return rand() % ((type)100);
}

template <typename type>
struct LinkedList {
    type value;
    LinkedList* next;
    LinkedList() {
        value = randUniform<type>();
        next = NULL;
    }
};

template <typename type>
LinkedList<type>* createNode() {
    LinkedList<type>* node = new LinkedList<type>();
    return node;
}

template <typename type>
void recursiveCreateLinkedList(LinkedList<type>* x, const int N) {
    static int count = 0;
    if (count == N) return;
    count++;
    x->next = createNode<type>();
    recursiveCreateLinkedList(x->next, N);
}

template <typename type>
LinkedList<type>* createLinkedList(const int count) {
    LinkedList<type>* head = new LinkedList<type>();
    LinkedList<type>* x = head;
    for (size_t i = 1; i < count; i++) {
        LinkedList<type>* t = new LinkedList<type>();
        x->next = t;
        x = t;
    }
    return head;
}

template <typename type>
void visitLinkedList(LinkedList<type>* x) {
    if (x == NULL) return;
    cout << x->value << ", ";
    visitLinkedList(x->next);
}

template <typename type>
int countNode(LinkedList<type>* x) {
    if (x == NULL) return 0;
    return 1 + countNode(x->next);
}

// template <typename type>
// void sortLinkedList(LinkedList<type>* x) {
    // LinkedList* t = x;
    // for (; x != NULL; x = x->next) {
        // for (LinkedList* y = x->next; y != NULL; y = y->next) {

        // }
    // }
// }

int main() {
    const int N = 10;
    // LinkedList<int>* head = createLinkedList<int>(N);
    LinkedList<int>* head = createNode<int>();
    recursiveCreateLinkedList(head, N);
    visitLinkedList(head);

    cout << endl << countNode(head) << endl;

    return 0;
}<|endoftext|>


<commit_before>/***************************************************************************
**
** Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include <QObject>
#include <MDebug>
#include <MApplication>
#include <MPannableViewport>
#include <MApplicationWindow>
#include <MSceneManager>
#include <MApplicationPage>

#include "mpannableviewportscroller.h"
#include "ut_mpannableviewportscroller.h"

void Ut_MPannableViewportScroller::initTestCase()
{
    static int argc = 1;
    static char *app_name[1] = { (char *) "./ut_mpannableviewportscroller" };
    app = new MApplication(argc, app_name);
    app->setLoadMInputContext(false);

    appWindow = new MApplicationWindow();

    page = new MApplicationPage();
    page->setComponentsDisplayMode(MApplicationPage::AllComponents,
                                   MApplicationPageModel::Hide);
    viewport = page->pannableViewport();
    panned = page->centralWidget();
    panned->setMinimumHeight(2000);

    page->appear(appWindow);
    appWindow->show();

    app->processEvents();
    scroller = new MPannableViewportScroller();

    while (qApp->hasPendingEvents()) {
        qApp->processEvents();
    }
}

void Ut_MPannableViewportScroller::cleanupTestCase()
{
    while (qApp->hasPendingEvents()) {
        qApp->processEvents();
    }
    delete scroller;
    delete appWindow;
    delete app;
}

void Ut_MPannableViewportScroller::testQueryScrollingAmount()
{
    // get the size of the visible area
    QRect sceneRect(QRect(QPoint(), page->sceneManager()->visibleSceneSize()));

    // Ask how much the window we need to scroll to get to a position smaller than its range
    QPoint amount = scroller->queryScrollingAmount(viewport, sceneRect, QPoint(0, 1000), QPoint());
    // in this case, the amount should be the position asked
    QCOMPARE(amount, QPoint(0, -1000));

    // Ask how much the window we need to scroll to get to a position bigger than its range
    amount = scroller->queryScrollingAmount(viewport, sceneRect, QPoint(0, 1800), QPoint());
    // in this case, the amount should be the range of the viewport
    QCOMPARE(amount, QPoint(0, -viewport->range().height()));
}

void Ut_MPannableViewportScroller::testApplyScrolling()
{
    scroller->applyScrolling(viewport, QPoint(0, 1000), 0, QEasingCurve());
    QCOMPARE(viewport->position(), QPointF(0, -1000));
}

void Ut_MPannableViewportScroller::testStopScrolling()
{
    scroller->applyScrolling(viewport, QPoint(0, 0), 1000, QEasingCurve());
    QTest::qWait(200);
    scroller->stopScrolling(viewport);

    // interrupting the animation at 200ms when its duration is 1000 ensures
    // that we haven't achieved the final position
    QVERIFY(viewport->position() != QPoint(0,0));
}

void Ut_MPannableViewportScroller::testRestoreScrolling()
{
    // doesn't really do anything
    scroller->restoreScrolling(viewport);
}

QTEST_APPLESS_MAIN(Ut_MPannableViewportScroller)
<commit_msg>Changes: removing delete app from Ut_MPannableViewportScroller RevBy: Jukka Halonen<commit_after>/***************************************************************************
**
** Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include <QObject>
#include <MDebug>
#include <MApplication>
#include <MPannableViewport>
#include <MApplicationWindow>
#include <MSceneManager>
#include <MApplicationPage>

#include "mpannableviewportscroller.h"
#include "ut_mpannableviewportscroller.h"

void Ut_MPannableViewportScroller::initTestCase()
{
    static int argc = 1;
    static char *app_name[1] = { (char *) "./ut_mpannableviewportscroller" };
    app = new MApplication(argc, app_name);
    app->setLoadMInputContext(false);

    appWindow = new MApplicationWindow();

    page = new MApplicationPage();
    page->setComponentsDisplayMode(MApplicationPage::AllComponents,
                                   MApplicationPageModel::Hide);
    viewport = page->pannableViewport();
    panned = page->centralWidget();
    panned->setMinimumHeight(2000);

    page->appear(appWindow);
    appWindow->show();

    app->processEvents();
    scroller = new MPannableViewportScroller();

    while (qApp->hasPendingEvents()) {
        qApp->processEvents();
    }
}

void Ut_MPannableViewportScroller::cleanupTestCase()
{
    while (qApp->hasPendingEvents()) {
        qApp->processEvents();
    }
    delete scroller;
    delete appWindow;
    //Commented to prevent the crash at exit
    //delete app;
}

void Ut_MPannableViewportScroller::testQueryScrollingAmount()
{
    // get the size of the visible area
    QRect sceneRect(QRect(QPoint(), page->sceneManager()->visibleSceneSize()));

    // Ask how much the window we need to scroll to get to a position smaller than its range
    QPoint amount = scroller->queryScrollingAmount(viewport, sceneRect, QPoint(0, 1000), QPoint());
    // in this case, the amount should be the position asked
    QCOMPARE(amount, QPoint(0, -1000));

    // Ask how much the window we need to scroll to get to a position bigger than its range
    amount = scroller->queryScrollingAmount(viewport, sceneRect, QPoint(0, 1800), QPoint());
    // in this case, the amount should be the range of the viewport
    QCOMPARE(amount, QPoint(0, -viewport->range().height()));
}

void Ut_MPannableViewportScroller::testApplyScrolling()
{
    scroller->applyScrolling(viewport, QPoint(0, 1000), 0, QEasingCurve());
    QCOMPARE(viewport->position(), QPointF(0, -1000));
}

void Ut_MPannableViewportScroller::testStopScrolling()
{
    scroller->applyScrolling(viewport, QPoint(0, 0), 1000, QEasingCurve());
    QTest::qWait(200);
    scroller->stopScrolling(viewport);

    // interrupting the animation at 200ms when its duration is 1000 ensures
    // that we haven't achieved the final position
    QVERIFY(viewport->position() != QPoint(0,0));
}

void Ut_MPannableViewportScroller::testRestoreScrolling()
{
    // doesn't really do anything
    scroller->restoreScrolling(viewport);
}

QTEST_APPLESS_MAIN(Ut_MPannableViewportScroller)
<|endoftext|>


<commit_before>#include "braincloud/internal/URLRequestMethod.h"
#include "braincloud/internal/win/XMLHTTPEventSink.h"
#include "braincloud/internal/win/XMLHTTPRequestLoader.h"

#include "braincloud/http_codes.h"

#include <condition_variable>
#include <chrono>
#include <codecvt>
#include <mutex>
#include <thread>
#include <future>
#include <iostream>

static std::string utf16ToUtf8(const std::wstring& utf16)
{
    static std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> stringConverter;
    return stringConverter.to_bytes(utf16);
}

namespace BrainCloud
{
    URLLoader* URLLoader::create()
    {
        return new XMLHTTPRequestLoader();
    }

    long XMLHTTPRequestLoader::_timeoutInterval = 0;

    XMLHTTPRequestLoader::XMLHTTPRequestLoader()
    {
    }

    XMLHTTPRequestLoader::~XMLHTTPRequestLoader()
    {
        close();
    }

    void XMLHTTPRequestLoader::close()
    {
        _requestMutex.lock();
        if (_request)
        {
            _request->abort();
            _request = NULL;
        }
        _requestMutex.unlock();
    }

    /**
    * This static method is the target of the thread creation.
    * Load up the proper reference to the object, and complete loading the page.
    *
    * @param urlLoader - pointer to the object which is loading the web page
    */
    void* XMLHTTPRequestLoader::loadThread(void *urlLoader)
    {
        // This is the starting point of a new thread.
        // A pointer to the object should have been passed as urlLoader.
        // Verify the pointer.
        if (urlLoader != NULL)
        {
            XMLHTTPRequestLoader *pLoader = reinterpret_cast<XMLHTTPRequestLoader *>(urlLoader);
            loadThreadXMLHTTPRequest(pLoader);
        }

        return NULL;
    }

    static void setXMLHTTPRequestHeaders(XMLHTTPRequestLoader* pLoader, CComPtr<IXMLHTTPRequest>& request)
    {
        // Set the headers
        std::vector<URLRequestHeader> h = pLoader->getRequest().getHeaders();
        for (std::string::size_type i = 0; i < h.size(); i++)
        {
            const std::string& headerName = h[i].getName();
            const std::string& headerValue = h[i].getValue();

            request->setRequestHeader(_bstr_t(headerName.c_str()), _bstr_t(headerValue.c_str()));
        }

        // Content Type (special header)
        request->setRequestHeader(_bstr_t("Content-type"), _bstr_t(pLoader->getRequest().getContentType().c_str()));

        // User Agent (special header)
        if (pLoader->getRequest().getUserAgent().length() > 0)
        {
            request->setRequestHeader(_bstr_t("User-Agent"), _bstr_t(pLoader->getRequest().getUserAgent().c_str()));
        }
    }

    void XMLHTTPRequestLoader::loadThreadXMLHTTPRequest(XMLHTTPRequestLoader* pLoader)
    {
        HRESULT hr;
        CComPtr<IXMLHTTPRequest> request;

        CoInitializeEx(NULL, COINIT_MULTITHREADED);

        auto urlRequest = pLoader->getRequest();
        auto url = urlRequest.getUrl();
        const auto& data = urlRequest.getData();
        const auto& method = urlRequest.getMethod();
        bool hasTimeout = _timeoutInterval > 0;

        bool isCompleted = false;
        std::mutex mutex;
        std::condition_variable cv;
        IDispatch *pSink;

        // Create the request
        if (method == URLRequestMethod::GET)
        {
            if (data.length() > 0)
            {
                // Append the request data to the end of the URL.
                url.append("?");
                url.append(data);
            }

            hr = request.CoCreateInstance(CLSID_XMLHTTP60);

            pLoader->_requestMutex.lock();
            pLoader->_request = request;
            pLoader->_requestMutex.unlock();

            hr = request->open(_bstr_t("GET"),
                               _bstr_t(url.c_str()),
                               _variant_t(VARIANT_TRUE),
                               _variant_t(),
                               _variant_t());

            setXMLHTTPRequestHeaders(pLoader, request);

            // Hook up the onreadystatechange event handler
            pSink = new XMLHTTPEventSink(request, [&]
            {
                // onCompleted
                std::unique_lock<std::mutex> lock(mutex);
                isCompleted = true;
                cv.notify_all();
            });
            request->put_onreadystatechange(pSink);

            // Send
            hr = request->send(_variant_t());
            if (hr != S_OK)
            {
                pLoader->_urlResponse.setStatusCode(HTTP_CLIENT_NETWORK_ERROR);
                pLoader->_urlResponse.setReasonPhrase("Failed to send request");
                pLoader->_threadRunning = false;
                pLoader->_requestMutex.lock();
                pLoader->_request = NULL;
                pLoader->_requestMutex.unlock();
                return;
            }
        }
        else if (method == URLRequestMethod::POST)
        {
            hr = request.CoCreateInstance(CLSID_XMLHTTP60);

            pLoader->_requestMutex.lock();
            pLoader->_request = request;
            pLoader->_requestMutex.unlock();

            hr = request->open(_bstr_t("POST"),
                               _bstr_t(url.c_str()),
                               _variant_t(VARIANT_TRUE),
                               _variant_t(),
                               _variant_t());

            setXMLHTTPRequestHeaders(pLoader, request);

            // Set post data
            request->setRequestHeader(_bstr_t("Content-length"), _bstr_t(std::to_string(data.size()).c_str()));

            _variant_t var;
            var.vt = VT_ARRAY | VT_UI1;

            SAFEARRAYBOUND rgsabound[1];
            rgsabound[0].cElements = (ULONG)data.size();
            rgsabound[0].lLbound = 0;

            var.parray = SafeArrayCreate(VT_UI1, 1, rgsabound); // This is cleaned up internally

            void* pArrayData = NULL;
            SafeArrayAccessData(var.parray, &pArrayData);
            memcpy(pArrayData, data.data(), data.size());
            SafeArrayUnaccessData(var.parray);

            // Hook up the onreadystatechange event handler
            pSink = new XMLHTTPEventSink(request, [&]
            {
                // onCompleted
                std::unique_lock<std::mutex> lock(mutex);
                isCompleted = true;
                cv.notify_all();
            });
            request->put_onreadystatechange(pSink);

            // Send
            hr = request->send(var);
            if (hr != S_OK)
            {
                pLoader->_urlResponse.setStatusCode(HTTP_CLIENT_NETWORK_ERROR);
                pLoader->_urlResponse.setReasonPhrase("Failed to send request");
                pLoader->_threadRunning = false;
                pLoader->_requestMutex.lock();
                pLoader->_request = NULL;
                pLoader->_requestMutex.unlock();
                return;
            }
        }
        else
        {
            // Method type not supported.
            pLoader->_urlResponse.setStatusCode(HTTP_CLIENT_NETWORK_ERROR);
            pLoader->_urlResponse.setReasonPhrase("Method type not supported: " + pLoader->getRequest().getMethod());
            pLoader->_threadRunning = false;
            pLoader->_requestMutex.lock();
            pLoader->_request = NULL;
            pLoader->_requestMutex.unlock();
            return;
        }

        // We wait until the async request is done
        auto startTime = std::chrono::steady_clock::now();
        auto endTime = startTime + std::chrono::milliseconds(_timeoutInterval);
        std::unique_lock<std::mutex> lock(mutex);
        cv.wait_until(lock, endTime, [&]()
        {
            return std::chrono::steady_clock::now() >= endTime || isCompleted;
        });

        pSink->Release();

        // Check if we timed out
        if (!isCompleted)
        {
            // Timeout
            printf("#BCC TIMEOUT\n");

            isCompleted = true;
            pLoader->_urlResponse.setStatusCode(HTTP_CLIENT_NETWORK_ERROR);
            pLoader->_threadRunning = false;

            // Abort the request in a thread
            pLoader->_requestMutex.lock();
            auto requestStopThread = std::thread([](CComPtr<IXMLHTTPRequest> request)
            {
				if (request)
				{
                	request->abort();
				}
            }, pLoader->_request);
            requestStopThread.detach();

            pLoader->_request = NULL;
            pLoader->_requestMutex.unlock();
            return;
        }

        // get status
        long status;
        hr = request->get_status(&status);
        if (hr != S_OK)
        {
            pLoader->_urlResponse.setStatusCode(HTTP_CLIENT_NETWORK_ERROR);
            pLoader->_urlResponse.setReasonPhrase("Failed to get HTTP status");
            pLoader->_threadRunning = false;
            pLoader->_requestMutex.lock();
            pLoader->_request = NULL;
            pLoader->_requestMutex.unlock();
            return;
        }
        pLoader->_urlResponse.setStatusCode((unsigned short)status);

        // Get response body
        if (status == 200)
        {
            VARIANT body;
            request->get_responseBody(&body);
            auto pData = (const uint8_t*)body.parray->pvData;
            auto size = (size_t)(body.parray->rgsabound[0].cElements);
            pLoader->_urlResponse.addData(std::string((char*)pData, size));
        }
        else
        {
            BSTR bstrResponse = NULL;
            request->get_responseText(&bstrResponse);
            std::wstring wret = L"";
            if (bstrResponse)
            {
                wret = bstrResponse;
                SysFreeString(bstrResponse);
            }
            pLoader->_urlResponse.setReasonPhrase(utf16ToUtf8(wret));
        }

        // We done
        pLoader->_requestMutex.lock();
        pLoader->_request = NULL;
        pLoader->_requestMutex.unlock();
        pLoader->_threadRunning = false;
    }

    void XMLHTTPRequestLoader::load(URLRequest const & urlRequest)
    {
        // Assume the specified URL in the request is valid.
        setRequest(urlRequest);

        // Start the load thread
        _threadRunning = true;
        auto thread = std::thread(loadThread, this);
        thread.detach();
    }

    bool XMLHTTPRequestLoader::isDone()
    {
        return !_threadRunning;
    }
}
<commit_msg>--amend<commit_after>#include "braincloud/internal/URLRequestMethod.h"
#include "braincloud/internal/win/XMLHTTPEventSink.h"
#include "braincloud/internal/win/XMLHTTPRequestLoader.h"

#include "braincloud/http_codes.h"

#include <condition_variable>
#include <chrono>
#include <codecvt>
#include <mutex>
#include <thread>
#include <future>
#include <iostream>

static std::string utf16ToUtf8(const std::wstring& utf16)
{
    static std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> stringConverter;
    return stringConverter.to_bytes(utf16);
}

namespace BrainCloud
{
    URLLoader* URLLoader::create()
    {
        return new XMLHTTPRequestLoader();
    }

    long XMLHTTPRequestLoader::_timeoutInterval = 0;

    XMLHTTPRequestLoader::XMLHTTPRequestLoader()
    {
    }

    XMLHTTPRequestLoader::~XMLHTTPRequestLoader()
    {
        close();
    }

    void XMLHTTPRequestLoader::close()
    {
        _requestMutex.lock();
        if (_request)
        {
            _request->abort();
            _request = NULL;
        }
        _requestMutex.unlock();
    }

    /**
    * This static method is the target of the thread creation.
    * Load up the proper reference to the object, and complete loading the page.
    *
    * @param urlLoader - pointer to the object which is loading the web page
    */
    void* XMLHTTPRequestLoader::loadThread(void *urlLoader)
    {
        // This is the starting point of a new thread.
        // A pointer to the object should have been passed as urlLoader.
        // Verify the pointer.
        if (urlLoader != NULL)
        {
            XMLHTTPRequestLoader *pLoader = reinterpret_cast<XMLHTTPRequestLoader *>(urlLoader);
            loadThreadXMLHTTPRequest(pLoader);
        }

        return NULL;
    }

    static void setXMLHTTPRequestHeaders(XMLHTTPRequestLoader* pLoader, CComPtr<IXMLHTTPRequest>& request)
    {
        // Set the headers
        std::vector<URLRequestHeader> h = pLoader->getRequest().getHeaders();
        for (std::string::size_type i = 0; i < h.size(); i++)
        {
            const std::string& headerName = h[i].getName();
            const std::string& headerValue = h[i].getValue();

            request->setRequestHeader(_bstr_t(headerName.c_str()), _bstr_t(headerValue.c_str()));
        }

        // Content Type (special header)
        request->setRequestHeader(_bstr_t("Content-type"), _bstr_t(pLoader->getRequest().getContentType().c_str()));

        // User Agent (special header)
        if (pLoader->getRequest().getUserAgent().length() > 0)
        {
            request->setRequestHeader(_bstr_t("User-Agent"), _bstr_t(pLoader->getRequest().getUserAgent().c_str()));
        }
    }

    void XMLHTTPRequestLoader::loadThreadXMLHTTPRequest(XMLHTTPRequestLoader* pLoader)
    {
        HRESULT hr;
        CComPtr<IXMLHTTPRequest> request;

        CoInitializeEx(NULL, COINIT_MULTITHREADED);

        auto urlRequest = pLoader->getRequest();
        auto url = urlRequest.getUrl();
        const auto& data = urlRequest.getData();
        const auto& method = urlRequest.getMethod();
        bool hasTimeout = _timeoutInterval > 0;

        bool isCompleted = false;
        std::mutex mutex;
        std::condition_variable cv;
        IDispatch *pSink;

        // Create the request
        if (method == URLRequestMethod::GET)
        {
            if (data.length() > 0)
            {
                // Append the request data to the end of the URL.
                url.append("?");
                url.append(data);
            }

            hr = request.CoCreateInstance(CLSID_XMLHTTP60);

            pLoader->_requestMutex.lock();
            pLoader->_request = request;
            pLoader->_requestMutex.unlock();

            hr = request->open(_bstr_t("GET"),
                               _bstr_t(url.c_str()),
                               _variant_t(VARIANT_TRUE),
                               _variant_t(),
                               _variant_t());

            setXMLHTTPRequestHeaders(pLoader, request);

            // Hook up the onreadystatechange event handler
            pSink = new XMLHTTPEventSink(request, [&]
            {
                // onCompleted
                std::unique_lock<std::mutex> lock(mutex);
                isCompleted = true;
                cv.notify_all();
            });
            request->put_onreadystatechange(pSink);

            // Send
            hr = request->send(_variant_t());
            if (hr != S_OK)
            {
                pLoader->_urlResponse.setStatusCode(HTTP_CLIENT_NETWORK_ERROR);
                pLoader->_urlResponse.setReasonPhrase("Failed to send request");
                pLoader->_threadRunning = false;
                pLoader->_requestMutex.lock();
                pLoader->_request = NULL;
                pLoader->_requestMutex.unlock();
                return;
            }
        }
        else if (method == URLRequestMethod::POST)
        {
            hr = request.CoCreateInstance(CLSID_XMLHTTP60);

            pLoader->_requestMutex.lock();
            pLoader->_request = request;
            pLoader->_requestMutex.unlock();

            hr = request->open(_bstr_t("POST"),
                               _bstr_t(url.c_str()),
                               _variant_t(VARIANT_TRUE),
                               _variant_t(),
                               _variant_t());

            setXMLHTTPRequestHeaders(pLoader, request);

            // Set post data
            request->setRequestHeader(_bstr_t("Content-length"), _bstr_t(std::to_string(data.size()).c_str()));

            _variant_t var;
            var.vt = VT_ARRAY | VT_UI1;

            SAFEARRAYBOUND rgsabound[1];
            rgsabound[0].cElements = (ULONG)data.size();
            rgsabound[0].lLbound = 0;

            var.parray = SafeArrayCreate(VT_UI1, 1, rgsabound); // This is cleaned up internally

            void* pArrayData = NULL;
            SafeArrayAccessData(var.parray, &pArrayData);
            memcpy(pArrayData, data.data(), data.size());
            SafeArrayUnaccessData(var.parray);

            // Hook up the onreadystatechange event handler
            pSink = new XMLHTTPEventSink(request, [&]
            {
                // onCompleted
                std::unique_lock<std::mutex> lock(mutex);
                isCompleted = true;
                cv.notify_all();
            });
            request->put_onreadystatechange(pSink);

            // Send
            hr = request->send(var);
            if (hr != S_OK)
            {
                pLoader->_urlResponse.setStatusCode(HTTP_CLIENT_NETWORK_ERROR);
                pLoader->_urlResponse.setReasonPhrase("Failed to send request");
                pLoader->_threadRunning = false;
                pLoader->_requestMutex.lock();
                pLoader->_request = NULL;
                pLoader->_requestMutex.unlock();
                return;
            }
        }
        else
        {
            // Method type not supported.
            pLoader->_urlResponse.setStatusCode(HTTP_CLIENT_NETWORK_ERROR);
            pLoader->_urlResponse.setReasonPhrase("Method type not supported: " + pLoader->getRequest().getMethod());
            pLoader->_threadRunning = false;
            pLoader->_requestMutex.lock();
            pLoader->_request = NULL;
            pLoader->_requestMutex.unlock();
            return;
        }

        // We wait until the async request is done
        auto startTime = std::chrono::steady_clock::now();
        auto endTime = startTime + std::chrono::milliseconds(_timeoutInterval);
        std::unique_lock<std::mutex> lock(mutex);
        cv.wait_until(lock, endTime, [&]()
        {
            return std::chrono::steady_clock::now() >= endTime || isCompleted;
        });

        pSink->Release();

        // Check if we timed out
        if (!isCompleted)
        {
            // Timeout
            printf("#BCC TIMEOUT\n");

            isCompleted = true;
            pLoader->_urlResponse.setStatusCode(HTTP_CLIENT_NETWORK_ERROR);
            pLoader->_threadRunning = false;

            // Abort the request in a thread
            pLoader->_requestMutex.lock();
            auto requestStopThread = std::thread([](CComPtr<IXMLHTTPRequest> request)
            {
                if (request)
                {
                    request->abort();
                }
            }, pLoader->_request);
            requestStopThread.detach();

            pLoader->_request = NULL;
            pLoader->_requestMutex.unlock();
            return;
        }

        // get status
        long status;
        hr = request->get_status(&status);
        if (hr != S_OK)
        {
            pLoader->_urlResponse.setStatusCode(HTTP_CLIENT_NETWORK_ERROR);
            pLoader->_urlResponse.setReasonPhrase("Failed to get HTTP status");
            pLoader->_threadRunning = false;
            pLoader->_requestMutex.lock();
            pLoader->_request = NULL;
            pLoader->_requestMutex.unlock();
            return;
        }
        pLoader->_urlResponse.setStatusCode((unsigned short)status);

        // Get response body
        if (status == 200)
        {
            VARIANT body;
            request->get_responseBody(&body);
            auto pData = (const uint8_t*)body.parray->pvData;
            auto size = (size_t)(body.parray->rgsabound[0].cElements);
            pLoader->_urlResponse.addData(std::string((char*)pData, size));
        }
        else
        {
            BSTR bstrResponse = NULL;
            request->get_responseText(&bstrResponse);
            std::wstring wret = L"";
            if (bstrResponse)
            {
                wret = bstrResponse;
                SysFreeString(bstrResponse);
            }
            pLoader->_urlResponse.setReasonPhrase(utf16ToUtf8(wret));
        }

        // We done
        pLoader->_requestMutex.lock();
        pLoader->_request = NULL;
        pLoader->_requestMutex.unlock();
        pLoader->_threadRunning = false;
    }

    void XMLHTTPRequestLoader::load(URLRequest const & urlRequest)
    {
        // Assume the specified URL in the request is valid.
        setRequest(urlRequest);

        // Start the load thread
        _threadRunning = true;
        auto thread = std::thread(loadThread, this);
        thread.detach();
    }

    bool XMLHTTPRequestLoader::isDone()
    {
        return !_threadRunning;
    }
}
<|endoftext|>


<commit_before>/* cclive
 * Copyright (C) 2010-2013  Toni Gundogdu <legatvs@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ccinternal>

#include <iostream>
#include <boost/program_options/variables_map.hpp>

#include <ccoptions>
#include <ccquvi>
#include <cclog>
#include <ccfile>
#include <ccutil>

namespace cc
{

namespace po = boost::program_options;

void get(quvi::media& media, void *curl)
{
  const po::variables_map map = cc::opts.map();

  const int max_retries  = map["max-retries"].as<int>();
  const int retry_wait   = map["retry-wait"].as<int>();

  const bool no_download = opts.flags.no_download;
  const bool exec        = map.count("exec");

  int retry = 0;

  while (retry <= max_retries)
    {
      cc::file file(media);

      if (file.nothing_todo())
        {
          if (exec)
            cc::exec(file);
#define E_NOTHING_TODO "media retrieved completely already"
          throw std::runtime_error(E_NOTHING_TODO);
#undef E_NOTHING_TODO
        }

      // Download media.

      if (retry > 0)
        {
          cc::log
              << "Retrying "
              << retry
              << " of "
              << max_retries
              << " ... "
              << std::flush;

          cc::wait(retry_wait);
        }

      cc::log << file.to_s(media) << std::endl;
      ++retry;

      if (!no_download)
        {
          if (!file.write(media, curl));
          continue; // Retry.

          if (exec)
            cc::exec(file);
        }

      break; // Stop retrying.
    }
}

} // namespace cc

// vim: set ts=2 sw=2 tw=72 expandtab:
<commit_msg>FIX: Regression in retrying introduced by 4b118378<commit_after>/* cclive
 * Copyright (C) 2010-2013  Toni Gundogdu <legatvs@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ccinternal>

#include <iostream>
#include <boost/program_options/variables_map.hpp>

#include <ccoptions>
#include <ccquvi>
#include <cclog>
#include <ccfile>
#include <ccutil>

namespace cc
{

namespace po = boost::program_options;

void get(quvi::media& media, void *curl)
{
  const po::variables_map map = cc::opts.map();

  const int max_retries  = map["max-retries"].as<int>();
  const int retry_wait   = map["retry-wait"].as<int>();

  const bool no_download = opts.flags.no_download;
  const bool exec        = map.count("exec");

  int retry = 0;

  while (retry <= max_retries)
    {
      cc::file file(media);

      if (file.nothing_todo())
        {
          if (exec)
            cc::exec(file);
#define E_NOTHING_TODO "media retrieved completely already"
          throw std::runtime_error(E_NOTHING_TODO);
#undef E_NOTHING_TODO
        }

      // Download media.

      if (retry > 0)
        {
          cc::log
              << "Retrying "
              << retry
              << " of "
              << max_retries
              << " ... "
              << std::flush;

          cc::wait(retry_wait);
        }

      cc::log << file.to_s(media) << std::endl;
      ++retry;

      if (!no_download)
        {
          if (!file.write(media, curl))
            continue; // Retry.

          if (exec)
            cc::exec(file);
        }

      break; // Stop retrying.
    }
}

} // namespace cc

// vim: set ts=2 sw=2 tw=72 expandtab:
<|endoftext|>