<commit_before>// Copyright (c) 2014-2018 The Crown developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "rpc/update.h"

#include "init.h"
#include "clientversion.h"
#include "rpc/server.h"
#include "util.h"

#include <univalue.h>
#include <boost/thread.hpp>

#include <sys/stat.h>
#include <libgen.h>
#include <unistd.h>
#include <sys/types.h>

using namespace std;
using namespace boost::filesystem;

bool RPCUpdate::started = false;
UniValue RPCUpdate::statusObj(UniValue::VOBJ);

std::string RPCUpdate::GetArchivePath() const
{
    std::string url = updater.GetDownloadUrl();
    return (tempDir / path(url).filename()).string();
}

bool RPCUpdate::Download()
{
    statusObj.setObject();
    // Create temporary directory
    tempDir = GetTempPath() / unique_path();
    bool result = TryCreateDirectory(tempDir);
    if (!result) {
        throw runtime_error("Failed to create directory" + tempDir.string());
    }

    // Download archive
    std::string archivePath = GetArchivePath();
    updater.DownloadFile(updater.GetDownloadUrl(), archivePath, &ProgressFunction);
    if (updater.GetStopDownload())
    {
        started = false;
        statusObj.push_back(Pair("Download", "Stopped"));
        remove_all(tempDir);
        return false;
    }

    std::string shacheck = CheckSha(archivePath);
    if (shacheck.compare("OK") == 0)
    {
        statusObj.push_back(Pair("Download", "Done - " + archivePath));
    }
    else
    {
        statusObj.push_back(Pair("Download", shacheck));
        remove_all(tempDir);
        return false;
    }
    return true;
}

#if (defined(__MINGW32__) || defined(__CYGWIN__))
struct heap_delete
{
    typedef LPVOID pointer;
    void operator()(LPVOID p)
    {
        ::HeapFree(::GetProcessHeap(), 0, p);
    }
};
typedef std::unique_ptr<LPVOID, heap_delete> heap_unique_ptr;

struct handle_delete
{
    typedef HANDLE pointer;
    void operator()(HANDLE p)
    {
        ::CloseHandle(p);
    }
};
typedef std::unique_ptr<HANDLE, handle_delete> handle_unique_ptr;

typedef uint32_t uid_t;

BOOL GetUserSID(HANDLE token, PSID* sid)
{
    if (
        token == nullptr || token == INVALID_HANDLE_VALUE
        || sid == nullptr
        )
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    DWORD tokenInformationLength = 0;
    ::GetTokenInformation(
        token, TokenUser, nullptr, 0, &tokenInformationLength);
    if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    {
        return FALSE;
    }
    heap_unique_ptr data(
        ::HeapAlloc(
            ::GetProcessHeap(), HEAP_ZERO_MEMORY,
            tokenInformationLength));
    if (data.get() == nullptr)
    {
        return FALSE;
    }
    BOOL getTokenInfo = ::GetTokenInformation(
        token, TokenUser, data.get(),
        tokenInformationLength, &tokenInformationLength);
    if (! getTokenInfo)
    {
        return FALSE;
    }
    PTOKEN_USER pTokenUser = (PTOKEN_USER)(data.get());
    DWORD sidLength = ::GetLengthSid(pTokenUser->User.Sid);
    heap_unique_ptr sidPtr(
        ::HeapAlloc(
            GetProcessHeap(), HEAP_ZERO_MEMORY, sidLength));
    PSID sidL = (PSID)(sidPtr.get());
    if (sidL == nullptr)
    {
        return FALSE;
    }
    BOOL copySid = ::CopySid(sidLength, sidL, pTokenUser->User.Sid);
    if (! copySid)
    {
        return FALSE;
    }
    if (!IsValidSid(sidL))
    {
        return FALSE;
    }
    *sid = sidL;
    sidPtr.release();
    return TRUE;
}

uid_t GetUID(HANDLE token)
{
    PSID sid = nullptr;
    BOOL getSID = GetUserSID(token, &sid);
    if (! getSID || ! sid)
    {
        return -1;
    }
    heap_unique_ptr sidPtr((LPVOID)(sid));
    LPWSTR stringSid = nullptr;
    BOOL convertSid = ::ConvertSidToStringSidW(
        sid, &stringSid);
    if (! convertSid)
    {
        return -1;
    }
    uid_t ret = -1;
    LPCWSTR p = ::wcsrchr(stringSid, L'-');
    if (p && ::iswdigit(p[1]))
    {
        ++p;
        ret = ::_wtoi(p);
    }
    ::LocalFree(stringSid);
    return ret;
}

uid_t getuid()
{
    HANDLE process = ::GetCurrentProcess();
    handle_unique_ptr processPtr(process);
    HANDLE token = nullptr;
    BOOL openToken = ::OpenProcessToken(
        process, TOKEN_READ|TOKEN_QUERY_SOURCE, &token);
    if (! openToken)
    {
        return -1;
    }
    handle_unique_ptr tokenPtr(token);
    uid_t ret = GetUID(token);
    return ret;
}
#endif

std::string getexe(bool pathonly = false)
{
    boost::filesystem::path result = boost::filesystem::read_symlink( "/proc/self/exe" );
    char *path = new char[MAX_PATH];
    strcpy( path, result.c_str() );

    if (pathonly)
        return std::string( dirname( path ) );
    else
        return std::string( result.c_str() );
}

void RPCUpdate::Install()
{
    statusObj.setObject();
    if (!Download())
    {
        return;
    }

    string installLoc = getexe();
    string install_path = getexe(true);
    struct stat buffer;
    stat(installLoc.c_str(), &buffer);
    int installuid = buffer.st_uid;
    int installgid = buffer.st_gid;
    string usesudo = "";

    if (getuid() != installuid)
        usesudo = strprintf("sudo -n -u %d:%d ", installuid, installgid);

    // Extract archive
    bool result = TryCreateDirectory(tempDir / "archive");
    if (!result) {
        throw runtime_error(strprintf("Failed to create directory %s", (tempDir / "archive").string()));
    }

    std::string strCommand = strprintf("tar xzf %s -C %s --strip-components=1", GetArchivePath(), (tempDir / "archive").string());
    int nErr = ::system(strCommand.c_str());
    if (nErr) {
        LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
        statusObj.push_back(Pair("Extract", "Error. Check debug.log"));
    } else {
        statusObj.push_back(Pair("Extract", "Done"));
    }

    // Copy files to install_path
    if (!nErr) {
        strCommand = strprintf("%scp -rf %s %s", usesudo, (tempDir / "archive/bin/*").string(), install_path);
        nErr = ::system(strCommand.c_str());
        if (nErr) {
            LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
            statusObj.push_back(Pair("Install", "Error. Check debug.log."));
        } else {
            statusObj.push_back(Pair("Install", "Done"));
        }

        // Restart terracoind
        StartRestart();
    }

    boost::filesystem::remove_all(tempDir);
}

void RPCUpdate::ProgressFunction(curl_off_t now, curl_off_t total)
{
    int percent = 0;
    if (total != 0) {
        percent = now * 100 / updater.GetDownloadSize();
    }
    if (statusObj.size() == 0) {
        statusObj.push_back(Pair("Download", "In Progress"));
    }
    if ((now == total) && now != 0) {
        started = false;
        statusObj.push_back(Pair("Download", "Done"));
    } else if (now != total) {
        started = true;
        statusObj.push_back(Pair("Download", strprintf("%0.1f/%0.1fMB, %d%%",
                                        static_cast<double>(now) / 1024 / 1024,
                                        static_cast<double>(updater.GetDownloadSize()) / 1024 / 1024,
                                        percent)));
    }
}

bool RPCUpdate::IsStarted() const
{
    return started;
}

UniValue RPCUpdate::GetStatusObject() const
{
    return statusObj;
}

std::string RPCUpdate::CheckSha(const std::string& fileName) const
{
    std::string result;
    std::string newSha = updater.GetDownloadSha256Sum();
    try
    {
        std::string sha = Sha256Sum(fileName);
        if (!sha.empty()) {
            if (newSha.compare(sha) == 0) {
                result = "OK";
            } else {
                result = "Error. SHA-256 verification did NOT match.";
            }
        } else {
            result = "Error. SHA-256 verification failed to run.";
        }
    } catch(std::string &e) {
        result = "Error. SHA-256 verification failed: " + e;
    }

    return result;
}

UniValue update(const UniValue& params, bool fHelp)
{
    RPCUpdate rpcUpdate;
    string strCommand;
    if (params.size() >= 1)
        strCommand = params[0].get_str();

    if (fHelp  || (strCommand != "check" && strCommand != "download" && strCommand != "status" &&
                   strCommand != "install" && strCommand != "stop"))
        throw runtime_error(
                "update \"command\"... ( \"passphrase\" )\n"
                "Set of commands to check and download application updates\n"
                "\nArguments:\n"
                "1. \"command\"        (string or set of strings, required) The command to execute\n"
                "2. \"passphrase\"     (string, optional) The wallet passphrase\n"
                "\nAvailable commands:\n"
                "  check        - Check for application updates\n"
                "  download     - Download a new version\n"
                "  status       - Check download status\n"
                "  install      - Install update\n"
                "  stop         - Stop update/install\n"
                );

    if (strCommand == "check")
    {
        if (params.size() > 1) {
            throw runtime_error("Too many parameters\n");
        }

        if (updater.GetStatus())
        {
            // There is an update
            UniValue obj(UniValue::VOBJ);
            obj.push_back(Pair("Current Version", FormatVersion(CLIENT_VERSION)));
            obj.push_back(Pair("Update Version", FormatVersion(updater.GetVersion())));
            obj.push_back(Pair("OS", updater.GetOsString()));
            obj.push_back(Pair("Url", updater.GetDownloadUrl()));
            obj.push_back(Pair("Sha256hash", updater.GetDownloadSha256Sum()));
            obj.push_back(Pair("Size", updater.GetDownloadSize()));
            return obj;
        }
        return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
    }

    if (strCommand == "download")
    {
        if (!updater.GetStatus())
        {
            return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
        }
        if (rpcUpdate.IsStarted())
        {
            return "Download is in progress. Run 'terracoin-cli update status' to check the status.";
        }
        boost::thread t(boost::bind(&RPCUpdate::Download, rpcUpdate));
        return "Terracoin Core download started. \nRun 'terracoin-cli update status' to check download status.";
    }

    if (strCommand == "status")
    {
        return rpcUpdate.GetStatusObject();
    }

    if (strCommand == "stop")
    {
        updater.StopDownload();
        return "Terracoin Core download stopped.";
    }

    if (strCommand == "install")
    {
        if (!fServer)
        {
            throw runtime_error("Command is available only in server mode."
                "\nterracoin-qt will automatically check and notify if there is an updates\n");
        }

        if (updater.GetOS() != Updater::LINUX_32 && updater.GetOS() != Updater::LINUX_64)
        {
            throw runtime_error("Command is available only in Linux.");
        }

        string installLoc = getexe();
        string install_path = getexe(true);
        struct stat buffer;
        if (stat(installLoc.c_str(), &buffer) != 0)
        {
            throw runtime_error(strprintf("Terracoin Core not located in %s, can not install.", install_path.c_str()));
        }
        int installeduid = buffer.st_uid;

        if (::system("command -v tar > /dev/null 2>&1"))
        {
            throw runtime_error("The command 'tar' could not be found. Please install it and try again.");
        }

        if (getuid() != installeduid) {
            if (::system("command -v sudo > /dev/null 2>&1"))
            {
                throw runtime_error("The command 'sudo' could not be found. Please install it and try again.");
            }
        }

        if (!updater.GetStatus())
        {
            return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
        }
        if (rpcUpdate.IsStarted())
        {
            return "Download is in progress. Run 'terracoin-cli update status' to check the status.";
        }
        boost::thread t(boost::bind(&RPCUpdate::Install, rpcUpdate));
        return "Terracoin Core install started. \nRun 'terracoin-cli update status' to check download status.";
    }
    return "";
}
<commit_msg>Properly fix total in rpc call<commit_after>// Copyright (c) 2014-2018 The Crown developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "rpc/update.h"

#include "init.h"
#include "clientversion.h"
#include "rpc/server.h"
#include "util.h"

#include <univalue.h>
#include <boost/thread.hpp>

#include <sys/stat.h>
#include <libgen.h>
#include <unistd.h>
#include <sys/types.h>

using namespace std;
using namespace boost::filesystem;

bool RPCUpdate::started = false;
UniValue RPCUpdate::statusObj(UniValue::VOBJ);

std::string RPCUpdate::GetArchivePath() const
{
    std::string url = updater.GetDownloadUrl();
    return (tempDir / path(url).filename()).string();
}

bool RPCUpdate::Download()
{
    statusObj.setObject();
    // Create temporary directory
    tempDir = GetTempPath() / unique_path();
    bool result = TryCreateDirectory(tempDir);
    if (!result) {
        throw runtime_error("Failed to create directory" + tempDir.string());
    }

    // Download archive
    std::string archivePath = GetArchivePath();
    updater.DownloadFile(updater.GetDownloadUrl(), archivePath, &ProgressFunction);
    if (updater.GetStopDownload())
    {
        started = false;
        statusObj.push_back(Pair("Download", "Stopped"));
        remove_all(tempDir);
        return false;
    }

    std::string shacheck = CheckSha(archivePath);
    if (shacheck.compare("OK") == 0)
    {
        statusObj.push_back(Pair("Download", "Done - " + archivePath));
    }
    else
    {
        statusObj.push_back(Pair("Download", shacheck));
        remove_all(tempDir);
        return false;
    }
    return true;
}

#if (defined(__MINGW32__) || defined(__CYGWIN__))
struct heap_delete
{
    typedef LPVOID pointer;
    void operator()(LPVOID p)
    {
        ::HeapFree(::GetProcessHeap(), 0, p);
    }
};
typedef std::unique_ptr<LPVOID, heap_delete> heap_unique_ptr;

struct handle_delete
{
    typedef HANDLE pointer;
    void operator()(HANDLE p)
    {
        ::CloseHandle(p);
    }
};
typedef std::unique_ptr<HANDLE, handle_delete> handle_unique_ptr;

typedef uint32_t uid_t;

BOOL GetUserSID(HANDLE token, PSID* sid)
{
    if (
        token == nullptr || token == INVALID_HANDLE_VALUE
        || sid == nullptr
        )
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    DWORD tokenInformationLength = 0;
    ::GetTokenInformation(
        token, TokenUser, nullptr, 0, &tokenInformationLength);
    if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    {
        return FALSE;
    }
    heap_unique_ptr data(
        ::HeapAlloc(
            ::GetProcessHeap(), HEAP_ZERO_MEMORY,
            tokenInformationLength));
    if (data.get() == nullptr)
    {
        return FALSE;
    }
    BOOL getTokenInfo = ::GetTokenInformation(
        token, TokenUser, data.get(),
        tokenInformationLength, &tokenInformationLength);
    if (! getTokenInfo)
    {
        return FALSE;
    }
    PTOKEN_USER pTokenUser = (PTOKEN_USER)(data.get());
    DWORD sidLength = ::GetLengthSid(pTokenUser->User.Sid);
    heap_unique_ptr sidPtr(
        ::HeapAlloc(
            GetProcessHeap(), HEAP_ZERO_MEMORY, sidLength));
    PSID sidL = (PSID)(sidPtr.get());
    if (sidL == nullptr)
    {
        return FALSE;
    }
    BOOL copySid = ::CopySid(sidLength, sidL, pTokenUser->User.Sid);
    if (! copySid)
    {
        return FALSE;
    }
    if (!IsValidSid(sidL))
    {
        return FALSE;
    }
    *sid = sidL;
    sidPtr.release();
    return TRUE;
}

uid_t GetUID(HANDLE token)
{
    PSID sid = nullptr;
    BOOL getSID = GetUserSID(token, &sid);
    if (! getSID || ! sid)
    {
        return -1;
    }
    heap_unique_ptr sidPtr((LPVOID)(sid));
    LPWSTR stringSid = nullptr;
    BOOL convertSid = ::ConvertSidToStringSidW(
        sid, &stringSid);
    if (! convertSid)
    {
        return -1;
    }
    uid_t ret = -1;
    LPCWSTR p = ::wcsrchr(stringSid, L'-');
    if (p && ::iswdigit(p[1]))
    {
        ++p;
        ret = ::_wtoi(p);
    }
    ::LocalFree(stringSid);
    return ret;
}

uid_t getuid()
{
    HANDLE process = ::GetCurrentProcess();
    handle_unique_ptr processPtr(process);
    HANDLE token = nullptr;
    BOOL openToken = ::OpenProcessToken(
        process, TOKEN_READ|TOKEN_QUERY_SOURCE, &token);
    if (! openToken)
    {
        return -1;
    }
    handle_unique_ptr tokenPtr(token);
    uid_t ret = GetUID(token);
    return ret;
}
#endif

std::string getexe(bool pathonly = false)
{
    boost::filesystem::path result = boost::filesystem::read_symlink( "/proc/self/exe" );
    char *path = new char[MAX_PATH];
    strcpy( path, result.c_str() );

    if (pathonly)
        return std::string( dirname( path ) );
    else
        return std::string( result.c_str() );
}

void RPCUpdate::Install()
{
    statusObj.setObject();
    if (!Download())
    {
        return;
    }

    string installLoc = getexe();
    string install_path = getexe(true);
    struct stat buffer;
    stat(installLoc.c_str(), &buffer);
    int installuid = buffer.st_uid;
    int installgid = buffer.st_gid;
    string usesudo = "";

    if (getuid() != installuid)
        usesudo = strprintf("sudo -n -u %d:%d ", installuid, installgid);

    // Extract archive
    bool result = TryCreateDirectory(tempDir / "archive");
    if (!result) {
        throw runtime_error(strprintf("Failed to create directory %s", (tempDir / "archive").string()));
    }

    std::string strCommand = strprintf("tar xzf %s -C %s --strip-components=1", GetArchivePath(), (tempDir / "archive").string());
    int nErr = ::system(strCommand.c_str());
    if (nErr) {
        LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
        statusObj.push_back(Pair("Extract", "Error. Check debug.log"));
    } else {
        statusObj.push_back(Pair("Extract", "Done"));
    }

    // Copy files to install_path
    if (!nErr) {
        strCommand = strprintf("%scp -rf %s %s", usesudo, (tempDir / "archive/bin/*").string(), install_path);
        nErr = ::system(strCommand.c_str());
        if (nErr) {
            LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
            statusObj.push_back(Pair("Install", "Error. Check debug.log."));
        } else {
            statusObj.push_back(Pair("Install", "Done"));
        }

        // Restart terracoind
        StartRestart();
    }

    boost::filesystem::remove_all(tempDir);
}

void RPCUpdate::ProgressFunction(curl_off_t now, curl_off_t total)
{
    int percent = now * 100 / updater.GetDownloadSize();
    if (statusObj.size() == 0) {
        statusObj.push_back(Pair("Download", "In Progress"));
    }
    if ((now == updater.GetDownloadSize()) && now != 0) {
        started = false;
        statusObj.push_back(Pair("Download", "Done"));
    } else if (now != updater.GetDownloadSize()) {
        started = true;
        statusObj.push_back(Pair("Download", strprintf("%0.1f/%0.1fMB, %d%%",
                                        static_cast<double>(now) / 1024 / 1024,
                                        static_cast<double>(updater.GetDownloadSize()) / 1024 / 1024,
                                        percent)));
    }
}

bool RPCUpdate::IsStarted() const
{
    return started;
}

UniValue RPCUpdate::GetStatusObject() const
{
    return statusObj;
}

std::string RPCUpdate::CheckSha(const std::string& fileName) const
{
    std::string result;
    std::string newSha = updater.GetDownloadSha256Sum();
    try
    {
        std::string sha = Sha256Sum(fileName);
        if (!sha.empty()) {
            if (newSha.compare(sha) == 0) {
                result = "OK";
            } else {
                result = "Error. SHA-256 verification did NOT match.";
            }
        } else {
            result = "Error. SHA-256 verification failed to run.";
        }
    } catch(std::string &e) {
        result = "Error. SHA-256 verification failed: " + e;
    }

    return result;
}

UniValue update(const UniValue& params, bool fHelp)
{
    RPCUpdate rpcUpdate;
    string strCommand;
    if (params.size() >= 1)
        strCommand = params[0].get_str();

    if (fHelp  || (strCommand != "check" && strCommand != "download" && strCommand != "status" &&
                   strCommand != "install" && strCommand != "stop"))
        throw runtime_error(
                "update \"command\"... ( \"passphrase\" )\n"
                "Set of commands to check and download application updates\n"
                "\nArguments:\n"
                "1. \"command\"        (string or set of strings, required) The command to execute\n"
                "2. \"passphrase\"     (string, optional) The wallet passphrase\n"
                "\nAvailable commands:\n"
                "  check        - Check for application updates\n"
                "  download     - Download a new version\n"
                "  status       - Check download status\n"
                "  install      - Install update\n"
                "  stop         - Stop update/install\n"
                );

    if (strCommand == "check")
    {
        if (params.size() > 1) {
            throw runtime_error("Too many parameters\n");
        }

        if (updater.GetStatus())
        {
            // There is an update
            UniValue obj(UniValue::VOBJ);
            obj.push_back(Pair("Current Version", FormatVersion(CLIENT_VERSION)));
            obj.push_back(Pair("Update Version", FormatVersion(updater.GetVersion())));
            obj.push_back(Pair("OS", updater.GetOsString()));
            obj.push_back(Pair("Url", updater.GetDownloadUrl()));
            obj.push_back(Pair("Sha256hash", updater.GetDownloadSha256Sum()));
            obj.push_back(Pair("Size", updater.GetDownloadSize()));
            return obj;
        }
        return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
    }

    if (strCommand == "download")
    {
        if (!updater.GetStatus())
        {
            return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
        }
        if (rpcUpdate.IsStarted())
        {
            return "Download is in progress. Run 'terracoin-cli update status' to check the status.";
        }
        boost::thread t(boost::bind(&RPCUpdate::Download, rpcUpdate));
        return "Terracoin Core download started. \nRun 'terracoin-cli update status' to check download status.";
    }

    if (strCommand == "status")
    {
        return rpcUpdate.GetStatusObject();
    }

    if (strCommand == "stop")
    {
        updater.StopDownload();
        return "Terracoin Core download stopped.";
    }

    if (strCommand == "install")
    {
        if (!fServer)
        {
            throw runtime_error("Command is available only in server mode."
                "\nterracoin-qt will automatically check and notify if there is an updates\n");
        }

        if (updater.GetOS() != Updater::LINUX_32 && updater.GetOS() != Updater::LINUX_64)
        {
            throw runtime_error("Command is available only in Linux.");
        }

        string installLoc = getexe();
        string install_path = getexe(true);
        struct stat buffer;
        if (stat(installLoc.c_str(), &buffer) != 0)
        {
            throw runtime_error(strprintf("Terracoin Core not located in %s, can not install.", install_path.c_str()));
        }
        int installeduid = buffer.st_uid;

        if (::system("command -v tar > /dev/null 2>&1"))
        {
            throw runtime_error("The command 'tar' could not be found. Please install it and try again.");
        }

        if (getuid() != installeduid) {
            if (::system("command -v sudo > /dev/null 2>&1"))
            {
                throw runtime_error("The command 'sudo' could not be found. Please install it and try again.");
            }
        }

        if (!updater.GetStatus())
        {
            return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
        }
        if (rpcUpdate.IsStarted())
        {
            return "Download is in progress. Run 'terracoin-cli update status' to check the status.";
        }
        boost::thread t(boost::bind(&RPCUpdate::Install, rpcUpdate));
        return "Terracoin Core install started. \nRun 'terracoin-cli update status' to check download status.";
    }
    return "";
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2018 The Crown developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "rpc/update.h"

#include "init.h"
#include "clientversion.h"
#include "rpc/server.h"
#include "util.h"

#include <univalue.h>
#include <boost/thread.hpp>

#include <sys/stat.h>
#include <libgen.h>
#include <unistd.h>
#include <sys/types.h>

using namespace std;
using namespace boost::filesystem;

bool RPCUpdate::started = false;
UniValue RPCUpdate::statusObj(UniValue::VOBJ);

std::string RPCUpdate::GetArchivePath() const
{
    std::string url = updater.GetDownloadUrl();
    return (tempDir / path(url).filename()).string();
}

bool RPCUpdate::Download()
{
    statusObj.setObject();
    // Create temporary directory
    tempDir = GetTempPath() / unique_path();
    bool result = TryCreateDirectory(tempDir);
    if (!result) {
        throw runtime_error("Failed to create directory" + tempDir.string());
    }

    // Download archive
    std::string archivePath = GetArchivePath();
    updater.DownloadFile(updater.GetDownloadUrl(), archivePath, &ProgressFunction);
    if (updater.GetStopDownload())
    {
        started = false;
        statusObj.push_back(Pair("Download", "Stopped"));
        remove_all(tempDir);
        return false;
    }

    std::string shacheck = CheckSha(archivePath);
    if (shacheck.compare("OK") == 0)
    {
        statusObj.push_back(Pair("Download", "Done - " + archivePath));
    }
    else
    {
        statusObj.push_back(Pair("Download", shacheck));
        remove_all(tempDir);
        return false;
    }
    return true;
}

#if (defined(__MINGW32__) || defined(__CYGWIN__))
typedef uint32_t uid_t;

uid_t getuid()
{
    return -1;
}
#endif

std::string getexe(bool pathonly = false)
{
    boost::filesystem::path result = boost::filesystem::read_symlink( "/proc/self/exe" );

    if (pathonly)
        return std::string( dirname( (char *)result.c_str() ) );
    else
        return std::string( (char *)result.c_str() );
}

void RPCUpdate::Install()
{
    statusObj.setObject();
    if (!Download())
    {
        return;
    }

    string installLoc = getexe();
    string install_path = getexe(true);
    struct stat buffer;
    stat(installLoc.c_str(), &buffer);
    int installuid = buffer.st_uid;
    int installgid = buffer.st_gid;
    string usesudo = "";

    if (getuid() != installuid)
        usesudo = strprintf("sudo -n -u \#%d -g \#%d ", installuid, installgid);

    // Extract archive
    bool result = TryCreateDirectory(tempDir / "archive");
    if (!result) {
        throw runtime_error(strprintf("Failed to create directory %s", (tempDir / "archive").string()));
    }

    std::string strCommand = strprintf("tar xzf %s -C %s --strip-components=1", GetArchivePath(), (tempDir / "archive").string());
    int nErr = ::system(strCommand.c_str());
    if (nErr) {
        LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
        statusObj.push_back(Pair("Extract", "Error. Check debug.log"));
    } else {
        statusObj.push_back(Pair("Extract", "Done"));
    }

    // Copy files to install_path
    if (!nErr) {
        strCommand = strprintf("%scp -rf %s %s", usesudo, (tempDir / "archive/bin/*").string(), install_path);
        nErr = ::system(strCommand.c_str());
        if (nErr) {
            LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
            statusObj.push_back(Pair("Install", "Error. Check debug.log."));
        } else {
            statusObj.push_back(Pair("Install", "Done"));
        }

        // Restart terracoind
        StartRestart();
    }

    boost::filesystem::remove_all(tempDir);
}

void RPCUpdate::ProgressFunction(curl_off_t now, curl_off_t total)
{
    int percent = now * 100 / updater.GetDownloadSize();
    if (statusObj.size() == 0) {
        statusObj.push_back(Pair("Download", "In Progress"));
    }
    if ((now == updater.GetDownloadSize()) && now != 0) {
        started = false;
        statusObj.push_back(Pair("Download", "Done"));
    } else if (now != updater.GetDownloadSize()) {
        started = true;
        statusObj.push_back(Pair("Download", strprintf("%0.1f/%0.1fMB, %d%%",
                                        static_cast<double>(now) / 1024 / 1024,
                                        static_cast<double>(updater.GetDownloadSize()) / 1024 / 1024,
                                        percent)));
    }
}

bool RPCUpdate::IsStarted() const
{
    return started;
}

UniValue RPCUpdate::GetStatusObject() const
{
    return statusObj;
}

std::string RPCUpdate::CheckSha(const std::string& fileName) const
{
    std::string result;
    std::string newSha = updater.GetDownloadSha256Sum();
    try
    {
        std::string sha = Sha256Sum(fileName);
        if (!sha.empty()) {
            if (newSha.compare(sha) == 0) {
                result = "OK";
            } else {
                result = "Error. SHA-256 verification did NOT match. (" + newSha + " != " + sha + ")";
            }
        } else {
            result = "Error. SHA-256 verification failed to run.";
        }
    } catch(std::string &e) {
        result = "Error. SHA-256 verification failed: " + e;
    }

    return result;
}

UniValue update(const UniValue& params, bool fHelp)
{
    RPCUpdate rpcUpdate;
    string strCommand;
    if (params.size() >= 1)
        strCommand = params[0].get_str();

    if (fHelp  || (strCommand != "check" && strCommand != "download" && strCommand != "status" &&
                   strCommand != "install" && strCommand != "stop"))
        throw runtime_error(
                "update \"command\"... ( \"passphrase\" )\n"
                "Set of commands to check and download application updates\n"
                "\nArguments:\n"
                "1. \"command\"        (string or set of strings, required) The command to execute\n"
                "2. \"passphrase\"     (string, optional) The wallet passphrase\n"
                "\nAvailable commands:\n"
                "  check        - Check for application updates\n"
                "  download     - Download a new version\n"
                "  status       - Check download status\n"
                "  install      - Install update\n"
                "  stop         - Stop update/install\n"
                );

    if (strCommand == "check")
    {
        if (params.size() > 1) {
            throw runtime_error("Too many parameters\n");
        }

        if (updater.GetStatus())
        {
            // There is an update
            UniValue obj(UniValue::VOBJ);
            obj.push_back(Pair("Current Version", FormatVersion(CLIENT_VERSION)));
            obj.push_back(Pair("Update Version", FormatVersion(updater.GetVersion())));
            obj.push_back(Pair("OS", updater.GetOsString()));
            obj.push_back(Pair("Url", updater.GetDownloadUrl()));
            obj.push_back(Pair("Sha256hash", updater.GetDownloadSha256Sum()));
            obj.push_back(Pair("Size", updater.GetDownloadSize()));
            return obj;
        }
        return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
    }

    if (strCommand == "download")
    {
        if (!updater.GetStatus())
        {
            return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
        }
        if (rpcUpdate.IsStarted())
        {
            return "Download is in progress. Run 'terracoin-cli update status' to check the status.";
        }
        boost::thread t(boost::bind(&RPCUpdate::Download, rpcUpdate));
        return "Terracoin Core download started. \nRun 'terracoin-cli update status' to check download status.";
    }

    if (strCommand == "status")
    {
        return rpcUpdate.GetStatusObject();
    }

    if (strCommand == "stop")
    {
        updater.StopDownload();
        return "Terracoin Core download stopped.";
    }

    if (strCommand == "install")
    {
        if (!fServer)
        {
            throw runtime_error("Command is available only in server mode."
                "\nterracoin-qt will automatically check and notify if there is an updates\n");
        }

        if (updater.GetOS() != Updater::LINUX_32 && updater.GetOS() != Updater::LINUX_64)
        {
            throw runtime_error("Command is available only in Linux.");
        }

        string installLoc = getexe();
        string install_path = getexe(true);
        struct stat buffer;
        if (stat(installLoc.c_str(), &buffer) != 0)
        {
            throw runtime_error(strprintf("Terracoin Core not located in %s, can not install.", install_path.c_str()));
        }
        int installeduid = buffer.st_uid;

        if (::system("command -v tar > /dev/null 2>&1"))
        {
            throw runtime_error("The command 'tar' could not be found. Please install it and try again.");
        }

        if (getuid() != installeduid) {
            if (::system("command -v sudo > /dev/null 2>&1"))
            {
                throw runtime_error("The command 'sudo' could not be found. Please install it and try again.");
            }
        }

        if (!updater.GetStatus())
        {
            return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
        }
        if (rpcUpdate.IsStarted())
        {
            return "Download is in progress. Run 'terracoin-cli update status' to check the status.";
        }
        boost::thread t(boost::bind(&RPCUpdate::Install, rpcUpdate));
        return "Terracoin Core install started. \nRun 'terracoin-cli update status' to check download status.";
    }
    return "";
}
<commit_msg>Fix escape sequence<commit_after>// Copyright (c) 2014-2018 The Crown developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "rpc/update.h"

#include "init.h"
#include "clientversion.h"
#include "rpc/server.h"
#include "util.h"

#include <univalue.h>
#include <boost/thread.hpp>

#include <sys/stat.h>
#include <libgen.h>
#include <unistd.h>
#include <sys/types.h>

using namespace std;
using namespace boost::filesystem;

bool RPCUpdate::started = false;
UniValue RPCUpdate::statusObj(UniValue::VOBJ);

std::string RPCUpdate::GetArchivePath() const
{
    std::string url = updater.GetDownloadUrl();
    return (tempDir / path(url).filename()).string();
}

bool RPCUpdate::Download()
{
    statusObj.setObject();
    // Create temporary directory
    tempDir = GetTempPath() / unique_path();
    bool result = TryCreateDirectory(tempDir);
    if (!result) {
        throw runtime_error("Failed to create directory" + tempDir.string());
    }

    // Download archive
    std::string archivePath = GetArchivePath();
    updater.DownloadFile(updater.GetDownloadUrl(), archivePath, &ProgressFunction);
    if (updater.GetStopDownload())
    {
        started = false;
        statusObj.push_back(Pair("Download", "Stopped"));
        remove_all(tempDir);
        return false;
    }

    std::string shacheck = CheckSha(archivePath);
    if (shacheck.compare("OK") == 0)
    {
        statusObj.push_back(Pair("Download", "Done - " + archivePath));
    }
    else
    {
        statusObj.push_back(Pair("Download", shacheck));
        remove_all(tempDir);
        return false;
    }
    return true;
}

#if (defined(__MINGW32__) || defined(__CYGWIN__))
typedef uint32_t uid_t;

uid_t getuid()
{
    return -1;
}
#endif

std::string getexe(bool pathonly = false)
{
    boost::filesystem::path result = boost::filesystem::read_symlink( "/proc/self/exe" );

    if (pathonly)
        return std::string( dirname( (char *)result.c_str() ) );
    else
        return std::string( (char *)result.c_str() );
}

void RPCUpdate::Install()
{
    statusObj.setObject();
    if (!Download())
    {
        return;
    }

    string installLoc = getexe();
    string install_path = getexe(true);
    struct stat buffer;
    stat(installLoc.c_str(), &buffer);
    int installuid = buffer.st_uid;
    int installgid = buffer.st_gid;
    string usesudo = "";

    if (getuid() != installuid)
        usesudo = strprintf("sudo -n -u \\#%d -g \\#%d ", installuid, installgid);

    // Extract archive
    bool result = TryCreateDirectory(tempDir / "archive");
    if (!result) {
        throw runtime_error(strprintf("Failed to create directory %s", (tempDir / "archive").string()));
    }

    std::string strCommand = strprintf("tar xzf %s -C %s --strip-components=1", GetArchivePath(), (tempDir / "archive").string());
    int nErr = ::system(strCommand.c_str());
    if (nErr) {
        LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
        statusObj.push_back(Pair("Extract", "Error. Check debug.log"));
    } else {
        statusObj.push_back(Pair("Extract", "Done"));
    }

    // Copy files to install_path
    if (!nErr) {
        strCommand = strprintf("%scp -rf %s %s", usesudo, (tempDir / "archive/bin/*").string(), install_path);
        nErr = ::system(strCommand.c_str());
        if (nErr) {
            LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
            statusObj.push_back(Pair("Install", "Error. Check debug.log."));
        } else {
            statusObj.push_back(Pair("Install", "Done"));
        }

        // Restart terracoind
        StartRestart();
    }

    boost::filesystem::remove_all(tempDir);
}

void RPCUpdate::ProgressFunction(curl_off_t now, curl_off_t total)
{
    int percent = now * 100 / updater.GetDownloadSize();
    if (statusObj.size() == 0) {
        statusObj.push_back(Pair("Download", "In Progress"));
    }
    if ((now == updater.GetDownloadSize()) && now != 0) {
        started = false;
        statusObj.push_back(Pair("Download", "Done"));
    } else if (now != updater.GetDownloadSize()) {
        started = true;
        statusObj.push_back(Pair("Download", strprintf("%0.1f/%0.1fMB, %d%%",
                                        static_cast<double>(now) / 1024 / 1024,
                                        static_cast<double>(updater.GetDownloadSize()) / 1024 / 1024,
                                        percent)));
    }
}

bool RPCUpdate::IsStarted() const
{
    return started;
}

UniValue RPCUpdate::GetStatusObject() const
{
    return statusObj;
}

std::string RPCUpdate::CheckSha(const std::string& fileName) const
{
    std::string result;
    std::string newSha = updater.GetDownloadSha256Sum();
    try
    {
        std::string sha = Sha256Sum(fileName);
        if (!sha.empty()) {
            if (newSha.compare(sha) == 0) {
                result = "OK";
            } else {
                result = "Error. SHA-256 verification did NOT match. (" + newSha + " != " + sha + ")";
            }
        } else {
            result = "Error. SHA-256 verification failed to run.";
        }
    } catch(std::string &e) {
        result = "Error. SHA-256 verification failed: " + e;
    }

    return result;
}

UniValue update(const UniValue& params, bool fHelp)
{
    RPCUpdate rpcUpdate;
    string strCommand;
    if (params.size() >= 1)
        strCommand = params[0].get_str();

    if (fHelp  || (strCommand != "check" && strCommand != "download" && strCommand != "status" &&
                   strCommand != "install" && strCommand != "stop"))
        throw runtime_error(
                "update \"command\"... ( \"passphrase\" )\n"
                "Set of commands to check and download application updates\n"
                "\nArguments:\n"
                "1. \"command\"        (string or set of strings, required) The command to execute\n"
                "2. \"passphrase\"     (string, optional) The wallet passphrase\n"
                "\nAvailable commands:\n"
                "  check        - Check for application updates\n"
                "  download     - Download a new version\n"
                "  status       - Check download status\n"
                "  install      - Install update\n"
                "  stop         - Stop update/install\n"
                );

    if (strCommand == "check")
    {
        if (params.size() > 1) {
            throw runtime_error("Too many parameters\n");
        }

        if (updater.GetStatus())
        {
            // There is an update
            UniValue obj(UniValue::VOBJ);
            obj.push_back(Pair("Current Version", FormatVersion(CLIENT_VERSION)));
            obj.push_back(Pair("Update Version", FormatVersion(updater.GetVersion())));
            obj.push_back(Pair("OS", updater.GetOsString()));
            obj.push_back(Pair("Url", updater.GetDownloadUrl()));
            obj.push_back(Pair("Sha256hash", updater.GetDownloadSha256Sum()));
            obj.push_back(Pair("Size", updater.GetDownloadSize()));
            return obj;
        }
        return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
    }

    if (strCommand == "download")
    {
        if (!updater.GetStatus())
        {
            return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
        }
        if (rpcUpdate.IsStarted())
        {
            return "Download is in progress. Run 'terracoin-cli update status' to check the status.";
        }
        boost::thread t(boost::bind(&RPCUpdate::Download, rpcUpdate));
        return "Terracoin Core download started. \nRun 'terracoin-cli update status' to check download status.";
    }

    if (strCommand == "status")
    {
        return rpcUpdate.GetStatusObject();
    }

    if (strCommand == "stop")
    {
        updater.StopDownload();
        return "Terracoin Core download stopped.";
    }

    if (strCommand == "install")
    {
        if (!fServer)
        {
            throw runtime_error("Command is available only in server mode."
                "\nterracoin-qt will automatically check and notify if there is an updates\n");
        }

        if (updater.GetOS() != Updater::LINUX_32 && updater.GetOS() != Updater::LINUX_64)
        {
            throw runtime_error("Command is available only in Linux.");
        }

        string installLoc = getexe();
        string install_path = getexe(true);
        struct stat buffer;
        if (stat(installLoc.c_str(), &buffer) != 0)
        {
            throw runtime_error(strprintf("Terracoin Core not located in %s, can not install.", install_path.c_str()));
        }
        int installeduid = buffer.st_uid;

        if (::system("command -v tar > /dev/null 2>&1"))
        {
            throw runtime_error("The command 'tar' could not be found. Please install it and try again.");
        }

        if (getuid() != installeduid) {
            if (::system("command -v sudo > /dev/null 2>&1"))
            {
                throw runtime_error("The command 'sudo' could not be found. Please install it and try again.");
            }
        }

        if (!updater.GetStatus())
        {
            return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
        }
        if (rpcUpdate.IsStarted())
        {
            return "Download is in progress. Run 'terracoin-cli update status' to check the status.";
        }
        boost::thread t(boost::bind(&RPCUpdate::Install, rpcUpdate));
        return "Terracoin Core install started. \nRun 'terracoin-cli update status' to check download status.";
    }
    return "";
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Luke San Antonio
 * All rights reserved.
 */
#include "sdl_helper.h"
#include "common/log.h"
#include "common/debugging.h"
namespace redc
{
  SDL_Init_Lock::SDL_Init_Lock(SDL_Init_Lock&& l) : window(l.window),
                                                    gl_context(l.gl_context)
  {
    l.window = nullptr;
    l.gl_context = nullptr;
  }

  SDL_Init_Lock& SDL_Init_Lock::operator=(SDL_Init_Lock&& l)
  {
    window = l.window;
    gl_context = l.gl_context;

    l.window = nullptr;
    l.gl_context = nullptr;

    return *this;
  }

  SDL_Init_Lock::~SDL_Init_Lock()
  {
    // If we don't have both for any reason, don't bother deallocating either.
    if(window && gl_context)
    {
      // That way, if we accidentally partially move we won't do the aggressive
      // SDL_Quit call.

      SDL_GL_DeleteContext(gl_context);
      SDL_DestroyWindow(window);
      SDL_Quit();
    }
  }

  SDL_Init_Lock init_sdl(std::string title, Vec<int> res, bool fullscreen,
                         bool vsync)
  {
    // Initialize SDL

    if(SDL_Init(SDL_INIT_VIDEO))
    {
      log_e("Failed to init SDL");
      return {};
    }

    SDL_version version;
    SDL_GetVersion(&version);

    log_i("Initialized SDL %.%.%", version.major, version.minor, version.patch);

    // Initialize window

    int flags = SDL_WINDOW_OPENGL;
    if(fullscreen) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;

    SDL_Init_Lock ret;

    ret.window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED,
                                  SDL_WINDOWPOS_UNDEFINED, res.x, res.y,
                                  flags);
    if(!ret.window)
    {
      SDL_Quit();
      log_e("Failed to initialize SDL for resolution %x%", res.x, res.y);
      return ret;
    }

    // Initialize OpenGL context

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);

    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);

    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);

    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

    SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 1);

    ret.gl_context = SDL_GL_CreateContext(ret.window);
    if(ret.gl_context == NULL)
    {
      log_e("Failed to create OpenGL context: %", SDL_GetError());
    }

    SDL_GL_MakeCurrent(ret.window, ret.gl_context);

    // Set vsync setting
    int sync_interval = 0;
    if(vsync) sync_interval = 1;
    if(SDL_GL_SetSwapInterval(sync_interval) == -1)
    {
      // Shit, failed to set that
      log_w("Failed to set swap interval: %", SDL_GetError());
    }

    gladLoadGLLoader((GLADloadproc) SDL_GL_GetProcAddress);

    int opengl_maj, opengl_min;
    glGetIntegerv(GL_MAJOR_VERSION, &opengl_maj);
    glGetIntegerv(GL_MINOR_VERSION, &opengl_min);
    log_i("OpenGL core profile %.%", opengl_maj, opengl_min);

    GLint front_type = 0;
    glGetFramebufferAttachmentParameteriv(
      GL_DRAW_FRAMEBUFFER, GL_BACK_LEFT,
      GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, &front_type
    );
    std::string framebuf_string("Unknown type");
    switch(front_type)
    {
    case GL_LINEAR:
      framebuf_string = "RGB";
      break;
    case GL_SRGB:
      framebuf_string = "sRGB";
      break;
    default:
      REDC_UNREACHABLE_MSG("Invalid framebuffer type");
    }
    log_d("Using % framebuffer", framebuf_string);

    return ret;
  }
}
<commit_msg>Print more OpenGL version information<commit_after>/*
 * Copyright (C) 2015 Luke San Antonio
 * All rights reserved.
 */
#include "sdl_helper.h"
#include "common/log.h"
#include "common/debugging.h"
namespace redc
{
  SDL_Init_Lock::SDL_Init_Lock(SDL_Init_Lock&& l) : window(l.window),
                                                    gl_context(l.gl_context)
  {
    l.window = nullptr;
    l.gl_context = nullptr;
  }

  SDL_Init_Lock& SDL_Init_Lock::operator=(SDL_Init_Lock&& l)
  {
    window = l.window;
    gl_context = l.gl_context;

    l.window = nullptr;
    l.gl_context = nullptr;

    return *this;
  }

  SDL_Init_Lock::~SDL_Init_Lock()
  {
    // If we don't have both for any reason, don't bother deallocating either.
    if(window && gl_context)
    {
      // That way, if we accidentally partially move we won't do the aggressive
      // SDL_Quit call.

      SDL_GL_DeleteContext(gl_context);
      SDL_DestroyWindow(window);
      SDL_Quit();
    }
  }

  SDL_Init_Lock init_sdl(std::string title, Vec<int> res, bool fullscreen,
                         bool vsync)
  {
    // Initialize SDL

    if(SDL_Init(SDL_INIT_VIDEO))
    {
      log_e("Failed to init SDL");
      return {};
    }

    SDL_version version;
    SDL_GetVersion(&version);

    log_i("Initialized SDL %.%.%", version.major, version.minor, version.patch);

    // Initialize window

    int flags = SDL_WINDOW_OPENGL;
    if(fullscreen) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;

    SDL_Init_Lock ret;

    ret.window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED,
                                  SDL_WINDOWPOS_UNDEFINED, res.x, res.y,
                                  flags);
    if(!ret.window)
    {
      SDL_Quit();
      log_e("Failed to initialize SDL for resolution %x%", res.x, res.y);
      return ret;
    }

    // Initialize OpenGL context

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);

    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);

    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);

    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

    SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 1);

    ret.gl_context = SDL_GL_CreateContext(ret.window);
    if(ret.gl_context == NULL)
    {
      log_e("Failed to create OpenGL context: %", SDL_GetError());
    }

    SDL_GL_MakeCurrent(ret.window, ret.gl_context);

    // Set vsync setting
    int sync_interval = 0;
    if(vsync) sync_interval = 1;
    if(SDL_GL_SetSwapInterval(sync_interval) == -1)
    {
      // Shit, failed to set that
      log_w("Failed to set swap interval: %", SDL_GetError());
    }

    gladLoadGLLoader((GLADloadproc) SDL_GL_GetProcAddress);

    log_i("OpenGL: %, GLSL: %, Vendor: %, Renderer: %",
          glGetString(GL_VERSION), glGetString(GL_SHADING_LANGUAGE_VERSION),
          glGetString(GL_VENDOR), glGetString(GL_RENDERER));

    GLint front_type = 0;
    glGetFramebufferAttachmentParameteriv(
      GL_DRAW_FRAMEBUFFER, GL_BACK_LEFT,
      GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, &front_type
    );
    std::string framebuf_string("Unknown type");
    switch(front_type)
    {
    case GL_LINEAR:
      framebuf_string = "RGB";
      break;
    case GL_SRGB:
      framebuf_string = "sRGB";
      break;
    default:
      REDC_UNREACHABLE_MSG("Invalid framebuffer type");
    }
    log_d("Using % framebuffer", framebuf_string);

    return ret;
  }
}
<|endoftext|>


<commit_before>/*
 *  statusspec.cpp
 *  StatusSpec project
 *
 *  Copyright (c) 2014 thesupremecommander
 *  BSD 2-Clause License
 *  http://opensource.org/licenses/BSD-2-Clause
 *
 */

#include "statusspec.h"

AntiFreeze *g_AntiFreeze = nullptr;
CameraTools *g_CameraTools = nullptr;
CustomTextures *g_CustomTextures = nullptr;
FOVOverride *g_FOVOverride = nullptr;
Killstreaks *g_Killstreaks = nullptr;
LoadoutIcons *g_LoadoutIcons = nullptr;
LocalPlayer *g_LocalPlayer = nullptr;
MedigunInfo *g_MedigunInfo = nullptr;
MultiPanel *g_MultiPanel = nullptr;
PlayerAliases *g_PlayerAliases = nullptr;
PlayerModels *g_PlayerModels = nullptr;
PlayerOutlines *g_PlayerOutlines = nullptr;
ProjectileOutlines *g_ProjectileOutlines = nullptr;
SpecGUIOrder *g_SpecGUIOrder = nullptr;
StatusIcons *g_StatusIcons = nullptr;
TeamOverrides *g_TeamOverrides = nullptr;

static int doPostScreenSpaceEffectsHook;

void __fastcall Detour_C_BaseEntity_SetModelIndex(C_BaseEntity *instance, void *, int index) {
	Funcs::CallFunc_C_BaseEntity_SetModelIndex(instance, index);
}

void __fastcall Detour_C_BaseEntity_SetModelPointer(C_BaseEntity *instance, void *, const model_t *pModel) {
	Funcs::CallFunc_C_BaseEntity_SetModelPointer(instance, pModel);
}

void Hook_IBaseClientDLL_FrameStageNotify(ClientFrameStage_t curStage) {
	if (!doPostScreenSpaceEffectsHook && Interfaces::GetClientMode()) {
		doPostScreenSpaceEffectsHook = Funcs::AddHook_IClientMode_DoPostScreenSpaceEffects(Interfaces::GetClientMode(), SH_STATIC(Hook_IClientMode_DoPostScreenSpaceEffects), false);
	}

	if (curStage == FRAME_RENDER_START) {
		int maxEntity = Interfaces::pClientEntityList->GetHighestEntityIndex();

		for (int i = 0; i < maxEntity; i++) {
			IClientEntity *entity = Interfaces::pClientEntityList->GetClientEntity(i);

			if (!entity) {
				continue;
			}

			if (g_PlayerOutlines) {
				g_PlayerOutlines->ProcessEntity(entity);
			}

			if (g_ProjectileOutlines) {
				g_ProjectileOutlines->ProcessEntity(entity);
			}
		}
	}

	RETURN_META(MRES_IGNORED);
}

bool Hook_IClientMode_DoPostScreenSpaceEffects(const CViewSetup *pSetup) {
	if (g_PlayerOutlines) {
		if (g_PlayerOutlines->IsEnabled()) {
			g_PlayerOutlines->PreGlowRender(pSetup);
		}
	}

	if (g_ProjectileOutlines) {
		if (g_ProjectileOutlines->IsEnabled()) {
			g_ProjectileOutlines->PreGlowRender(pSetup);
		}
	}

	g_GlowObjectManager.RenderGlowEffects(pSetup);

	RETURN_META_VALUE(MRES_OVERRIDE, true);
}

void Hook_IPanel_PaintTraverse_Post(vgui::VPANEL vguiPanel, bool forceRepaint, bool allowForce = true) {
	if (g_StatusIcons) {
		if (g_StatusIcons->IsEnabled()) {
			g_StatusIcons->Paint(vguiPanel);
		}
		else {
			g_StatusIcons->NoPaint(vguiPanel);
		}
	}

	RETURN_META(MRES_IGNORED);
}

void Hook_IPanel_SendMessage(vgui::VPANEL vguiPanel, KeyValues *params, vgui::VPANEL ifromPanel) {
	if (g_StatusIcons) {
		if (g_StatusIcons->IsEnabled()) {
			g_StatusIcons->InterceptMessage(vguiPanel, params, ifromPanel);
		}
	}

	RETURN_META(MRES_IGNORED);
}

// The plugin is a static singleton that is exported as an interface
StatusSpecPlugin g_StatusSpecPlugin;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(StatusSpecPlugin, IServerPluginCallbacks, INTERFACEVERSION_ISERVERPLUGINCALLBACKS, g_StatusSpecPlugin);

StatusSpecPlugin::StatusSpecPlugin()
{
}

StatusSpecPlugin::~StatusSpecPlugin()
{
}

bool StatusSpecPlugin::Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory)
{
	if (!Interfaces::Load(interfaceFactory, gameServerFactory)) {
		Warning("[%s] Unable to load required libraries!\n", PLUGIN_DESC);
		return false;
	}

	if (!Entities::PrepareOffsets()) {
		Warning("[%s] Unable to determine proper offsets!\n", PLUGIN_DESC);
		return false;
	}

	if (!Funcs::Load()) {
		Warning("[%s] Unable to initialize hooking!", PLUGIN_DESC);
		return false;
	}

	Funcs::AddDetour_C_BaseEntity_SetModelIndex(Detour_C_BaseEntity_SetModelIndex);
	Funcs::AddDetour_C_BaseEntity_SetModelPointer(Detour_C_BaseEntity_SetModelPointer);

	Funcs::AddHook_IBaseClientDLL_FrameStageNotify(Interfaces::pClientDLL, SH_STATIC(Hook_IBaseClientDLL_FrameStageNotify), false);
	Funcs::AddHook_IPanel_PaintTraverse(g_pVGuiPanel, SH_STATIC(Hook_IPanel_PaintTraverse_Post), true);
	Funcs::AddHook_IPanel_SendMessage(g_pVGuiPanel, SH_STATIC(Hook_IPanel_SendMessage), false);

	ConVar_Register();

	g_AntiFreeze = new AntiFreeze();
	g_CameraTools = new CameraTools();
	g_CustomTextures = new CustomTextures();
	g_FOVOverride = new FOVOverride();
	g_Killstreaks = new Killstreaks();
	g_LoadoutIcons = new LoadoutIcons();
	g_LocalPlayer = new LocalPlayer();
	g_MedigunInfo = new MedigunInfo();
	g_MultiPanel = new MultiPanel();
	g_PlayerAliases = new PlayerAliases();
	g_PlayerModels = new PlayerModels();
	g_PlayerOutlines = new PlayerOutlines();
	g_ProjectileOutlines = new ProjectileOutlines();
	g_SpecGUIOrder = new SpecGUIOrder();
	g_StatusIcons = new StatusIcons();
	g_TeamOverrides = new TeamOverrides();

	Msg("%s loaded!\n", PLUGIN_DESC);
	return true;
}

void StatusSpecPlugin::Unload(void)
{
	delete g_AntiFreeze;
	delete g_CameraTools;
	delete g_CustomTextures;
	delete g_FOVOverride;
	delete g_Killstreaks;
	delete g_LoadoutIcons;
	delete g_LocalPlayer;
	delete g_MedigunInfo;
	delete g_MultiPanel;
	delete g_PlayerAliases;
	delete g_PlayerModels;
	delete g_PlayerOutlines;
	delete g_ProjectileOutlines;
	delete g_SpecGUIOrder;
	delete g_StatusIcons;
	delete g_TeamOverrides;

	Funcs::Unload();

	ConVar_Unregister();
	Interfaces::Unload();
}

void StatusSpecPlugin::Pause(void) {
	Funcs::Pause();
}

void StatusSpecPlugin::UnPause(void) {
	Funcs::Unpause();
}

const char *StatusSpecPlugin::GetPluginDescription(void) {
	return PLUGIN_DESC;
}

void StatusSpecPlugin::LevelInit(char const *pMapName) {}
void StatusSpecPlugin::ServerActivate(edict_t *pEdictList, int edictCount, int clientMax) {}
void StatusSpecPlugin::GameFrame(bool simulating) {}
void StatusSpecPlugin::LevelShutdown(void) {}
void StatusSpecPlugin::ClientActive(edict_t *pEntity) {}
void StatusSpecPlugin::ClientDisconnect(edict_t *pEntity) {}
void StatusSpecPlugin::ClientPutInServer(edict_t *pEntity, char const *playername) {}
void StatusSpecPlugin::SetCommandClient(int index) {}
void StatusSpecPlugin::ClientSettingsChanged(edict_t *pEdict) {}
PLUGIN_RESULT StatusSpecPlugin::ClientConnect(bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen) { return PLUGIN_CONTINUE; }
PLUGIN_RESULT StatusSpecPlugin::ClientCommand(edict_t *pEntity, const CCommand &args) { return PLUGIN_CONTINUE; }
PLUGIN_RESULT StatusSpecPlugin::NetworkIDValidated(const char *pszUserName, const char *pszNetworkID) { return PLUGIN_CONTINUE; }
void StatusSpecPlugin::OnQueryCvarValueFinished(QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue) {}
void StatusSpecPlugin::OnEdictAllocated(edict_t *edict) {}
void StatusSpecPlugin::OnEdictFreed(const edict_t *edict) {}<commit_msg>Remove global model detours.<commit_after>/*
 *  statusspec.cpp
 *  StatusSpec project
 *
 *  Copyright (c) 2014 thesupremecommander
 *  BSD 2-Clause License
 *  http://opensource.org/licenses/BSD-2-Clause
 *
 */

#include "statusspec.h"

AntiFreeze *g_AntiFreeze = nullptr;
CameraTools *g_CameraTools = nullptr;
CustomTextures *g_CustomTextures = nullptr;
FOVOverride *g_FOVOverride = nullptr;
Killstreaks *g_Killstreaks = nullptr;
LoadoutIcons *g_LoadoutIcons = nullptr;
LocalPlayer *g_LocalPlayer = nullptr;
MedigunInfo *g_MedigunInfo = nullptr;
MultiPanel *g_MultiPanel = nullptr;
PlayerAliases *g_PlayerAliases = nullptr;
PlayerModels *g_PlayerModels = nullptr;
PlayerOutlines *g_PlayerOutlines = nullptr;
ProjectileOutlines *g_ProjectileOutlines = nullptr;
SpecGUIOrder *g_SpecGUIOrder = nullptr;
StatusIcons *g_StatusIcons = nullptr;
TeamOverrides *g_TeamOverrides = nullptr;

static int doPostScreenSpaceEffectsHook;

void Hook_IBaseClientDLL_FrameStageNotify(ClientFrameStage_t curStage) {
	if (!doPostScreenSpaceEffectsHook && Interfaces::GetClientMode()) {
		doPostScreenSpaceEffectsHook = Funcs::AddHook_IClientMode_DoPostScreenSpaceEffects(Interfaces::GetClientMode(), SH_STATIC(Hook_IClientMode_DoPostScreenSpaceEffects), false);
	}

	if (curStage == FRAME_RENDER_START) {
		int maxEntity = Interfaces::pClientEntityList->GetHighestEntityIndex();

		for (int i = 0; i < maxEntity; i++) {
			IClientEntity *entity = Interfaces::pClientEntityList->GetClientEntity(i);

			if (!entity) {
				continue;
			}

			if (g_PlayerOutlines) {
				g_PlayerOutlines->ProcessEntity(entity);
			}

			if (g_ProjectileOutlines) {
				g_ProjectileOutlines->ProcessEntity(entity);
			}
		}
	}

	RETURN_META(MRES_IGNORED);
}

bool Hook_IClientMode_DoPostScreenSpaceEffects(const CViewSetup *pSetup) {
	if (g_PlayerOutlines) {
		if (g_PlayerOutlines->IsEnabled()) {
			g_PlayerOutlines->PreGlowRender(pSetup);
		}
	}

	if (g_ProjectileOutlines) {
		if (g_ProjectileOutlines->IsEnabled()) {
			g_ProjectileOutlines->PreGlowRender(pSetup);
		}
	}

	g_GlowObjectManager.RenderGlowEffects(pSetup);

	RETURN_META_VALUE(MRES_OVERRIDE, true);
}

void Hook_IPanel_PaintTraverse_Post(vgui::VPANEL vguiPanel, bool forceRepaint, bool allowForce = true) {
	if (g_StatusIcons) {
		if (g_StatusIcons->IsEnabled()) {
			g_StatusIcons->Paint(vguiPanel);
		}
		else {
			g_StatusIcons->NoPaint(vguiPanel);
		}
	}

	RETURN_META(MRES_IGNORED);
}

void Hook_IPanel_SendMessage(vgui::VPANEL vguiPanel, KeyValues *params, vgui::VPANEL ifromPanel) {
	if (g_StatusIcons) {
		if (g_StatusIcons->IsEnabled()) {
			g_StatusIcons->InterceptMessage(vguiPanel, params, ifromPanel);
		}
	}

	RETURN_META(MRES_IGNORED);
}

// The plugin is a static singleton that is exported as an interface
StatusSpecPlugin g_StatusSpecPlugin;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(StatusSpecPlugin, IServerPluginCallbacks, INTERFACEVERSION_ISERVERPLUGINCALLBACKS, g_StatusSpecPlugin);

StatusSpecPlugin::StatusSpecPlugin()
{
}

StatusSpecPlugin::~StatusSpecPlugin()
{
}

bool StatusSpecPlugin::Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory)
{
	if (!Interfaces::Load(interfaceFactory, gameServerFactory)) {
		Warning("[%s] Unable to load required libraries!\n", PLUGIN_DESC);
		return false;
	}

	if (!Entities::PrepareOffsets()) {
		Warning("[%s] Unable to determine proper offsets!\n", PLUGIN_DESC);
		return false;
	}

	if (!Funcs::Load()) {
		Warning("[%s] Unable to initialize hooking!", PLUGIN_DESC);
		return false;
	}

	Funcs::AddHook_IBaseClientDLL_FrameStageNotify(Interfaces::pClientDLL, SH_STATIC(Hook_IBaseClientDLL_FrameStageNotify), false);
	Funcs::AddHook_IPanel_PaintTraverse(g_pVGuiPanel, SH_STATIC(Hook_IPanel_PaintTraverse_Post), true);
	Funcs::AddHook_IPanel_SendMessage(g_pVGuiPanel, SH_STATIC(Hook_IPanel_SendMessage), false);

	ConVar_Register();

	g_AntiFreeze = new AntiFreeze();
	g_CameraTools = new CameraTools();
	g_CustomTextures = new CustomTextures();
	g_FOVOverride = new FOVOverride();
	g_Killstreaks = new Killstreaks();
	g_LoadoutIcons = new LoadoutIcons();
	g_LocalPlayer = new LocalPlayer();
	g_MedigunInfo = new MedigunInfo();
	g_MultiPanel = new MultiPanel();
	g_PlayerAliases = new PlayerAliases();
	g_PlayerModels = new PlayerModels();
	g_PlayerOutlines = new PlayerOutlines();
	g_ProjectileOutlines = new ProjectileOutlines();
	g_SpecGUIOrder = new SpecGUIOrder();
	g_StatusIcons = new StatusIcons();
	g_TeamOverrides = new TeamOverrides();

	Msg("%s loaded!\n", PLUGIN_DESC);
	return true;
}

void StatusSpecPlugin::Unload(void)
{
	delete g_AntiFreeze;
	delete g_CameraTools;
	delete g_CustomTextures;
	delete g_FOVOverride;
	delete g_Killstreaks;
	delete g_LoadoutIcons;
	delete g_LocalPlayer;
	delete g_MedigunInfo;
	delete g_MultiPanel;
	delete g_PlayerAliases;
	delete g_PlayerModels;
	delete g_PlayerOutlines;
	delete g_ProjectileOutlines;
	delete g_SpecGUIOrder;
	delete g_StatusIcons;
	delete g_TeamOverrides;

	Funcs::Unload();

	ConVar_Unregister();
	Interfaces::Unload();
}

void StatusSpecPlugin::Pause(void) {
	Funcs::Pause();
}

void StatusSpecPlugin::UnPause(void) {
	Funcs::Unpause();
}

const char *StatusSpecPlugin::GetPluginDescription(void) {
	return PLUGIN_DESC;
}

void StatusSpecPlugin::LevelInit(char const *pMapName) {}
void StatusSpecPlugin::ServerActivate(edict_t *pEdictList, int edictCount, int clientMax) {}
void StatusSpecPlugin::GameFrame(bool simulating) {}
void StatusSpecPlugin::LevelShutdown(void) {}
void StatusSpecPlugin::ClientActive(edict_t *pEntity) {}
void StatusSpecPlugin::ClientDisconnect(edict_t *pEntity) {}
void StatusSpecPlugin::ClientPutInServer(edict_t *pEntity, char const *playername) {}
void StatusSpecPlugin::SetCommandClient(int index) {}
void StatusSpecPlugin::ClientSettingsChanged(edict_t *pEdict) {}
PLUGIN_RESULT StatusSpecPlugin::ClientConnect(bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen) { return PLUGIN_CONTINUE; }
PLUGIN_RESULT StatusSpecPlugin::ClientCommand(edict_t *pEntity, const CCommand &args) { return PLUGIN_CONTINUE; }
PLUGIN_RESULT StatusSpecPlugin::NetworkIDValidated(const char *pszUserName, const char *pszNetworkID) { return PLUGIN_CONTINUE; }
void StatusSpecPlugin::OnQueryCvarValueFinished(QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue) {}
void StatusSpecPlugin::OnEdictAllocated(edict_t *edict) {}
void StatusSpecPlugin::OnEdictFreed(const edict_t *edict) {}<|endoftext|>


<commit_before>#include "taskqueuer.h"

WARNINGS_DISABLE
#include <QCoreApplication>
#include <QEventLoop>
#include <QVariant>
WARNINGS_ENABLE

#include "cmdlinetask.h"

/*! Track info about tasks. */
struct TaskMeta
{
    /*! Actual task. */
    CmdlineTask *task;
    /*! Does this task need to be the only one happening? */
    bool isExclusive;
    /*! This is a "create archive" task? */
    bool isBackup;
};

TaskQueuer::TaskQueuer() : _threadPool(QThreadPool::globalInstance())
{
#ifdef QT_TESTLIB_LIB
    _fakeNextTask = false;
#endif
}

TaskQueuer::~TaskQueuer()
{
    // Wait up to 1 second to finish any background tasks
    _threadPool->waitForDone(1000);
    // Wait up to 1 second to delete objects scheduled with ->deleteLater()
    QCoreApplication::processEvents(QEventLoop::AllEvents, 1000);
}

void TaskQueuer::stopTasks(bool interrupt, bool running, bool queued)
{
    // Clear the queue first, to avoid starting a queued task
    // after already clearing the running task(s).
    if(queued)
    {
        while(!_taskQueue.isEmpty())
        {
            TaskMeta *   tm   = _taskQueue.dequeue();
            CmdlineTask *task = tm->task;
            if(task)
            {
                task->emitCanceled();
                task->deleteLater();
            }
        }
        emit message("Cleared queued tasks.");
    }

    // Deal with a running backup.
    if(interrupt)
    {
        // Sending a SIGQUIT will cause the tarsnap binary to
        // create a checkpoint.  Non-tarsnap binaries should be
        // receive a CmdlineTask::stop() instead of a SIGQUIT.
        if(!_runningTasks.isEmpty())
            _runningTasks.first()->task->sigquit();
        emit message("Interrupting current backup.");
    }

    // Stop running tasks.
    if(running)
    {
        for(TaskMeta *tm : _runningTasks)
        {
            CmdlineTask *task = tm->task;
            if(task)
                task->stop();
        }
        emit message("Stopped running tasks.");
    }
}

void TaskQueuer::queueTask(CmdlineTask *task, bool exclusive, bool isBackup)
{
    // Sanity check.
    Q_ASSERT(task != nullptr);

    // Create & initialize the TaskMeta object.
    TaskMeta *tm    = new TaskMeta;
    tm->task        = task;
    tm->isExclusive = exclusive;
    tm->isBackup    = isBackup;

    // Add to the queue and trigger starting a new task.
    _taskQueue.enqueue(tm);
    startTasks();
}

void TaskQueuer::startTasks()
{
    while(!_taskQueue.isEmpty() && !isExclusiveTaskRunning())
    {
        // Bail from loop if the next task requires exclusive running.
        if(!_runningTasks.isEmpty() && _taskQueue.head()->isExclusive)
            break;

        startTask();
    }

    // Send the updated task numbers.
    updateTaskNumbers();
}

void TaskQueuer::startTask()
{
    // Bail if there's nothing to do.
    if(_taskQueue.isEmpty())
        return;

    // Check for exclusive.
    if(isExclusiveTaskRunning())
        return;

    // Get a new task.
    TaskMeta *tm = _taskQueue.dequeue();

    // Set up the task ending.
    CmdlineTask *task = tm->task;
    connect(task, &CmdlineTask::dequeue, this, &TaskQueuer::dequeueTask);
    task->setAutoDelete(false);

    // Record this thread as "running", even though it hasn't actually
    // started yet.  QThreadPool::start() is non-blocking, and in fact
    // explicitly states that a QRunnable can be added to an internal
    // run queue if it's exceeded QThreadPoll::maxThreadCount().
    //
    // However, for the purpose of this TaskQueuer, the task should not
    // be recorded in our _taskQueue (because we've just dequeued()'d it).
    // The "strictly correct" solution would be to add a
    // _waitingForStart queue, and move items out of that queue when the
    // relevant CmdlineTask::started signal was emitted.  At the moment,
    // I don't think that step is necessary, but I might need to revisit
    // that decision later.
    _runningTasks.append(tm);

    // Start the task.
#ifdef QT_TESTLIB_LIB
    if(_fakeNextTask)
        task->fake();
#endif
    _threadPool->start(task);
}

void TaskQueuer::dequeueTask()
{
    // Get the task.
    CmdlineTask *task = qobject_cast<CmdlineTask *>(sender());

    // Sanity check.
    if(task == nullptr)
        return;

    // Clean up task.
    for(TaskMeta *tm : _runningTasks)
    {
        if(tm->task == task)
        {
            _runningTasks.removeOne(tm);
            delete tm;
            break;
        }
    }
    task->deleteLater();

    // Start another task(s) if applicable.
    startTasks();
}

bool TaskQueuer::isExclusiveTaskRunning()
{
    for(TaskMeta *tm : _runningTasks)
    {
        if(tm->isExclusive)
            return true;
    }
    return false;
}

bool TaskQueuer::isBackupTaskRunning()
{
    for(TaskMeta *tm : _runningTasks)
    {
        if(tm->isBackup)
            return true;
    }
    return false;
}

void TaskQueuer::updateTaskNumbers()
{
    bool backupTaskRunning = isBackupTaskRunning();
    emit numTasks(backupTaskRunning, _runningTasks.count(), _taskQueue.count());
}

#ifdef QT_TESTLIB_LIB
void TaskQueuer::fakeNextTask()
{
    _fakeNextTask = true;
}

void TaskQueuer::waitUntilIdle()
{
    while(!(_taskQueue.isEmpty() && _runningTasks.isEmpty()))
        QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
#endif
<commit_msg>TaskQueuer: don't try to run too many tasks at once<commit_after>#include "taskqueuer.h"

WARNINGS_DISABLE
#include <QCoreApplication>
#include <QEventLoop>
#include <QVariant>
WARNINGS_ENABLE

#include "cmdlinetask.h"

/*! Track info about tasks. */
struct TaskMeta
{
    /*! Actual task. */
    CmdlineTask *task;
    /*! Does this task need to be the only one happening? */
    bool isExclusive;
    /*! This is a "create archive" task? */
    bool isBackup;
};

TaskQueuer::TaskQueuer() : _threadPool(QThreadPool::globalInstance())
{
#ifdef QT_TESTLIB_LIB
    _fakeNextTask = false;
#endif
}

TaskQueuer::~TaskQueuer()
{
    // Wait up to 1 second to finish any background tasks
    _threadPool->waitForDone(1000);
    // Wait up to 1 second to delete objects scheduled with ->deleteLater()
    QCoreApplication::processEvents(QEventLoop::AllEvents, 1000);
}

void TaskQueuer::stopTasks(bool interrupt, bool running, bool queued)
{
    // Clear the queue first, to avoid starting a queued task
    // after already clearing the running task(s).
    if(queued)
    {
        while(!_taskQueue.isEmpty())
        {
            TaskMeta *   tm   = _taskQueue.dequeue();
            CmdlineTask *task = tm->task;
            if(task)
            {
                task->emitCanceled();
                task->deleteLater();
            }
        }
        emit message("Cleared queued tasks.");
    }

    // Deal with a running backup.
    if(interrupt)
    {
        // Sending a SIGQUIT will cause the tarsnap binary to
        // create a checkpoint.  Non-tarsnap binaries should be
        // receive a CmdlineTask::stop() instead of a SIGQUIT.
        if(!_runningTasks.isEmpty())
            _runningTasks.first()->task->sigquit();
        emit message("Interrupting current backup.");
    }

    // Stop running tasks.
    if(running)
    {
        for(TaskMeta *tm : _runningTasks)
        {
            CmdlineTask *task = tm->task;
            if(task)
                task->stop();
        }
        emit message("Stopped running tasks.");
    }
}

void TaskQueuer::queueTask(CmdlineTask *task, bool exclusive, bool isBackup)
{
    // Sanity check.
    Q_ASSERT(task != nullptr);

    // Create & initialize the TaskMeta object.
    TaskMeta *tm    = new TaskMeta;
    tm->task        = task;
    tm->isExclusive = exclusive;
    tm->isBackup    = isBackup;

    // Add to the queue and trigger starting a new task.
    _taskQueue.enqueue(tm);
    startTasks();
}

void TaskQueuer::startTasks()
{
    while(!_taskQueue.isEmpty() && !isExclusiveTaskRunning())
    {
        // Bail from loop if the next task requires exclusive running.
        if(!_runningTasks.isEmpty() && _taskQueue.head()->isExclusive)
            break;

        // Bail from loop if we've reached the maximum number of threads.
        if(_runningTasks.count() >= _threadPool->maxThreadCount())
            break;

        startTask();
    }

    // Send the updated task numbers.
    updateTaskNumbers();
}

void TaskQueuer::startTask()
{
    // Bail if there's nothing to do.
    if(_taskQueue.isEmpty())
        return;

    // Check for exclusive.
    if(isExclusiveTaskRunning())
        return;

    // Get a new task.
    TaskMeta *tm = _taskQueue.dequeue();

    // Set up the task ending.
    CmdlineTask *task = tm->task;
    connect(task, &CmdlineTask::dequeue, this, &TaskQueuer::dequeueTask);
    task->setAutoDelete(false);

    // Record this thread as "running", even though it hasn't actually
    // started yet.  QThreadPool::start() is non-blocking, and in fact
    // explicitly states that a QRunnable can be added to an internal
    // run queue if it's exceeded QThreadPoll::maxThreadCount().
    //
    // However, for the purpose of this TaskQueuer, the task should not
    // be recorded in our _taskQueue (because we've just dequeued()'d it).
    // The "strictly correct" solution would be to add a
    // _waitingForStart queue, and move items out of that queue when the
    // relevant CmdlineTask::started signal was emitted.  At the moment,
    // I don't think that step is necessary, but I might need to revisit
    // that decision later.
    _runningTasks.append(tm);

    // Start the task.
#ifdef QT_TESTLIB_LIB
    if(_fakeNextTask)
        task->fake();
#endif
    _threadPool->start(task);
}

void TaskQueuer::dequeueTask()
{
    // Get the task.
    CmdlineTask *task = qobject_cast<CmdlineTask *>(sender());

    // Sanity check.
    if(task == nullptr)
        return;

    // Clean up task.
    for(TaskMeta *tm : _runningTasks)
    {
        if(tm->task == task)
        {
            _runningTasks.removeOne(tm);
            delete tm;
            break;
        }
    }
    task->deleteLater();

    // Start another task(s) if applicable.
    startTasks();
}

bool TaskQueuer::isExclusiveTaskRunning()
{
    for(TaskMeta *tm : _runningTasks)
    {
        if(tm->isExclusive)
            return true;
    }
    return false;
}

bool TaskQueuer::isBackupTaskRunning()
{
    for(TaskMeta *tm : _runningTasks)
    {
        if(tm->isBackup)
            return true;
    }
    return false;
}

void TaskQueuer::updateTaskNumbers()
{
    bool backupTaskRunning = isBackupTaskRunning();
    emit numTasks(backupTaskRunning, _runningTasks.count(), _taskQueue.count());
}

#ifdef QT_TESTLIB_LIB
void TaskQueuer::fakeNextTask()
{
    _fakeNextTask = true;
}

void TaskQueuer::waitUntilIdle()
{
    while(!(_taskQueue.isEmpty() && _runningTasks.isEmpty()))
        QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
#endif
<|endoftext|>


<commit_before>/*
This file is part of the ORK library.
Full copyright and license terms can be found in the LICENSE.txt file.
*/
#include"ork/color.hpp"
#include"ork/glm.hpp"

#include"ork/test/catch_include.hpp"

#include"glm/vec4.hpp"

using namespace ork;

bool within_hex(const float v1, const float v2) {
	return std::abs(v1 - v2) < 0.5 / 255.0;
}
bool within_hex(const color4& v1, const color4& v2) {
	LOOPVIG(v1) {
		if(!within_hex(v1[i], v2[i])) {
			return false;
		}
	}
	return true;
}
bool within_3(const float v1, const float v2) {
	return std::abs(v1 - v2) < 0.001;
}
bool within_3(const color4& v1, const color4& v2) {
	LOOPVIG(v1) {
		if(!within_3(v1[i], v2[i])) {
			return false;
		}
	}
	return true;
}
bool within_5(const float v1, const float v2) {
	return std::abs(v1 - v2) < 0.00001;
}
bool within_5(const color4& v1, const color4& v2) {
	LOOPVIG(v1) {
		if(!within_3(v1[i], v2[i])) {
			return false;
		}
	}
	return true;
}

TEST_CASE("Convention for hue", "[color]") {
	const color4 d1{0.0, 0.0, 0.0, 1.0};
	const color4 r1{0.0, 0.0, 0.0, 1.0};
	REQUIRE(truncate_hue(d1, color_space::rgb) == r1);
}

TEST_CASE("RGB To HSL", "[color]") {
	const color4 red_hsl_e{0.0, 1.0, 0.5, 1.0};
	const color4 green_hsl_e{1.0 / 3.0, 1.0, 0.5, 1.0};
	const color4 blue_hsl_e{2.0 / 3.0, 1.0, 0.5, 1.0};
	const color4 white_hsl_e{0.0, 0.0, 1.0, 1.0};
	const color4 black_hsl_e{0.0, 0.0, 0.0, 1.0};

	const color4 red_hsl_r{convert(red, color_space::rgb, color_space::hsl)};
	const color4 green_hsl_r{convert(green, color_space::rgb, color_space::hsl)};
	const color4 blue_hsl_r{convert(blue, color_space::rgb, color_space::hsl)};
	const color4 white_hsl_r{convert(white, color_space::rgb, color_space::hsl)};
	const color4 black_hsl_r{convert(black, color_space::rgb, color_space::hsl)};

	REQUIRE(within_5(red_hsl_e, red_hsl_r));
	REQUIRE(within_5(green_hsl_e, green_hsl_r));
	REQUIRE(within_5(blue_hsl_e, blue_hsl_r));
	REQUIRE(within_5(white_hsl_e, white_hsl_r));
	REQUIRE(within_5(black_hsl_e, black_hsl_r));
	const color4 c1{convert(rgb, color_space::rgb, color_space::hsl)};
	const color4 c2{convert(rgb, color_space::rgb, color_space::hsv)};
}

TEST_CASE("RGB round trip to HEX", "[color]") {
	const color4 d1{0.1, 0.2, 0.3, 0.4};
	const string r1{ORK("1A334D66")};
	const string to{to_hex(d1, color_space::rgb)};
	const color4 from{from_hex(to, color_space::rgb)};
	REQUIRE(to == r1);
	REQUIRE(within_hex(from, d1));
}
<commit_msg>Added cases for rgb to hsv<commit_after>/*
This file is part of the ORK library.
Full copyright and license terms can be found in the LICENSE.txt file.
*/
#include"ork/color.hpp"
#include"ork/glm.hpp"

#include"ork/test/catch_include.hpp"

#include"glm/vec4.hpp"

using namespace ork;

bool within_hex(const float v1, const float v2) {
	return std::abs(v1 - v2) < 0.5 / 255.0;
}
bool within_hex(const color4& v1, const color4& v2) {
	LOOPVIG(v1) {
		if(!within_hex(v1[i], v2[i])) {
			return false;
		}
	}
	return true;
}
bool within_3(const float v1, const float v2) {
	return std::abs(v1 - v2) < 0.001;
}
bool within_3(const color4& v1, const color4& v2) {
	LOOPVIG(v1) {
		if(!within_3(v1[i], v2[i])) {
			return false;
		}
	}
	return true;
}
bool within_5(const float v1, const float v2) {
	return std::abs(v1 - v2) < 0.00001;
}
bool within_5(const color4& v1, const color4& v2) {
	LOOPVIG(v1) {
		if(!within_3(v1[i], v2[i])) {
			return false;
		}
	}
	return true;
}

TEST_CASE("Convention for hue", "[color]") {
	const color4 d1{0.0, 0.0, 0.0, 1.0};
	const color4 r1{0.0, 0.0, 0.0, 1.0};
	REQUIRE(truncate_hue(d1, color_space::rgb) == r1);
}

TEST_CASE("RGB To HSL", "[color]") {
	const color4 red_hsl_e{0.0, 1.0, 0.5, 1.0};
	const color4 green_hsl_e{1.0 / 3.0, 1.0, 0.5, 1.0};
	const color4 blue_hsl_e{2.0 / 3.0, 1.0, 0.5, 1.0};
	const color4 white_hsl_e{0.0, 0.0, 1.0, 1.0};
	const color4 black_hsl_e{0.0, 0.0, 0.0, 1.0};

	const color4 red_hsl_r{convert(red, color_space::rgb, color_space::hsl)};
	const color4 green_hsl_r{convert(green, color_space::rgb, color_space::hsl)};
	const color4 blue_hsl_r{convert(blue, color_space::rgb, color_space::hsl)};
	const color4 white_hsl_r{convert(white, color_space::rgb, color_space::hsl)};
	const color4 black_hsl_r{convert(black, color_space::rgb, color_space::hsl)};

	REQUIRE(within_5(red_hsl_e, red_hsl_r));
	REQUIRE(within_5(green_hsl_e, green_hsl_r));
	REQUIRE(within_5(blue_hsl_e, blue_hsl_r));
	REQUIRE(within_5(white_hsl_e, white_hsl_r));
	REQUIRE(within_5(black_hsl_e, black_hsl_r));
}

TEST_CASE("RGB To HSV", "[color]") {
	const color4 red_hsv_e{0.0, 1.0, 1.0, 1.0};
	const color4 green_hsv_e{1.0 / 3.0, 1.0, 1.0, 1.0};
	const color4 blue_hsv_e{2.0 / 3.0, 1.0, 1.0, 1.0};
	const color4 white_hsv_e{0.0, 0.0, 1.0, 1.0};
	const color4 black_hsv_e{0.0, 0.0, 0.0, 1.0};

	const color4 red_hsv_r{convert(red, color_space::rgb, color_space::hsv)};
	const color4 green_hsv_r{convert(green, color_space::rgb, color_space::hsv)};
	const color4 blue_hsv_r{convert(blue, color_space::rgb, color_space::hsv)};
	const color4 white_hsv_r{convert(white, color_space::rgb, color_space::hsv)};
	const color4 black_hsv_r{convert(black, color_space::rgb, color_space::hsv)};

	REQUIRE(within_5(red_hsv_e, red_hsv_r));
	REQUIRE(within_5(green_hsv_e, green_hsv_r));
	REQUIRE(within_5(blue_hsv_e, blue_hsv_r));
	REQUIRE(within_5(white_hsv_e, white_hsv_r));
	REQUIRE(within_5(black_hsv_e, black_hsv_r));
}

TEST_CASE("RGB round trip to HEX", "[color]") {
	const color4 d1{0.1, 0.2, 0.3, 0.4};
	const string r1{ORK("1A334D66")};
	const string to{to_hex(d1, color_space::rgb)};
	const color4 from{from_hex(to, color_space::rgb)};
	REQUIRE(to == r1);
	REQUIRE(within_hex(from, d1));
}
<|endoftext|>


<commit_before>#include "titlestate.hpp"
#include <iostream>
#include <functional>

TitleState::TitleState(StateStack& mystack, Context context)
: State(mystack, context)
, mBackgroundSprite()
, mText()
, mShowText(true)
, mTextEffectTime()
, mVerticalMenu()
, mFocusGroup()
, mPlayButton("Play", context.fonts->get(Font::Text), std::bind(&TitleState::play, this))
, mSettingButton("Setting", context.fonts->get(Font::Text), []{})
, mQuitButton("Quit", context.fonts->get(Font::Text), std::bind(&TitleState::quit, this))

{
	TextButtonStyle normal;
	normal.color = sf::Color::White;
	normal.font = &context.fonts->get(Font::Text);
	normal.char_size = 180;

	TextButtonStyle focus;
	focus.color = sf::Color::Red;
	focus.font = &context.fonts->get(Font::Text);
	focus.char_size = 200;

	mPlayButton.defineStyle(TextButtonStyle::Normal, normal);
	mPlayButton.defineStyle(TextButtonStyle::Focus, focus);
	mQuitButton.defineStyle(TextButtonStyle::Normal, normal);
	mQuitButton.defineStyle(TextButtonStyle::Focus, focus);
	mSettingButton.defineStyle(TextButtonStyle::Normal, normal);
	mSettingButton.defineStyle(TextButtonStyle::Focus, focus);

	mVerticalMenu.setHorizontalAlignment(VerticalMenu::CENTER);
	mVerticalMenu.append(mPlayButton);
	mVerticalMenu.append(mSettingButton);
	mVerticalMenu.append(mQuitButton);

	float width = context.window->getSize().x;
	mVerticalMenu.setWidth(width/2);
	sf::Vector2f position = {width/2, context.window->getSize().y / 3.f};
	mVerticalMenu.setPosition(position);

	mPlayButton.setFocusGroup(&mFocusGroup);
	mSettingButton.setFocusGroup(&mFocusGroup);
	mQuitButton.setFocusGroup(&mFocusGroup);
}

bool TitleState::handleEvent(const sf::Event& event)
{

    if (event.type == sf::Event::KeyPressed)
    {
		if (event.key.code == sf::Keyboard::Down) {
			mFocusGroup.next();
		}
		else if (event.key.code == sf::Keyboard::Up) {
			mFocusGroup.previous();
		}

		if (mFocusGroup.current())
			mFocusGroup.current()->event(event);
    }
    else if (
		event.type == sf::Event::MouseButtonPressed
		|| event.type == sf::Event::MouseButtonReleased
		|| event.type == sf::Event::MouseMoved
	){
		auto w = getContext().window;
		return mFocusGroup.mouseEvent(event,
			w->mapPixelToCoords(sf::Mouse::getPosition(*w))
		);
	}

    return true;
}

bool TitleState::update(sf::Time dt)
{
    mTextEffectTime += dt;

	// blinking text effect
    if (mTextEffectTime >= sf::seconds(0.7f))
    {
        mShowText = !mShowText;
        mTextEffectTime = sf::Time::Zero;
    }
    return true;
}

void TitleState::draw()
{
    mContext.window->clear();
    mContext.window->draw(mBackgroundSprite);
    //if (mShowText)
        //mContext.window->draw(mText);
	mContext.window->draw(mPlayButton);
	mContext.window->draw(mSettingButton);
	mContext.window->draw(mQuitButton);
    mContext.window->display();
}

void TitleState::play()
{
	requestStateClear();
	requestStackPush(States::Game);
}

void TitleState::quit()
{
	requestStackPop();
}

<commit_msg>Woops, setting+s.<commit_after>#include "titlestate.hpp"
#include <iostream>
#include <functional>

TitleState::TitleState(StateStack& mystack, Context context)
: State(mystack, context)
, mBackgroundSprite()
, mText()
, mShowText(true)
, mTextEffectTime()
, mVerticalMenu()
, mFocusGroup()
, mPlayButton("Play", context.fonts->get(Font::Text), std::bind(&TitleState::play, this))
, mSettingsButton("Settings", context.fonts->get(Font::Text), []{})
, mQuitButton("Quit", context.fonts->get(Font::Text), std::bind(&TitleState::quit, this))

{
	TextButtonStyle normal;
	normal.color = sf::Color::White;
	normal.font = &context.fonts->get(Font::Text);
	normal.char_size = 180;

	TextButtonStyle focus;
	focus.color = sf::Color::Red;
	focus.font = &context.fonts->get(Font::Text);
	focus.char_size = 200;

	mPlayButton.defineStyle(TextButtonStyle::Normal, normal);
	mPlayButton.defineStyle(TextButtonStyle::Focus, focus);
	mQuitButton.defineStyle(TextButtonStyle::Normal, normal);
	mQuitButton.defineStyle(TextButtonStyle::Focus, focus);
	mSettingsButton.defineStyle(TextButtonStyle::Normal, normal);
	mSettingsButton.defineStyle(TextButtonStyle::Focus, focus);

	mVerticalMenu.setHorizontalAlignment(VerticalMenu::CENTER);
	mVerticalMenu.append(mPlayButton);
	mVerticalMenu.append(mSettingsButton);
	mVerticalMenu.append(mQuitButton);

	float width = context.window->getSize().x;
	mVerticalMenu.setWidth(width/2);
	sf::Vector2f position = {width/2, context.window->getSize().y / 3.f};
	mVerticalMenu.setPosition(position);

	mPlayButton.setFocusGroup(&mFocusGroup);
	mSettingsButton.setFocusGroup(&mFocusGroup);
	mQuitButton.setFocusGroup(&mFocusGroup);
}

bool TitleState::handleEvent(const sf::Event& event)
{

    if (event.type == sf::Event::KeyPressed)
    {
		if (event.key.code == sf::Keyboard::Down) {
			mFocusGroup.next();
		}
		else if (event.key.code == sf::Keyboard::Up) {
			mFocusGroup.previous();
		}

		if (mFocusGroup.current())
			mFocusGroup.current()->event(event);
    }
    else if (
		event.type == sf::Event::MouseButtonPressed
		|| event.type == sf::Event::MouseButtonReleased
		|| event.type == sf::Event::MouseMoved
	){
		auto w = getContext().window;
		return mFocusGroup.mouseEvent(event,
			w->mapPixelToCoords(sf::Mouse::getPosition(*w))
		);
	}

    return true;
}

bool TitleState::update(sf::Time dt)
{
    mTextEffectTime += dt;

	// blinking text effect
    if (mTextEffectTime >= sf::seconds(0.7f))
    {
        mShowText = !mShowText;
        mTextEffectTime = sf::Time::Zero;
    }
    return true;
}

void TitleState::draw()
{
    mContext.window->clear();
    mContext.window->draw(mBackgroundSprite);
    //if (mShowText)
        //mContext.window->draw(mText);
	mContext.window->draw(mPlayButton);
	mContext.window->draw(mSettingsButton);
	mContext.window->draw(mQuitButton);
    mContext.window->display();
}

void TitleState::play()
{
	requestStateClear();
	requestStackPush(States::Game);
}

void TitleState::quit()
{
	requestStackPop();
}

<|endoftext|>