<commit_before>#include "progress-bar.hh"
#include "util.hh"
#include "sync.hh"
#include "store-api.hh"
#include "names.hh"

#include <atomic>
#include <map>
#include <thread>
#include <iostream>

namespace nix {

static std::string getS(const std::vector<Logger::Field> & fields, size_t n)
{
    assert(n < fields.size());
    assert(fields[n].type == Logger::Field::tString);
    return fields[n].s;
}

static uint64_t getI(const std::vector<Logger::Field> & fields, size_t n)
{
    assert(n < fields.size());
    assert(fields[n].type == Logger::Field::tInt);
    return fields[n].i;
}

static std::string_view storePathToName(std::string_view path)
{
    auto base = baseNameOf(path);
    auto i = base.find('-');
    return i == std::string::npos ? base.substr(0, 0) : base.substr(i + 1);
}

class ProgressBar : public Logger
{
private:

    struct ActInfo
    {
        std::string s, lastLine, phase;
        ActivityType type = actUnknown;
        uint64_t done = 0;
        uint64_t expected = 0;
        uint64_t running = 0;
        uint64_t failed = 0;
        std::map<ActivityType, uint64_t> expectedByType;
        bool visible = true;
        ActivityId parent;
        std::optional<std::string> name;
    };

    struct ActivitiesByType
    {
        std::map<ActivityId, std::list<ActInfo>::iterator> its;
        uint64_t done = 0;
        uint64_t expected = 0;
        uint64_t failed = 0;
    };

    struct State
    {
        std::list<ActInfo> activities;
        std::map<ActivityId, std::list<ActInfo>::iterator> its;

        std::map<ActivityType, ActivitiesByType> activitiesByType;

        uint64_t filesLinked = 0, bytesLinked = 0;

        uint64_t corruptedPaths = 0, untrustedPaths = 0;

        bool active = true;
        bool haveUpdate = true;
    };

    Sync<State> state_;

    std::thread updateThread;

    std::condition_variable quitCV, updateCV;

    bool printBuildLogs;
    bool isTTY;

public:

    ProgressBar(bool printBuildLogs, bool isTTY)
        : printBuildLogs(printBuildLogs)
        , isTTY(isTTY)
    {
        state_.lock()->active = isTTY;
        updateThread = std::thread([&]() {
            auto state(state_.lock());
            while (state->active) {
                if (!state->haveUpdate)
                    state.wait(updateCV);
                draw(*state);
                state.wait_for(quitCV, std::chrono::milliseconds(50));
            }
        });
    }

    ~ProgressBar()
    {
        stop();
        updateThread.join();
    }

    void stop() override
    {
        auto state(state_.lock());
        if (!state->active) return;
        state->active = false;
        writeToStderr("\r\e[K");
        updateCV.notify_one();
        quitCV.notify_one();
    }

    bool isVerbose() override {
        return printBuildLogs;
    }

    void log(Verbosity lvl, const FormatOrString & fs) override
    {
        auto state(state_.lock());
        log(*state, lvl, fs.s);
    }

    void logEI(const ErrorInfo &ei) override
    {
        auto state(state_.lock());

        std::stringstream oss;
        showErrorInfo(oss, ei, loggerSettings.showTrace.get());

        log(*state, ei.level, oss.str());
    }

    void log(State & state, Verbosity lvl, const std::string & s)
    {
        if (state.active) {
            writeToStderr("\r\e[K" + filterANSIEscapes(s, !isTTY) + ANSI_NORMAL "\n");
            draw(state);
        } else {
            auto s2 = s + ANSI_NORMAL "\n";
            if (!isTTY) s2 = filterANSIEscapes(s2, true);
            writeToStderr(s2);
        }
    }

    void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
        const std::string & s, const Fields & fields, ActivityId parent) override
    {
        auto state(state_.lock());

        if (lvl <= verbosity && !s.empty() && type != actBuildWaiting)
            log(*state, lvl, s + "...");

        state->activities.emplace_back(ActInfo());
        auto i = std::prev(state->activities.end());
        i->s = s;
        i->type = type;
        i->parent = parent;
        state->its.emplace(act, i);
        state->activitiesByType[type].its.emplace(act, i);

        if (type == actBuild) {
            std::string name(storePathToName(getS(fields, 0)));
            if (hasSuffix(name, ".drv"))
                name = name.substr(0, name.size() - 4);
            i->s = fmt("building " ANSI_BOLD "%s" ANSI_NORMAL, name);
            auto machineName = getS(fields, 1);
            if (machineName != "")
                i->s += fmt(" on " ANSI_BOLD "%s" ANSI_NORMAL, machineName);
            auto curRound = getI(fields, 2);
            auto nrRounds = getI(fields, 3);
            if (nrRounds != 1)
                i->s += fmt(" (round %d/%d)", curRound, nrRounds);
            i->name = DrvName(name).name;
        }

        if (type == actSubstitute) {
            auto name = storePathToName(getS(fields, 0));
            auto sub = getS(fields, 1);
            i->s = fmt(
                hasPrefix(sub, "local")
                ? "copying " ANSI_BOLD "%s" ANSI_NORMAL " from %s"
                : "fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s",
                name, sub);
        }

        if (type == actPostBuildHook) {
            auto name = storePathToName(getS(fields, 0));
            if (hasSuffix(name, ".drv"))
                name = name.substr(0, name.size() - 4);
            i->s = fmt("post-build " ANSI_BOLD "%s" ANSI_NORMAL, name);
            i->name = DrvName(name).name;
        }

        if (type == actQueryPathInfo) {
            auto name = storePathToName(getS(fields, 0));
            i->s = fmt("querying " ANSI_BOLD "%s" ANSI_NORMAL " on %s", name, getS(fields, 1));
        }

        if ((type == actFileTransfer && hasAncestor(*state, actCopyPath, parent))
            || (type == actFileTransfer && hasAncestor(*state, actQueryPathInfo, parent))
            || (type == actCopyPath && hasAncestor(*state, actSubstitute, parent)))
            i->visible = false;

        update(*state);
    }

    /* Check whether an activity has an ancestore with the specified
       type. */
    bool hasAncestor(State & state, ActivityType type, ActivityId act)
    {
        while (act != 0) {
            auto i = state.its.find(act);
            if (i == state.its.end()) break;
            if (i->second->type == type) return true;
            act = i->second->parent;
        }
        return false;
    }

    void stopActivity(ActivityId act) override
    {
        auto state(state_.lock());

        auto i = state->its.find(act);
        if (i != state->its.end()) {

            auto & actByType = state->activitiesByType[i->second->type];
            actByType.done += i->second->done;
            actByType.failed += i->second->failed;

            for (auto & j : i->second->expectedByType)
                state->activitiesByType[j.first].expected -= j.second;

            actByType.its.erase(act);
            state->activities.erase(i->second);
            state->its.erase(i);
        }

        update(*state);
    }

    void result(ActivityId act, ResultType type, const std::vector<Field> & fields) override
    {
        auto state(state_.lock());

        if (type == resFileLinked) {
            state->filesLinked++;
            state->bytesLinked += getI(fields, 0);
            update(*state);
        }

        else if (type == resBuildLogLine || type == resPostBuildLogLine) {
            auto lastLine = chomp(getS(fields, 0));
            if (!lastLine.empty()) {
                auto i = state->its.find(act);
                assert(i != state->its.end());
                ActInfo info = *i->second;
                if (printBuildLogs) {
                    auto suffix = "> ";
                    if (type == resPostBuildLogLine) {
                        suffix = " (post)> ";
                    }
                    log(*state, lvlInfo, ANSI_FAINT + info.name.value_or("unnamed") + suffix + ANSI_NORMAL + lastLine);
                } else {
                    state->activities.erase(i->second);
                    info.lastLine = lastLine;
                    state->activities.emplace_back(info);
                    i->second = std::prev(state->activities.end());
                    update(*state);
                }
            }
        }

        else if (type == resUntrustedPath) {
            state->untrustedPaths++;
            update(*state);
        }

        else if (type == resCorruptedPath) {
            state->corruptedPaths++;
            update(*state);
        }

        else if (type == resSetPhase) {
            auto i = state->its.find(act);
            assert(i != state->its.end());
            i->second->phase = getS(fields, 0);
            update(*state);
        }

        else if (type == resProgress) {
            auto i = state->its.find(act);
            assert(i != state->its.end());
            ActInfo & actInfo = *i->second;
            actInfo.done = getI(fields, 0);
            actInfo.expected = getI(fields, 1);
            actInfo.running = getI(fields, 2);
            actInfo.failed = getI(fields, 3);
            update(*state);
        }

        else if (type == resSetExpected) {
            auto i = state->its.find(act);
            assert(i != state->its.end());
            ActInfo & actInfo = *i->second;
            auto type = (ActivityType) getI(fields, 0);
            auto & j = actInfo.expectedByType[type];
            state->activitiesByType[type].expected -= j;
            j = getI(fields, 1);
            state->activitiesByType[type].expected += j;
            update(*state);
        }
    }

    void update(State & state)
    {
        state.haveUpdate = true;
        updateCV.notify_one();
    }

    void draw(State & state)
    {
        state.haveUpdate = false;
        if (!state.active) return;

        std::string line;

        std::string status = getStatus(state);
        if (!status.empty()) {
            line += '[';
            line += status;
            line += "]";
        }

        if (!state.activities.empty()) {
            if (!status.empty()) line += " ";
            auto i = state.activities.rbegin();

            while (i != state.activities.rend() && (!i->visible || (i->s.empty() && i->lastLine.empty())))
                ++i;

            if (i != state.activities.rend()) {
                line += i->s;
                if (!i->phase.empty()) {
                    line += " (";
                    line += i->phase;
                    line += ")";
                }
                if (!i->lastLine.empty()) {
                    if (!i->s.empty()) line += ": ";
                    line += i->lastLine;
                }
            }
        }

        auto width = getWindowSize().second;
        if (width <= 0) width = std::numeric_limits<decltype(width)>::max();

        writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K");
    }

    std::string getStatus(State & state)
    {
        auto MiB = 1024.0 * 1024.0;

        std::string res;

        auto renderActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) {
            auto & act = state.activitiesByType[type];
            uint64_t done = act.done, expected = act.done, running = 0, failed = act.failed;
            for (auto & j : act.its) {
                done += j.second->done;
                expected += j.second->expected;
                running += j.second->running;
                failed += j.second->failed;
            }

            expected = std::max(expected, act.expected);

            std::string s;

            if (running || done || expected || failed) {
                if (running)
                    if (expected != 0)
                        s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt,
                            running / unit, done / unit, expected / unit);
                    else
                        s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL,
                            running / unit, done / unit);
                else if (expected != done)
                    if (expected != 0)
                        s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt,
                            done / unit, expected / unit);
                    else
                        s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL, done / unit);
                else
                    s = fmt(done ? ANSI_GREEN + numberFmt + ANSI_NORMAL : numberFmt, done / unit);
                s = fmt(itemFmt, s);

                if (failed)
                    s += fmt(" (" ANSI_RED "%d failed" ANSI_NORMAL ")", failed / unit);
            }

            return s;
        };

        auto showActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) {
            auto s = renderActivity(type, itemFmt, numberFmt, unit);
            if (s.empty()) return;
            if (!res.empty()) res += ", ";
            res += s;
        };

        showActivity(actBuilds, "%s built");

        auto s1 = renderActivity(actCopyPaths, "%s copied");
        auto s2 = renderActivity(actCopyPath, "%s MiB", "%.1f", MiB);

        if (!s1.empty() || !s2.empty()) {
            if (!res.empty()) res += ", ";
            if (s1.empty()) res += "0 copied"; else res += s1;
            if (!s2.empty()) { res += " ("; res += s2; res += ')'; }
        }

        showActivity(actFileTransfer, "%s MiB DL", "%.1f", MiB);

        {
            auto s = renderActivity(actOptimiseStore, "%s paths optimised");
            if (s != "") {
                s += fmt(", %.1f MiB / %d inodes freed", state.bytesLinked / MiB, state.filesLinked);
                if (!res.empty()) res += ", ";
                res += s;
            }
        }

        // FIXME: don't show "done" paths in green.
        showActivity(actVerifyPaths, "%s paths verified");

        if (state.corruptedPaths) {
            if (!res.empty()) res += ", ";
            res += fmt(ANSI_RED "%d corrupted" ANSI_NORMAL, state.corruptedPaths);
        }

        if (state.untrustedPaths) {
            if (!res.empty()) res += ", ";
            res += fmt(ANSI_RED "%d untrusted" ANSI_NORMAL, state.untrustedPaths);
        }

        return res;
    }

    void writeToStdout(std::string_view s) override
    {
        auto state(state_.lock());
        if (state->active) {
            std::cerr << "\r\e[K";
            Logger::writeToStdout(s);
            draw(*state);
        } else {
            Logger::writeToStdout(s);
        }
    }

    std::optional<char> ask(std::string_view msg) override
    {
        auto state(state_.lock());
        if (!state->active || !isatty(STDIN_FILENO)) return {};
        std::cerr << fmt("\r\e[K%s ", msg);
        auto s = trim(readLine(STDIN_FILENO));
        if (s.size() != 1) return {};
        draw(*state);
        return s[0];
    }
};

Logger * makeProgressBar(bool printBuildLogs)
{
    return new ProgressBar(
        printBuildLogs,
        isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb"
    );
}

void startProgressBar(bool printBuildLogs)
{
    logger = makeProgressBar(printBuildLogs);
}

void stopProgressBar()
{
    auto progressBar = dynamic_cast<ProgressBar *>(logger);
    if (progressBar) progressBar->stop();

}

}
<commit_msg>ProgressBar: Respect verbosity level<commit_after>#include "progress-bar.hh"
#include "util.hh"
#include "sync.hh"
#include "store-api.hh"
#include "names.hh"

#include <atomic>
#include <map>
#include <thread>
#include <iostream>

namespace nix {

static std::string getS(const std::vector<Logger::Field> & fields, size_t n)
{
    assert(n < fields.size());
    assert(fields[n].type == Logger::Field::tString);
    return fields[n].s;
}

static uint64_t getI(const std::vector<Logger::Field> & fields, size_t n)
{
    assert(n < fields.size());
    assert(fields[n].type == Logger::Field::tInt);
    return fields[n].i;
}

static std::string_view storePathToName(std::string_view path)
{
    auto base = baseNameOf(path);
    auto i = base.find('-');
    return i == std::string::npos ? base.substr(0, 0) : base.substr(i + 1);
}

class ProgressBar : public Logger
{
private:

    struct ActInfo
    {
        std::string s, lastLine, phase;
        ActivityType type = actUnknown;
        uint64_t done = 0;
        uint64_t expected = 0;
        uint64_t running = 0;
        uint64_t failed = 0;
        std::map<ActivityType, uint64_t> expectedByType;
        bool visible = true;
        ActivityId parent;
        std::optional<std::string> name;
    };

    struct ActivitiesByType
    {
        std::map<ActivityId, std::list<ActInfo>::iterator> its;
        uint64_t done = 0;
        uint64_t expected = 0;
        uint64_t failed = 0;
    };

    struct State
    {
        std::list<ActInfo> activities;
        std::map<ActivityId, std::list<ActInfo>::iterator> its;

        std::map<ActivityType, ActivitiesByType> activitiesByType;

        uint64_t filesLinked = 0, bytesLinked = 0;

        uint64_t corruptedPaths = 0, untrustedPaths = 0;

        bool active = true;
        bool haveUpdate = true;
    };

    Sync<State> state_;

    std::thread updateThread;

    std::condition_variable quitCV, updateCV;

    bool printBuildLogs;
    bool isTTY;

public:

    ProgressBar(bool printBuildLogs, bool isTTY)
        : printBuildLogs(printBuildLogs)
        , isTTY(isTTY)
    {
        state_.lock()->active = isTTY;
        updateThread = std::thread([&]() {
            auto state(state_.lock());
            while (state->active) {
                if (!state->haveUpdate)
                    state.wait(updateCV);
                draw(*state);
                state.wait_for(quitCV, std::chrono::milliseconds(50));
            }
        });
    }

    ~ProgressBar()
    {
        stop();
        updateThread.join();
    }

    void stop() override
    {
        auto state(state_.lock());
        if (!state->active) return;
        state->active = false;
        writeToStderr("\r\e[K");
        updateCV.notify_one();
        quitCV.notify_one();
    }

    bool isVerbose() override {
        return printBuildLogs;
    }

    void log(Verbosity lvl, const FormatOrString & fs) override
    {
        if (lvl > verbosity) return;
        auto state(state_.lock());
        log(*state, lvl, fs.s);
    }

    void logEI(const ErrorInfo &ei) override
    {
        auto state(state_.lock());

        std::stringstream oss;
        showErrorInfo(oss, ei, loggerSettings.showTrace.get());

        log(*state, ei.level, oss.str());
    }

    void log(State & state, Verbosity lvl, const std::string & s)
    {
        if (state.active) {
            writeToStderr("\r\e[K" + filterANSIEscapes(s, !isTTY) + ANSI_NORMAL "\n");
            draw(state);
        } else {
            auto s2 = s + ANSI_NORMAL "\n";
            if (!isTTY) s2 = filterANSIEscapes(s2, true);
            writeToStderr(s2);
        }
    }

    void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
        const std::string & s, const Fields & fields, ActivityId parent) override
    {
        auto state(state_.lock());

        if (lvl <= verbosity && !s.empty() && type != actBuildWaiting)
            log(*state, lvl, s + "...");

        state->activities.emplace_back(ActInfo());
        auto i = std::prev(state->activities.end());
        i->s = s;
        i->type = type;
        i->parent = parent;
        state->its.emplace(act, i);
        state->activitiesByType[type].its.emplace(act, i);

        if (type == actBuild) {
            std::string name(storePathToName(getS(fields, 0)));
            if (hasSuffix(name, ".drv"))
                name = name.substr(0, name.size() - 4);
            i->s = fmt("building " ANSI_BOLD "%s" ANSI_NORMAL, name);
            auto machineName = getS(fields, 1);
            if (machineName != "")
                i->s += fmt(" on " ANSI_BOLD "%s" ANSI_NORMAL, machineName);
            auto curRound = getI(fields, 2);
            auto nrRounds = getI(fields, 3);
            if (nrRounds != 1)
                i->s += fmt(" (round %d/%d)", curRound, nrRounds);
            i->name = DrvName(name).name;
        }

        if (type == actSubstitute) {
            auto name = storePathToName(getS(fields, 0));
            auto sub = getS(fields, 1);
            i->s = fmt(
                hasPrefix(sub, "local")
                ? "copying " ANSI_BOLD "%s" ANSI_NORMAL " from %s"
                : "fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s",
                name, sub);
        }

        if (type == actPostBuildHook) {
            auto name = storePathToName(getS(fields, 0));
            if (hasSuffix(name, ".drv"))
                name = name.substr(0, name.size() - 4);
            i->s = fmt("post-build " ANSI_BOLD "%s" ANSI_NORMAL, name);
            i->name = DrvName(name).name;
        }

        if (type == actQueryPathInfo) {
            auto name = storePathToName(getS(fields, 0));
            i->s = fmt("querying " ANSI_BOLD "%s" ANSI_NORMAL " on %s", name, getS(fields, 1));
        }

        if ((type == actFileTransfer && hasAncestor(*state, actCopyPath, parent))
            || (type == actFileTransfer && hasAncestor(*state, actQueryPathInfo, parent))
            || (type == actCopyPath && hasAncestor(*state, actSubstitute, parent)))
            i->visible = false;

        update(*state);
    }

    /* Check whether an activity has an ancestore with the specified
       type. */
    bool hasAncestor(State & state, ActivityType type, ActivityId act)
    {
        while (act != 0) {
            auto i = state.its.find(act);
            if (i == state.its.end()) break;
            if (i->second->type == type) return true;
            act = i->second->parent;
        }
        return false;
    }

    void stopActivity(ActivityId act) override
    {
        auto state(state_.lock());

        auto i = state->its.find(act);
        if (i != state->its.end()) {

            auto & actByType = state->activitiesByType[i->second->type];
            actByType.done += i->second->done;
            actByType.failed += i->second->failed;

            for (auto & j : i->second->expectedByType)
                state->activitiesByType[j.first].expected -= j.second;

            actByType.its.erase(act);
            state->activities.erase(i->second);
            state->its.erase(i);
        }

        update(*state);
    }

    void result(ActivityId act, ResultType type, const std::vector<Field> & fields) override
    {
        auto state(state_.lock());

        if (type == resFileLinked) {
            state->filesLinked++;
            state->bytesLinked += getI(fields, 0);
            update(*state);
        }

        else if (type == resBuildLogLine || type == resPostBuildLogLine) {
            auto lastLine = chomp(getS(fields, 0));
            if (!lastLine.empty()) {
                auto i = state->its.find(act);
                assert(i != state->its.end());
                ActInfo info = *i->second;
                if (printBuildLogs) {
                    auto suffix = "> ";
                    if (type == resPostBuildLogLine) {
                        suffix = " (post)> ";
                    }
                    log(*state, lvlInfo, ANSI_FAINT + info.name.value_or("unnamed") + suffix + ANSI_NORMAL + lastLine);
                } else {
                    state->activities.erase(i->second);
                    info.lastLine = lastLine;
                    state->activities.emplace_back(info);
                    i->second = std::prev(state->activities.end());
                    update(*state);
                }
            }
        }

        else if (type == resUntrustedPath) {
            state->untrustedPaths++;
            update(*state);
        }

        else if (type == resCorruptedPath) {
            state->corruptedPaths++;
            update(*state);
        }

        else if (type == resSetPhase) {
            auto i = state->its.find(act);
            assert(i != state->its.end());
            i->second->phase = getS(fields, 0);
            update(*state);
        }

        else if (type == resProgress) {
            auto i = state->its.find(act);
            assert(i != state->its.end());
            ActInfo & actInfo = *i->second;
            actInfo.done = getI(fields, 0);
            actInfo.expected = getI(fields, 1);
            actInfo.running = getI(fields, 2);
            actInfo.failed = getI(fields, 3);
            update(*state);
        }

        else if (type == resSetExpected) {
            auto i = state->its.find(act);
            assert(i != state->its.end());
            ActInfo & actInfo = *i->second;
            auto type = (ActivityType) getI(fields, 0);
            auto & j = actInfo.expectedByType[type];
            state->activitiesByType[type].expected -= j;
            j = getI(fields, 1);
            state->activitiesByType[type].expected += j;
            update(*state);
        }
    }

    void update(State & state)
    {
        state.haveUpdate = true;
        updateCV.notify_one();
    }

    void draw(State & state)
    {
        state.haveUpdate = false;
        if (!state.active) return;

        std::string line;

        std::string status = getStatus(state);
        if (!status.empty()) {
            line += '[';
            line += status;
            line += "]";
        }

        if (!state.activities.empty()) {
            if (!status.empty()) line += " ";
            auto i = state.activities.rbegin();

            while (i != state.activities.rend() && (!i->visible || (i->s.empty() && i->lastLine.empty())))
                ++i;

            if (i != state.activities.rend()) {
                line += i->s;
                if (!i->phase.empty()) {
                    line += " (";
                    line += i->phase;
                    line += ")";
                }
                if (!i->lastLine.empty()) {
                    if (!i->s.empty()) line += ": ";
                    line += i->lastLine;
                }
            }
        }

        auto width = getWindowSize().second;
        if (width <= 0) width = std::numeric_limits<decltype(width)>::max();

        writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K");
    }

    std::string getStatus(State & state)
    {
        auto MiB = 1024.0 * 1024.0;

        std::string res;

        auto renderActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) {
            auto & act = state.activitiesByType[type];
            uint64_t done = act.done, expected = act.done, running = 0, failed = act.failed;
            for (auto & j : act.its) {
                done += j.second->done;
                expected += j.second->expected;
                running += j.second->running;
                failed += j.second->failed;
            }

            expected = std::max(expected, act.expected);

            std::string s;

            if (running || done || expected || failed) {
                if (running)
                    if (expected != 0)
                        s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt,
                            running / unit, done / unit, expected / unit);
                    else
                        s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL,
                            running / unit, done / unit);
                else if (expected != done)
                    if (expected != 0)
                        s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt,
                            done / unit, expected / unit);
                    else
                        s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL, done / unit);
                else
                    s = fmt(done ? ANSI_GREEN + numberFmt + ANSI_NORMAL : numberFmt, done / unit);
                s = fmt(itemFmt, s);

                if (failed)
                    s += fmt(" (" ANSI_RED "%d failed" ANSI_NORMAL ")", failed / unit);
            }

            return s;
        };

        auto showActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) {
            auto s = renderActivity(type, itemFmt, numberFmt, unit);
            if (s.empty()) return;
            if (!res.empty()) res += ", ";
            res += s;
        };

        showActivity(actBuilds, "%s built");

        auto s1 = renderActivity(actCopyPaths, "%s copied");
        auto s2 = renderActivity(actCopyPath, "%s MiB", "%.1f", MiB);

        if (!s1.empty() || !s2.empty()) {
            if (!res.empty()) res += ", ";
            if (s1.empty()) res += "0 copied"; else res += s1;
            if (!s2.empty()) { res += " ("; res += s2; res += ')'; }
        }

        showActivity(actFileTransfer, "%s MiB DL", "%.1f", MiB);

        {
            auto s = renderActivity(actOptimiseStore, "%s paths optimised");
            if (s != "") {
                s += fmt(", %.1f MiB / %d inodes freed", state.bytesLinked / MiB, state.filesLinked);
                if (!res.empty()) res += ", ";
                res += s;
            }
        }

        // FIXME: don't show "done" paths in green.
        showActivity(actVerifyPaths, "%s paths verified");

        if (state.corruptedPaths) {
            if (!res.empty()) res += ", ";
            res += fmt(ANSI_RED "%d corrupted" ANSI_NORMAL, state.corruptedPaths);
        }

        if (state.untrustedPaths) {
            if (!res.empty()) res += ", ";
            res += fmt(ANSI_RED "%d untrusted" ANSI_NORMAL, state.untrustedPaths);
        }

        return res;
    }

    void writeToStdout(std::string_view s) override
    {
        auto state(state_.lock());
        if (state->active) {
            std::cerr << "\r\e[K";
            Logger::writeToStdout(s);
            draw(*state);
        } else {
            Logger::writeToStdout(s);
        }
    }

    std::optional<char> ask(std::string_view msg) override
    {
        auto state(state_.lock());
        if (!state->active || !isatty(STDIN_FILENO)) return {};
        std::cerr << fmt("\r\e[K%s ", msg);
        auto s = trim(readLine(STDIN_FILENO));
        if (s.size() != 1) return {};
        draw(*state);
        return s[0];
    }
};

Logger * makeProgressBar(bool printBuildLogs)
{
    return new ProgressBar(
        printBuildLogs,
        isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb"
    );
}

void startProgressBar(bool printBuildLogs)
{
    logger = makeProgressBar(printBuildLogs);
}

void stopProgressBar()
{
    auto progressBar = dynamic_cast<ProgressBar *>(logger);
    if (progressBar) progressBar->stop();

}

}
<|endoftext|>


<commit_before>/*
 *  main.cpp
 *  Program:  kalarm
 *  (C) 2001 - 2003 by David Jarvie  software@astrojar.org.uk
 *
 *  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 2 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, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include "kalarm.h"

#include <stdlib.h>

#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include <klocale.h>
#include <kdebug.h>

#include "kalarmapp.h"

#define PROGRAM_NAME "kalarm"

QCString execArguments;    // argument to --exec option

static KCmdLineOptions options[] =
{
	{ "a", 0, 0 },
	{ "ack-confirm", I18N_NOOP("Prompt for confirmation when alarm is acknowledged"), 0 },
	{ "A", 0, 0 },
	{ "attach <url>", I18N_NOOP("Attach file to email (repeat as needed)"), 0 },
	{ "bcc", I18N_NOOP("Blind copy email to self"), 0 },
	{ "b", 0, 0 },
	{ "beep", I18N_NOOP("Beep when message is displayed"), 0 },
	{ "colour", 0, 0 },
	{ "c", 0, 0 },
	{ "color <color>", I18N_NOOP("Message background color (name or hex 0xRRGGBB)"), 0 },
	{ "calendarURL <url>", I18N_NOOP("URL of calendar file"), 0 },
	{ "cancelEvent <eventID>", I18N_NOOP("Cancel alarm with the specified event ID"), 0 },
	{ "e", 0, 0 },
	{ "exec <commandline>", I18N_NOOP("Execute a shell command line"), 0 },
	{ "f", 0, 0 },
	{ "file <url>", I18N_NOOP("File to display"), 0 },
	{ "handleEvent <eventID>", I18N_NOOP("Trigger or cancel alarm with the specified event ID"), 0 },
	{ "i", 0, 0 },
	{ "interval <period>", I18N_NOOP("Interval between alarm recurrences"), 0 },
	{ "l", 0, 0 },
	{ "late-cancel", I18N_NOOP("Cancel alarm if it cannot be triggered on time"), 0 },
	{ "L", 0, 0 },
	{ "login", I18N_NOOP("Repeat alarm at every login"), 0 },
	{ "m", 0, 0 },
	{ "mail <address>", I18N_NOOP("Send an email to the given address (repeat as needed)"), 0 },
	{ "recurrence <spec>", I18N_NOOP("Specify alarm recurrence using iCalendar syntax"), 0 },
	{ "R", 0, 0 },
	{ "reminder <period>", I18N_NOOP("Display reminder in advance of alarm"), 0 },
	{ "r", 0, 0 },
	{ "repeat <count>", I18N_NOOP("Number of times to repeat alarm (after the initial occasion)"), 0 },
	{ "reset", I18N_NOOP("Reset the alarm scheduling daemon"), 0 },
	{ "s", 0, 0 },
	{ "sound <url>", I18N_NOOP("Audio file to play"), 0 },
	{ "stop", I18N_NOOP("Stop the alarm scheduling daemon"), 0 },
	{ "S", 0, 0 },
	{ "subject ", I18N_NOOP("Email subject line"), 0 },
	{ "t", 0, 0 },
	{ "time <time>", I18N_NOOP("Trigger alarm at time [[[yyyy-]mm-]dd-]hh:mm, or date yyyy-mm-dd"), 0 },
	{ "tray", I18N_NOOP("Display system tray icon"), 0 },
	{ "u", 0, 0 },
	{ "until <time>", I18N_NOOP("Repeat until time [[[yyyy-]mm-]dd-]hh:mm, or date yyyy-mm-dd"), 0 },
	{ "displayEvent <eventID>", I18N_NOOP("Obsolete: use --triggerEvent instead"), 0 },
	{ "triggerEvent <eventID>", I18N_NOOP("Trigger alarm with the specified event ID"), 0 },
	{ "+[message]", I18N_NOOP("Message text to display"), 0 },
	KCmdLineLastOption
};

int main(int argc, char *argv[])
{
	KAboutData aboutData(PROGRAM_NAME, I18N_NOOP("KAlarm"), KALARM_VERSION,
		I18N_NOOP("Personal alarm message, command and email scheduler for KDE"),
		KAboutData::License_GPL,
		"(c) 2001 - 2003, David Jarvie", 0, "http://www.astrojar.org.uk/linux/kalarm.html");
	aboutData.addAuthor("David Jarvie", 0, "software@astrojar.org.uk");

	// Fetch all command line options/arguments after --exec and concatenate
	// them into a single argument. Then change the leading '-'.
	// This is necessary because the "!" indicator in the 'options'
	// array above doesn't work (on KDE2, at least)
	for (int i = 0;  i < argc;  ++i)
	{
		if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--exec"))
		{
			while (++i < argc)
			{
				execArguments += argv[i];
				if (i < argc - 1)
					execArguments += ' ';
				argv[i][0] = 'x';     // in case it's an option
			}
			break;
		}
	}
	KCmdLineArgs::init(argc, argv, &aboutData);
	KCmdLineArgs::addCmdLineOptions(options);
	KUniqueApplication::addCmdLineOptions();

	if (!KAlarmApp::start())
	{
		// An instance of the application is already running
		exit(0);
	}

	// This is the first time through
	kdDebug(5950) << "main(): initialising\n";
	KAlarmApp* app = KAlarmApp::getInstance();
	app->restoreSession();
	return app->exec();
}
<commit_msg>Comment changes<commit_after>/*
 *  main.cpp
 *  Program:  kalarm
 *  (C) 2001, 2002, 2003 by David Jarvie  software@astrojar.org.uk
 *
 *  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 2 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, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 *  As a special exception, permission is given to link this program
 *  with any edition of Qt, and distribute the resulting executable,
 *  without including the source code for Qt in the source distribution.
 */

#include "kalarm.h"

#include <stdlib.h>

#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include <klocale.h>
#include <kdebug.h>

#include "kalarmapp.h"

#define PROGRAM_NAME "kalarm"

QCString execArguments;    // argument to --exec option

static KCmdLineOptions options[] =
{
	{ "a", 0, 0 },
	{ "ack-confirm", I18N_NOOP("Prompt for confirmation when alarm is acknowledged"), 0 },
	{ "A", 0, 0 },
	{ "attach <url>", I18N_NOOP("Attach file to email (repeat as needed)"), 0 },
	{ "bcc", I18N_NOOP("Blind copy email to self"), 0 },
	{ "b", 0, 0 },
	{ "beep", I18N_NOOP("Beep when message is displayed"), 0 },
	{ "colour", 0, 0 },
	{ "c", 0, 0 },
	{ "color <color>", I18N_NOOP("Message background color (name or hex 0xRRGGBB)"), 0 },
	{ "calendarURL <url>", I18N_NOOP("URL of calendar file"), 0 },
	{ "cancelEvent <eventID>", I18N_NOOP("Cancel alarm with the specified event ID"), 0 },
	{ "e", 0, 0 },
	{ "exec <commandline>", I18N_NOOP("Execute a shell command line"), 0 },
	{ "f", 0, 0 },
	{ "file <url>", I18N_NOOP("File to display"), 0 },
	{ "handleEvent <eventID>", I18N_NOOP("Trigger or cancel alarm with the specified event ID"), 0 },
	{ "i", 0, 0 },
	{ "interval <period>", I18N_NOOP("Interval between alarm recurrences"), 0 },
	{ "l", 0, 0 },
	{ "late-cancel", I18N_NOOP("Cancel alarm if it cannot be triggered on time"), 0 },
	{ "L", 0, 0 },
	{ "login", I18N_NOOP("Repeat alarm at every login"), 0 },
	{ "m", 0, 0 },
	{ "mail <address>", I18N_NOOP("Send an email to the given address (repeat as needed)"), 0 },
	{ "recurrence <spec>", I18N_NOOP("Specify alarm recurrence using iCalendar syntax"), 0 },
	{ "R", 0, 0 },
	{ "reminder <period>", I18N_NOOP("Display reminder in advance of alarm"), 0 },
	{ "r", 0, 0 },
	{ "repeat <count>", I18N_NOOP("Number of times to repeat alarm (after the initial occasion)"), 0 },
	{ "reset", I18N_NOOP("Reset the alarm scheduling daemon"), 0 },
	{ "s", 0, 0 },
	{ "sound <url>", I18N_NOOP("Audio file to play"), 0 },
	{ "stop", I18N_NOOP("Stop the alarm scheduling daemon"), 0 },
	{ "S", 0, 0 },
	{ "subject ", I18N_NOOP("Email subject line"), 0 },
	{ "t", 0, 0 },
	{ "time <time>", I18N_NOOP("Trigger alarm at time [[[yyyy-]mm-]dd-]hh:mm, or date yyyy-mm-dd"), 0 },
	{ "tray", I18N_NOOP("Display system tray icon"), 0 },
	{ "u", 0, 0 },
	{ "until <time>", I18N_NOOP("Repeat until time [[[yyyy-]mm-]dd-]hh:mm, or date yyyy-mm-dd"), 0 },
	{ "displayEvent <eventID>", I18N_NOOP("Obsolete: use --triggerEvent instead"), 0 },
	{ "triggerEvent <eventID>", I18N_NOOP("Trigger alarm with the specified event ID"), 0 },
	{ "+[message]", I18N_NOOP("Message text to display"), 0 },
	KCmdLineLastOption
};

int main(int argc, char *argv[])
{
	KAboutData aboutData(PROGRAM_NAME, I18N_NOOP("KAlarm"), KALARM_VERSION,
		I18N_NOOP("Personal alarm message, command and email scheduler for KDE"),
		KAboutData::License_GPL,
		"(c) 2001 - 2003, David Jarvie", 0, "http://www.astrojar.org.uk/linux/kalarm.html");
	aboutData.addAuthor("David Jarvie", 0, "software@astrojar.org.uk");

	// Fetch all command line options/arguments after --exec and concatenate
	// them into a single argument. Then change the leading '-'.
	// This is necessary because the "!" indicator in the 'options'
	// array above doesn't work (on KDE2, at least)
	for (int i = 0;  i < argc;  ++i)
	{
		if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--exec"))
		{
			while (++i < argc)
			{
				execArguments += argv[i];
				if (i < argc - 1)
					execArguments += ' ';
				argv[i][0] = 'x';     // in case it's an option
			}
			break;
		}
	}
	KCmdLineArgs::init(argc, argv, &aboutData);
	KCmdLineArgs::addCmdLineOptions(options);
	KUniqueApplication::addCmdLineOptions();

	if (!KAlarmApp::start())
	{
		// An instance of the application is already running
		exit(0);
	}

	// This is the first time through
	kdDebug(5950) << "main(): initialising\n";
	KAlarmApp* app = KAlarmApp::getInstance();
	app->restoreSession();
	return app->exec();
}
<|endoftext|>


<commit_before>#include "rules.h"
#include "tokenizer.h"
#include "display.h"
#include "space.h"
#include "window.h"
#include "tree.h"
#include "helpers.h"

extern kwm_tiling KWMTiling;

/* Current Window Properties:
 *          float = "true" | "false"
 *          display = "id"
 *
 * Replaces:
 *          kwmc config float
 *          kwmc config capture
 *
 * Additions:
 *          (??) kwmc add-role
 *          (??) override gap / padding
 *          (??) callbacks
 *
 * Examples:
 *          kwmc rule owner="" name="" properties={float=""; display=""} except=""
 *
 * Apply given properties to all iTerm2 windows that does not have
 * a title containing $except:
 *          kwmc rule owner="iTerm2" properties={float="true"} except="borderless"
 *
 * Apply given properties to all iTerm2 windows that does have a
 * title containing $name and at the same times does not contain $except:
 *          kwmc rule owner="iTerm2" name="borderless" properties={float="true"} except="ignore"
 *
 * Apply given properties to all Steam windows
 *          kwmc rule owner="Steam" properties={float="true"}
 *
 * Apply given properties to all iTunes windows
 *          kwmc rule owner="iTunes" properties={display="1"}
*/

bool ParseIdentifier(tokenizer *Tokenizer, std::string *Member)
{
    if(RequireToken(Tokenizer, Token_Equals))
    {
        token Token = GetToken(Tokenizer);
        switch(Token.Type)
        {
            case Token_String:
            {
                std::string String;
                for(int Index = 0; Index < Token.TextLength; ++Index)
                    String += Token.Text[Index];
                *Member = String;
                return true;
            } break;
            default:
            {
                printf("Unexpected token %d: %.*s\n", Token.Type, Token.TextLength, Token.Text);
            } break;
        }
    }
    else
    {
        printf("Expected token '='\n");
    }

    return false;
}

bool ParseProperties(tokenizer *Tokenizer, window_properties *Properties)
{
    if(RequireToken(Tokenizer, Token_Equals))
    {
        if(RequireToken(Tokenizer, Token_OpenBrace))
        {
            Properties->Display = -1;
            Properties->Float = -1;
            bool ValidState = true;

            while(ValidState)
            {
                token Token = GetToken(Tokenizer);
                switch(Token.Type)
                {
                    case Token_SemiColon: { continue; } break;
                    case Token_CloseBrace: { ValidState = false; } break;
                    case Token_Identifier:
                    {
                        if(TokenEquals(Token, "float"))
                        {
                            std::string Value;
                            if(ParseIdentifier(Tokenizer, &Value))
                            {
                                if(Value == "true")
                                    Properties->Float = 1;
                                else if(Value == "false")
                                    Properties->Float = 0;
                            }
                        }
                        else if(TokenEquals(Token, "display"))
                        {
                            std::string Value;
                            if(ParseIdentifier(Tokenizer, &Value))
                                Properties->Display = ConvertStringToInt(Value);
                        }
                    } break;
                    default: { printf("Unexpected token %d: %.*s\n", Token.Type, Token.TextLength, Token.Text); } break; }
            }

            return true;
        }
        else
        {
            printf("Expected token '{'\n");
        }
    }
    else
    {
        printf("Expected token '='\n");
    }

    return false;
}

bool KwmParseRule(std::string RuleSym, window_rule *Rule)
{
    tokenizer Tokenizer = {};
    Tokenizer.At = const_cast<char*>(RuleSym.c_str());

    bool Result = true;
    bool Parsing = true;
    while(Parsing)
    {
        token Token = GetToken(&Tokenizer);
        switch(Token.Type)
        {
            case Token_EndOfStream:
            {
                Parsing = false;
            } break;
            case Token_Unknown:
            {
            } break;
            case Token_Identifier:
            {
                if(TokenEquals(Token, "owner"))
                    Result = Result && ParseIdentifier(&Tokenizer, &Rule->Owner);
                else if(TokenEquals(Token, "name"))
                    Result = Result && ParseIdentifier(&Tokenizer, &Rule->Name);
                else if(TokenEquals(Token, "properties"))
                    Result = Result && ParseProperties(&Tokenizer, &Rule->Properties);
                else if(TokenEquals(Token, "except"))
                    Result = Result && ParseIdentifier(&Tokenizer, &Rule->Except);
            } break;
            default:
            {
                //printf("%d: %.*s\n", Token.Type, Token.TextLength, Token.Text);
            } break;
        }
    }

    return Result;
}

void KwmAddRule(std::string RuleSym)
{
    window_rule Rule = {};
    if(KwmParseRule(RuleSym, &Rule))
    {
        printf("the following rule has been added:\n owner: %s\n name: %s\n float: %d\n display: %d\n except: %s\n", Rule.Owner.c_str(), Rule.Name.c_str(), Rule.Properties.Float, Rule.Properties.Display, Rule.Except.c_str());
        KWMTiling.WindowRules.push_back(Rule);
    }
}

bool CheckWindowRule(window_rule *Rule, window_info *Window)
{
    bool Match = true;
    if(!Rule->Owner.empty())
        Match = Rule->Owner == Window->Owner;

    if(!Rule->Name.empty())
        Match = Match && Window->Name.find(Rule->Name) != std::string::npos;

    if(!Rule->Except.empty())
        Match = Match && Window->Name.find(Rule->Except) == std::string::npos;

    return Match;
}

bool ApplyWindowRules(window_info *Window)
{
    bool ApplyRules = false;
    Window->Display = -1;
    Window->Float = 0;

    for(int Index = 0; Index < KWMTiling.WindowRules.size(); ++Index)
    {
        window_rule *Rule = &KWMTiling.WindowRules[Index];
        if(CheckWindowRule(Rule, Window))
        {
            if(Rule->Properties.Float != -1)
                Window->Float = Rule->Properties.Float == 1;

            if(Rule->Properties.Display != -1)
                Window->Display = Rule->Properties.Display;

            ApplyRules = true;
        }
    }

    if(ApplyRules)
    {
        if(Window->Float)
        {
            screen_info *ScreenOfWindow = GetDisplayOfWindow(Window);
            if(ScreenOfWindow)
            {
                space_info *SpaceOfWindow = GetActiveSpaceOfScreen(ScreenOfWindow);
                if(SpaceOfWindow->Settings.Mode == SpaceModeBSP)
                    RemoveWindowFromBSPTree(ScreenOfWindow, Window->WID, false, false);
                else if(SpaceOfWindow->Settings.Mode == SpaceModeMonocle)
                    RemoveWindowFromMonocleTree(ScreenOfWindow, Window->WID, false);
            }
        }

        if(Window->Display != -1)
        {
            screen_info *Screen = GetDisplayFromScreenID(Window->Display);
            if(Screen && Screen != GetDisplayOfWindow(Window))
            {
                MoveWindowToDisplay(Window, Window->Display, false, true);
                return true;
            }
        }
    }

    return false;
}
<commit_msg>remove printfs and use DEBUG macro<commit_after>#include "rules.h"
#include "tokenizer.h"
#include "display.h"
#include "space.h"
#include "window.h"
#include "tree.h"
#include "helpers.h"

extern kwm_tiling KWMTiling;

/* Current Window Properties:
 *          float = "true" | "false"
 *          display = "id"
 *
 * Replaces:
 *          kwmc config float
 *          kwmc config capture
 *
 * Additions:
 *          (??) kwmc add-role
 *          (??) override gap / padding
 *          (??) callbacks
 *
 * Examples:
 *          kwmc rule owner="" name="" properties={float=""; display=""} except=""
 *
 * Apply given properties to all iTerm2 windows that does not have
 * a title containing $except:
 *          kwmc rule owner="iTerm2" properties={float="true"} except="borderless"
 *
 * Apply given properties to all iTerm2 windows that does have a
 * title containing $name and at the same times does not contain $except:
 *          kwmc rule owner="iTerm2" name="borderless" properties={float="true"} except="ignore"
 *
 * Apply given properties to all Steam windows
 *          kwmc rule owner="Steam" properties={float="true"}
 *
 * Apply given properties to all iTunes windows
 *          kwmc rule owner="iTunes" properties={display="1"}
*/

bool ParseIdentifier(tokenizer *Tokenizer, std::string *Member)
{
    if(RequireToken(Tokenizer, Token_Equals))
    {
        token Token = GetToken(Tokenizer);
        switch(Token.Type)
        {
            case Token_String:
            {
                std::string String;
                for(int Index = 0; Index < Token.TextLength; ++Index)
                    String += Token.Text[Index];
                *Member = String;
                return true;
            } break;
            default:
            {
                DEBUG("Expected token of type Token_String")
            } break;
        }
    }
    else
    {
        DEBUG("Expected token '='\n");
    }

    return false;
}

bool ParseProperties(tokenizer *Tokenizer, window_properties *Properties)
{
    if(RequireToken(Tokenizer, Token_Equals))
    {
        if(RequireToken(Tokenizer, Token_OpenBrace))
        {
            Properties->Display = -1;
            Properties->Float = -1;
            bool ValidState = true;

            while(ValidState)
            {
                token Token = GetToken(Tokenizer);
                switch(Token.Type)
                {
                    case Token_SemiColon: { continue; } break;
                    case Token_CloseBrace: { ValidState = false; } break;
                    case Token_Identifier:
                    {
                        if(TokenEquals(Token, "float"))
                        {
                            std::string Value;
                            if(ParseIdentifier(Tokenizer, &Value))
                            {
                                if(Value == "true")
                                    Properties->Float = 1;
                                else if(Value == "false")
                                    Properties->Float = 0;
                            }
                        }
                        else if(TokenEquals(Token, "display"))
                        {
                            std::string Value;
                            if(ParseIdentifier(Tokenizer, &Value))
                                Properties->Display = ConvertStringToInt(Value);
                        }
                    } break;
                    default: { DEBUG("Expected token of type Token_Identifier") } break;
                }
            }

            return true;
        }
        else
        {
            DEBUG("Expected token '{'")
        }
    }
    else
    {
        DEBUG("Expected token '='")
    }

    return false;
}

bool KwmParseRule(std::string RuleSym, window_rule *Rule)
{
    tokenizer Tokenizer = {};
    Tokenizer.At = const_cast<char*>(RuleSym.c_str());

    bool Result = true;
    bool Parsing = true;
    while(Parsing)
    {
        token Token = GetToken(&Tokenizer);
        switch(Token.Type)
        {
            case Token_EndOfStream:
            {
                Parsing = false;
            } break;
            case Token_Unknown:
            {
            } break;
            case Token_Identifier:
            {
                if(TokenEquals(Token, "owner"))
                    Result = Result && ParseIdentifier(&Tokenizer, &Rule->Owner);
                else if(TokenEquals(Token, "name"))
                    Result = Result && ParseIdentifier(&Tokenizer, &Rule->Name);
                else if(TokenEquals(Token, "properties"))
                    Result = Result && ParseProperties(&Tokenizer, &Rule->Properties);
                else if(TokenEquals(Token, "except"))
                    Result = Result && ParseIdentifier(&Tokenizer, &Rule->Except);
            } break;
            default: { } break;
        }
    }

    return Result;
}

void KwmAddRule(std::string RuleSym)
{
    window_rule Rule = {};
    if(KwmParseRule(RuleSym, &Rule))
        KWMTiling.WindowRules.push_back(Rule);
}

bool CheckWindowRule(window_rule *Rule, window_info *Window)
{
    bool Match = true;
    if(!Rule->Owner.empty())
        Match = Rule->Owner == Window->Owner;

    if(!Rule->Name.empty())
        Match = Match && Window->Name.find(Rule->Name) != std::string::npos;

    if(!Rule->Except.empty())
        Match = Match && Window->Name.find(Rule->Except) == std::string::npos;

    return Match;
}

bool ApplyWindowRules(window_info *Window)
{
    bool ApplyRules = false;
    Window->Display = -1;
    Window->Float = 0;

    for(int Index = 0; Index < KWMTiling.WindowRules.size(); ++Index)
    {
        window_rule *Rule = &KWMTiling.WindowRules[Index];
        if(CheckWindowRule(Rule, Window))
        {
            if(Rule->Properties.Float != -1)
                Window->Float = Rule->Properties.Float == 1;

            if(Rule->Properties.Display != -1)
                Window->Display = Rule->Properties.Display;

            ApplyRules = true;
        }
    }

    if(ApplyRules)
    {
        if(Window->Float)
        {
            screen_info *ScreenOfWindow = GetDisplayOfWindow(Window);
            if(ScreenOfWindow)
            {
                space_info *SpaceOfWindow = GetActiveSpaceOfScreen(ScreenOfWindow);
                if(SpaceOfWindow->Settings.Mode == SpaceModeBSP)
                    RemoveWindowFromBSPTree(ScreenOfWindow, Window->WID, false, false);
                else if(SpaceOfWindow->Settings.Mode == SpaceModeMonocle)
                    RemoveWindowFromMonocleTree(ScreenOfWindow, Window->WID, false);
            }
        }

        if(Window->Display != -1)
        {
            screen_info *Screen = GetDisplayFromScreenID(Window->Display);
            if(Screen && Screen != GetDisplayOfWindow(Window))
            {
                MoveWindowToDisplay(Window, Window->Display, false, true);
                return true;
            }
        }
    }

    return false;
}
<|endoftext|>


<commit_before>/*
 * STLL Simple Text Layouting Library
 *
 * STLL is the legal property of its developers, whose
 * names are listed in the COPYRIGHT file, which is included
 * within the source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include "layouterSDL.h"

#include "layouter.h"
#include "color.h"

#include <ft2build.h>
#include FT_FREETYPE_H

#include <unordered_map>

namespace STLL {

static std::unordered_map<GlyphKey_c, FT_GlyphSlotRec_ *> glyhCache;

static Uint32 getpixel(SDL_Surface *surface, int x, int y)
{
  int bpp = surface->format->BytesPerPixel;
  /* Here p is the address to the pixel we want to retrieve */
  Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

  switch(bpp) {
    case 1:
      return *p;
      break;

    case 2:
      return *(Uint16 *)p;
      break;

    case 3:
      if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
        return p[0] << 16 | p[1] << 8 | p[2];
      else
        return p[0] | p[1] << 8 | p[2] << 16;
      break;

    case 4:
      return *(Uint32 *)p;
      break;

    default:
      return 0;       /* shouldn't happen, but avoids warnings */
  }
}

static void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
  int bpp = surface->format->BytesPerPixel;
  /* Here p is the address to the pixel we want to set */
  Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

  switch(bpp) {
    case 1:
      *p = pixel;
      break;

    case 2:
      *(Uint16 *)p = pixel;
      break;

    case 3:
      if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
        p[0] = (pixel >> 16) & 0xff;
        p[1] = (pixel >> 8) & 0xff;
        p[2] = pixel & 0xff;
      } else {
        p[0] = pixel & 0xff;
        p[1] = (pixel >> 8) & 0xff;
        p[2] = (pixel >> 16) & 0xff;
      }
      break;

    case 4:
      *(Uint32 *)p = pixel;
      break;
  }
}

static uint8_t blend(uint8_t a1, uint8_t a2, uint8_t b)
{
  return a1 + (a2-a1) * b / 255;
}

static void outputGlyph_NONE_Fallback(int sx, int sy, FT_GlyphSlot img, color_c c, SDL_Surface * s)
{
  int stx = (sx+32)/64 + img->bitmap_left;
  int sty = (sy+32)/64 - img->bitmap_top;

  int yp = sty;

  for (int y = 0; y < img->bitmap.rows; y++)
  {
    if (yp >= 0 && yp < s->h)
    {
      int xp = stx;

      for (int x = 0; x < img->bitmap.width; x++)
      {
        if (xp >= 0 && xp < s->w )
        {
          uint8_t a = (c.a()*img->bitmap.buffer[y*img->bitmap.pitch+x]+128)/255;
          auto p = getpixel(s, xp, yp);

          Uint8 r, g, b;
          SDL_GetRGB(p, s->format, &r, &g, &b);

          r = blend(r, c.r(), a);
          g = blend(g, c.g(), a);
          b = blend(b, c.b(), a);

          putpixel(s, xp, yp, SDL_MapRGBA(s->format, r, g, b, SDL_ALPHA_OPAQUE));

        }
        xp++;
      }
    }
    yp++;
  }
}

static void outputGlyph_NONE_RGBx(int sx, int sy, FT_GlyphSlot img, color_c c, SDL_Surface * s)
{
  int stx = (sx+32)/64 + img->bitmap_left;
  int sty = (sy+32)/64 - img->bitmap_top;

  int yp = sty;

  for (int y = 0; y < img->bitmap.rows; y++)
  {
    if (yp >= 0 && yp < s->h)
    {
      int xp = stx;

      uint8_t * dst = (uint8_t*)s->pixels + yp * s->pitch + 4*xp;
      uint8_t * src = img->bitmap.buffer + y*img->bitmap.pitch;

      for (int x = 0; x < img->bitmap.width; x++)
      {
        if (xp >= 0 && xp < s->w )
        {
          uint8_t a = (c.a()*img->bitmap.buffer[y*img->bitmap.pitch+x]+128)/255;

          *dst = blend(*dst, c.b(), a); dst++;
          *dst = blend(*dst, c.g(), a); dst++;
          *dst = blend(*dst, c.r(), a); dst++;
          dst++;
        }
        xp++;
      }
    }
    yp++;
  }
}

static void outputGlyph_HorizontalRGB_Fallback(int sx, int sy, FT_GlyphSlot img, color_c c, SDL_Surface * s)
{
  int stx = sx/64 + img->bitmap_left;
  int sty = sy/64 - img->bitmap_top;
  int stc = (3*sx/64) % 3;

  int yp = sty;

  for (int y = 0; y < img->bitmap.rows; y++)
  {
    if (yp >= 0 && yp < s->h)
    {
      int xp = stx;
      int col = stc;

      for (int x = 0; x < img->bitmap.width; x++)
      {
        if (xp >= 0 && xp < s->w )
        {
          auto p = getpixel(s, xp, yp);

          Uint8 r, g, b;
          SDL_GetRGB(p, s->format, &r, &g, &b);

          // blend values
          switch (col)
          {
            case 0: r = blend(r, c.r(), (img->bitmap.buffer[y*img->bitmap.pitch+x] * c.a() + 128) / 255); break;
            case 1: g = blend(g, c.g(), (img->bitmap.buffer[y*img->bitmap.pitch+x] * c.a() + 128) / 255); break;
            case 2: b = blend(b, c.b(), (img->bitmap.buffer[y*img->bitmap.pitch+x] * c.a() + 128) / 255); break;
          }

          putpixel(s, xp, yp, SDL_MapRGBA(s->format, r, g, b, SDL_ALPHA_OPAQUE));
        }

        col++;
        if (col >= 3)
        {
          col = 0;
          xp++;
        }
      }
    }
    yp++;
  }
}

static void outputGlyph_HorizontalRGB_RGBx(int sx, int sy, FT_GlyphSlot img, color_c c, SDL_Surface * s)
{
  int stx = sx/64 + img->bitmap_left;
  int sty = (sy+32)/64 - img->bitmap_top;
  int stc = (3*sx/64) % 3;

  int yp = sty;

  assert(img->bitmap.width % 3 == 0);

  for (int y = 0; y < img->bitmap.rows; y++)
  {
    if (yp >= 0 && yp < s->h)
    {
      int xp = stx;
      int col = stc;

      uint8_t * dst = (uint8_t*)s->pixels + yp * s->pitch + 4*xp + 2 - col;
      uint8_t * src = img->bitmap.buffer + y*img->bitmap.pitch;

      for (int x = 0; x < img->bitmap.width; x++)
      {
        if (xp >= 0 && xp < s->w )
        {
          switch(col)
          {
            case 0: *dst = blend(*dst, c.r(), (*src*c.a()+128)/255); break;
            case 1: *dst = blend(*dst, c.g(), (*src*c.a()+128)/255); break;
            case 2: *dst = blend(*dst, c.b(), (*src*c.a()+128)/255); break;
          }
        }

        col++;
        dst--;
        src++;
        if (col >= 3)
        {
          col = 0;
          xp++;
          dst+=7;
        }
      }
    }
    yp++;
  }
}

static bool isRGBxSurface(SDL_Surface * s)
{
  auto f = s->format;

  return f->BytesPerPixel == 4 && f->Rmask == 0xFF0000 && f->Gmask == 0xFF00 && f->Bmask == 0xFF;
}

static void outputGlyph(int sx, int sy, FT_GlyphSlot img, SubPixelArrangement sp, color_c c, SDL_Surface * s)
{
  // check for the right image format
  if (!img) return;
  if (img->format != FT_GLYPH_FORMAT_BITMAP) return;
  if (sp == SUBP_NONE && img->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) return;
  if ((sp == SUBP_RGB || sp == SUBP_BGR) && img->bitmap.pixel_mode != FT_PIXEL_MODE_LCD) return;
  if ((sp == SUBP_RGB_V || sp == SUBP_BGR_V) && img->bitmap.pixel_mode != FT_PIXEL_MODE_LCD_V) return;

  // hub code to decide which function to use for output, there are fast functions
  // for some of the output functions and fallbacks that always work

  switch (sp)
  {
    case SUBP_NONE:
      // no suppixels
      if (isRGBxSurface(s))
        outputGlyph_NONE_RGBx(sx, sy, img, c, s);
      else
        outputGlyph_NONE_Fallback(sx, sy, img, c, s);
      break;

    case SUBP_RGB:

      if (isRGBxSurface(s))
        outputGlyph_HorizontalRGB_RGBx(sx, sy, img, c, s);
      else
        outputGlyph_HorizontalRGB_Fallback(sx, sy, img, c, s);
      break;
  }
}

static FT_GlyphSlotRec_ * getGlyph(std::shared_ptr<FontFace_c> face, glyphIndex_t glyph, SubPixelArrangement sp)
{
  GlyphKey_c k(face, glyph, sp);

  auto i = glyhCache.find(k);

  if (i == glyhCache.end())
  {
    auto g = face->renderGlyph(glyph, sp);

    FT_GlyphSlotRec_ * h = new FT_GlyphSlotRec;

    memcpy(h, g, sizeof(FT_GlyphSlotRec));
    h->bitmap.buffer = new uint8_t [g->bitmap.rows*g->bitmap.pitch];
    memcpy(h->bitmap.buffer, g->bitmap.buffer, g->bitmap.rows*g->bitmap.pitch);

    i = glyhCache.insert(std::make_pair(k, h)).first;
  }

  return i->second;
}

void showLayoutSDL(const TextLayout_c & l, int sx, int sy, SDL_Surface * s,
                   SubPixelArrangement sp, imageDrawerSDL_c * images)
{
  /* set up rendering via spanners */
  SDL_Rect r;

#ifndef NDEBUG
  int cnt = 1;
  for (auto & i : l.links)
  {
    int c2 = 10;
    for (auto & j : i.areas)
    {
      r.x = (j.x+sx+32)/64;
      r.y = (j.y+sy+32)/64;
      r.w = (j.w+63)/64;
      r.h = (j.h+63)/64;
      SDL_FillRect(s, &r, SDL_MapRGBA(s->format, 10*(uint8_t)(123*cnt)/c2, 10*(uint8_t)(183*cnt)/c2, 10*(uint8_t)(421*cnt)/c2, 128));

      c2++;
    }
    cnt++;
  }
#endif

  /* render */
  for (auto & i : l.getData())
  {
    switch (i.command)
    {
      case CommandData_c::CMD_GLYPH:
        outputGlyph(sx+i.x, sy+i.y, getGlyph(i.font, i.glyphIndex, sp), sp, i.c, s);
        break;

      case CommandData_c::CMD_RECT:
        r.x = (i.x+sx+32)/64;
        r.y = (i.y+sy+32)/64;
        r.w = (i.x+sx+i.w+32)/64-r.x;
        r.h = (i.y+sy+i.h+32)/64-r.y;
        SDL_FillRect(s, &r, SDL_MapRGBA(s->format, i.c.r(), i.c.g(), i.c.b(), i.c.a()));
        break;

      case CommandData_c::CMD_IMAGE:
        if (images)
          images->draw(i.x+sx, i.y+sy, i.w, i.h, s, i.imageURL);
        break;
    }
  }
}

}
<commit_msg>properly encapsulate the freetype bitmap in our own class<commit_after>/*
 * STLL Simple Text Layouting Library
 *
 * STLL is the legal property of its developers, whose
 * names are listed in the COPYRIGHT file, which is included
 * within the source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include "layouterSDL.h"

#include "layouter.h"
#include "color.h"

#include <ft2build.h>
#include FT_FREETYPE_H

#include <unordered_map>

namespace STLL {

// TODO properly handle it, when FreeType returns an bitmap format that is not supported

class GlyphData_c
{
  public:
    int32_t left;
    int32_t top;
    int32_t rows;
    int32_t width;
    int32_t pitch;
    uint8_t * buffer;

    GlyphData_c(FT_GlyphSlotRec_ * ft) :
      left(ft->bitmap_left), top(ft->bitmap_top), rows(ft->bitmap.rows),
      width(ft->bitmap.width), pitch(ft->bitmap.pitch), buffer(0)
    {
      buffer = new uint8_t [rows*pitch];
      memcpy(buffer, ft->bitmap.buffer, rows*pitch);
    }

    GlyphData_c(GlyphData_c && g) :
      left(g.left), top(g.top), rows(g.rows),
      width(g.width), pitch(g.pitch), buffer(0)
    {
      buffer = g.buffer;
      g.buffer = 0;
    }

    ~GlyphData_c(void)
    {
      if (buffer)
        delete [] buffer;
    }
};

static std::unordered_map<GlyphKey_c, GlyphData_c> glyhCache;

static Uint32 getpixel(SDL_Surface *surface, int x, int y)
{
  int bpp = surface->format->BytesPerPixel;
  /* Here p is the address to the pixel we want to retrieve */
  Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

  switch(bpp) {
    case 1:
      return *p;
      break;

    case 2:
      return *(Uint16 *)p;
      break;

    case 3:
      if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
        return p[0] << 16 | p[1] << 8 | p[2];
      else
        return p[0] | p[1] << 8 | p[2] << 16;
      break;

    case 4:
      return *(Uint32 *)p;
      break;

    default:
      return 0;       /* shouldn't happen, but avoids warnings */
  }
}

static void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
  int bpp = surface->format->BytesPerPixel;
  /* Here p is the address to the pixel we want to set */
  Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

  switch(bpp) {
    case 1:
      *p = pixel;
      break;

    case 2:
      *(Uint16 *)p = pixel;
      break;

    case 3:
      if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
        p[0] = (pixel >> 16) & 0xff;
        p[1] = (pixel >> 8) & 0xff;
        p[2] = pixel & 0xff;
      } else {
        p[0] = pixel & 0xff;
        p[1] = (pixel >> 8) & 0xff;
        p[2] = (pixel >> 16) & 0xff;
      }
      break;

    case 4:
      *(Uint32 *)p = pixel;
      break;
  }
}

static uint8_t blend(uint8_t a1, uint8_t a2, uint8_t b)
{
  return a1 + (a2-a1) * b / 255;
}

static void outputGlyph_NONE_Fallback(int sx, int sy, const GlyphData_c & img, color_c c, SDL_Surface * s)
{
  int stx = (sx+32)/64 + img.left;
  int sty = (sy+32)/64 - img.top;

  int yp = sty;

  for (int y = 0; y < img.rows; y++)
  {
    if (yp >= 0 && yp < s->h)
    {
      int xp = stx;

      for (int x = 0; x < img.width; x++)
      {
        if (xp >= 0 && xp < s->w )
        {
          uint8_t a = (c.a()*img.buffer[y*img.pitch+x]+128)/255;
          auto p = getpixel(s, xp, yp);

          Uint8 r, g, b;
          SDL_GetRGB(p, s->format, &r, &g, &b);

          r = blend(r, c.r(), a);
          g = blend(g, c.g(), a);
          b = blend(b, c.b(), a);

          putpixel(s, xp, yp, SDL_MapRGBA(s->format, r, g, b, SDL_ALPHA_OPAQUE));

        }
        xp++;
      }
    }
    yp++;
  }
}

static void outputGlyph_NONE_RGBx(int sx, int sy, const GlyphData_c & img, color_c c, SDL_Surface * s)
{
  int stx = (sx+32)/64 + img.left;
  int sty = (sy+32)/64 - img.top;

  int yp = sty;

  for (int y = 0; y < img.rows; y++)
  {
    if (yp >= 0 && yp < s->h)
    {
      int xp = stx;

      uint8_t * dst = (uint8_t*)s->pixels + yp * s->pitch + 4*xp;
      uint8_t * src = img.buffer + y*img.pitch;

      for (int x = 0; x < img.width; x++)
      {
        if (xp >= 0 && xp < s->w )
        {
          uint8_t a = (c.a()*img.buffer[y*img.pitch+x]+128)/255;

          *dst = blend(*dst, c.b(), a); dst++;
          *dst = blend(*dst, c.g(), a); dst++;
          *dst = blend(*dst, c.r(), a); dst++;
          dst++;
        }
        xp++;
      }
    }
    yp++;
  }
}

static void outputGlyph_HorizontalRGB_Fallback(int sx, int sy, const GlyphData_c & img, color_c c, SDL_Surface * s)
{
  int stx = sx/64 + img.left;
  int sty = sy/64 - img.top;
  int stc = (3*sx/64) % 3;

  int yp = sty;

  for (int y = 0; y < img.rows; y++)
  {
    if (yp >= 0 && yp < s->h)
    {
      int xp = stx;
      int col = stc;

      for (int x = 0; x < img.width; x++)
      {
        if (xp >= 0 && xp < s->w )
        {
          auto p = getpixel(s, xp, yp);

          Uint8 r, g, b;
          SDL_GetRGB(p, s->format, &r, &g, &b);

          // blend values
          switch (col)
          {
            case 0: r = blend(r, c.r(), (img.buffer[y*img.pitch+x] * c.a() + 128) / 255); break;
            case 1: g = blend(g, c.g(), (img.buffer[y*img.pitch+x] * c.a() + 128) / 255); break;
            case 2: b = blend(b, c.b(), (img.buffer[y*img.pitch+x] * c.a() + 128) / 255); break;
          }

          putpixel(s, xp, yp, SDL_MapRGBA(s->format, r, g, b, SDL_ALPHA_OPAQUE));
        }

        col++;
        if (col >= 3)
        {
          col = 0;
          xp++;
        }
      }
    }
    yp++;
  }
}

static void outputGlyph_HorizontalRGB_RGBx(int sx, int sy, const GlyphData_c & img, color_c c, SDL_Surface * s)
{
  int stx = sx/64 + img.left;
  int sty = (sy+32)/64 - img.top;
  int stc = (3*sx/64) % 3;

  int yp = sty;

  assert(img.width % 3 == 0);

  for (int y = 0; y < img.rows; y++)
  {
    if (yp >= 0 && yp < s->h)
    {
      int xp = stx;
      int col = stc;

      uint8_t * dst = (uint8_t*)s->pixels + yp * s->pitch + 4*xp + 2 - col;
      uint8_t * src = img.buffer + y*img.pitch;

      for (int x = 0; x < img.width; x++)
      {
        if (xp >= 0 && xp < s->w )
        {
          switch(col)
          {
            case 0: *dst = blend(*dst, c.r(), (*src*c.a()+128)/255); break;
            case 1: *dst = blend(*dst, c.g(), (*src*c.a()+128)/255); break;
            case 2: *dst = blend(*dst, c.b(), (*src*c.a()+128)/255); break;
          }
        }

        col++;
        dst--;
        src++;
        if (col >= 3)
        {
          col = 0;
          xp++;
          dst+=7;
        }
      }
    }
    yp++;
  }
}

static bool isRGBxSurface(SDL_Surface * s)
{
  auto f = s->format;

  return f->BytesPerPixel == 4 && f->Rmask == 0xFF0000 && f->Gmask == 0xFF00 && f->Bmask == 0xFF;
}

static void outputGlyph(int sx, int sy, const GlyphData_c & img, SubPixelArrangement sp, color_c c, SDL_Surface * s)
{
  // check for the right image format

  // hub code to decide which function to use for output, there are fast functions
  // for some of the output functions and fallbacks that always work

  switch (sp)
  {
    case SUBP_NONE:
      // no suppixels
      if (isRGBxSurface(s))
        outputGlyph_NONE_RGBx(sx, sy, img, c, s);
      else
        outputGlyph_NONE_Fallback(sx, sy, img, c, s);
      break;

    case SUBP_RGB:

      if (isRGBxSurface(s))
        outputGlyph_HorizontalRGB_RGBx(sx, sy, img, c, s);
      else
        outputGlyph_HorizontalRGB_Fallback(sx, sy, img, c, s);
      break;
  }
}

static GlyphData_c & getGlyph(std::shared_ptr<FontFace_c> face, glyphIndex_t glyph, SubPixelArrangement sp)
{
  GlyphKey_c k(face, glyph, sp);

  auto i = glyhCache.find(k);

  if (i == glyhCache.end())
  {
    auto g = face->renderGlyph(glyph, sp);

    assert(g->format == FT_GLYPH_FORMAT_BITMAP);
    assert( sp != SUBP_NONE                       || g->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
    assert((sp != SUBP_RGB && sp != SUBP_BGR)     || g->bitmap.pixel_mode == FT_PIXEL_MODE_LCD);
    assert((sp != SUBP_RGB_V && sp != SUBP_BGR_V) || g->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V);

    i = glyhCache.insert(std::make_pair(k, std::move(GlyphData_c(g)))).first;
  }

  return i->second;
}

void showLayoutSDL(const TextLayout_c & l, int sx, int sy, SDL_Surface * s,
                   SubPixelArrangement sp, imageDrawerSDL_c * images)
{
  /* set up rendering via spanners */
  SDL_Rect r;

#ifndef NDEBUG
  int cnt = 1;
  for (auto & i : l.links)
  {
    int c2 = 10;
    for (auto & j : i.areas)
    {
      r.x = (j.x+sx+32)/64;
      r.y = (j.y+sy+32)/64;
      r.w = (j.w+63)/64;
      r.h = (j.h+63)/64;
      SDL_FillRect(s, &r, SDL_MapRGBA(s->format, 10*(uint8_t)(123*cnt)/c2, 10*(uint8_t)(183*cnt)/c2, 10*(uint8_t)(421*cnt)/c2, 128));

      c2++;
    }
    cnt++;
  }
#endif

  /* render */
  for (auto & i : l.getData())
  {
    switch (i.command)
    {
      case CommandData_c::CMD_GLYPH:
        outputGlyph(sx+i.x, sy+i.y, getGlyph(i.font, i.glyphIndex, sp), sp, i.c, s);
        break;

      case CommandData_c::CMD_RECT:
        r.x = (i.x+sx+32)/64;
        r.y = (i.y+sy+32)/64;
        r.w = (i.x+sx+i.w+32)/64-r.x;
        r.h = (i.y+sy+i.h+32)/64-r.y;
        SDL_FillRect(s, &r, SDL_MapRGBA(s->format, i.c.r(), i.c.g(), i.c.b(), i.c.a()));
        break;

      case CommandData_c::CMD_IMAGE:
        if (images)
          images->draw(i.x+sx, i.y+sy, i.w, i.h, s, i.imageURL);
        break;
    }
  }
}

}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Jonathan Howard
 * License: https://github.com/v3n/altertum/blob/master/LICENSE
 */

#include <cstring>

#include "macros.h"
#include "lua_environment.h"

namespace altertum
{

/* from taylor001/crown:lua_environment.cpp:50 */
// When an error occurs, logs the error message and pauses the engine.
static int error_handler(lua_State* L)
{
    lua_getfield(L, LUA_GLOBALSINDEX, "debug");
    if (!lua_istable(L, -1))
    {
        lua_pop(L, 1);
        return 0;
    }

    lua_getfield(L, -1, "traceback");
    if (!lua_isfunction(L, -1))
    {
        lua_pop(L, 2);
        return 0;
    }

    lua_pushvalue(L, 1); // Pass error message
    lua_pushinteger(L, 2);
    lua_call(L, 2, 1); // Call debug.traceback

    printf(lua_tostring(L, -1)); // Print error message
    lua_pop(L, 1); // Remove error message from stack
    lua_pop(L, 1); // Remove debug.traceback from stack

    // device()->pause();
    return 0;
}

LuaEnvironment::LuaEnvironment()
{
    _l = luaL_newstate();

    ASSERT(_l);
}

LuaEnvironment::~LuaEnvironment()
{
    lua_close(_l);
}

void LuaEnvironment::execute_string(const char* s)
{
    lua_pushcfunction(_l, error_handler);
    luaL_loadstring(_l, s);
    lua_pcall(_l, 0, 0, -2);
    lua_pop(_l, 1);
}

void LuaEnvironment::load_libraries()
{
    luaL_openlibs(_l);
}

} // namespace altertum
<commit_msg>fixed printf() warning<commit_after>/*
 * Copyright (c) 2015 Jonathan Howard
 * License: https://github.com/v3n/altertum/blob/master/LICENSE
 */

#include <cstring>

#include "macros.h"
#include "lua_environment.h"

namespace altertum
{

/* from taylor001/crown:lua_environment.cpp:50 */
// When an error occurs, logs the error message and pauses the engine.
static int error_handler(lua_State* L)
{
    lua_getfield(L, LUA_GLOBALSINDEX, "debug");
    if (!lua_istable(L, -1))
    {
        lua_pop(L, 1);
        return 0;
    }

    lua_getfield(L, -1, "traceback");
    if (!lua_isfunction(L, -1))
    {
        lua_pop(L, 2);
        return 0;
    }

    lua_pushvalue(L, 1); // Pass error message
    lua_pushinteger(L, 2);
    lua_call(L, 2, 1); // Call debug.traceback

    printf("%s", lua_tostring(L, -1)); // Print error message
    lua_pop(L, 1); // Remove error message from stack
    lua_pop(L, 1); // Remove debug.traceback from stack

    // device()->pause();
    return 0;
}

LuaEnvironment::LuaEnvironment()
{
    _l = luaL_newstate();

    ASSERT(_l);
}

LuaEnvironment::~LuaEnvironment()
{
    lua_close(_l);
}

void LuaEnvironment::execute_string(const char* s)
{
    lua_pushcfunction(_l, error_handler);
    luaL_loadstring(_l, s);
    lua_pcall(_l, 0, 0, -2);
    lua_pop(_l, 1);
}

void LuaEnvironment::load_libraries()
{
    luaL_openlibs(_l);
}

} // namespace altertum
<|endoftext|>


<commit_before><commit_msg>glXSwapBuffers is a no-op for pixmaps<commit_after><|endoftext|>


<commit_before>#include "../base/SRC_FIRST.hpp"
#include "tiler.hpp"
#include "../indexer/mercator.hpp"
#include "../indexer/scales.hpp"
#include "../base/logging.hpp"

Tiler::RectInfo::RectInfo()
  : m_drawScale(0), m_tileScale(0), m_x(0), m_y(0)
{}

Tiler::RectInfo::RectInfo(int drawScale, int tileScale, int x, int y)
  : m_drawScale(drawScale), m_tileScale(tileScale), m_x(x), m_y(y)
{
  initRect();
}

void Tiler::RectInfo::initRect()
{
  int k = 1 << m_tileScale;

  double rectSizeX = (MercatorBounds::maxX - MercatorBounds::minX) / k;
  double rectSizeY = (MercatorBounds::maxY - MercatorBounds::minY) / k;

  m_rect.setMinX(m_x * rectSizeX);
  m_rect.setMaxX((m_x + 1) * rectSizeX);
  m_rect.setMinY(m_y * rectSizeY);
  m_rect.setMaxY((m_y + 1) * rectSizeY);
}

LessByDistance::LessByDistance(m2::PointD const & pt)
  : m_pt(pt)
{
}

bool LessByDistance::operator()(Tiler::RectInfo const & l, Tiler::RectInfo const & r)
{
  return l.m_rect.Center().Length(m_pt) < r.m_rect.Center().Length(m_pt);
}

bool operator<(Tiler::RectInfo const & l, Tiler::RectInfo const & r)
{
  if (l.m_y != r.m_y)
    return l.m_y < r.m_y;
  if (l.m_x != r.m_x)
    return l.m_x < r.m_x;
  if (l.m_drawScale != r.m_drawScale)
    return l.m_drawScale < r.m_drawScale;
  if (l.m_tileScale != r.m_tileScale)
    return l.m_tileScale < r.m_tileScale;
  return false;
}

bool operator==(Tiler::RectInfo const & l, Tiler::RectInfo const & r)
{
  return (l.m_y == r.m_y)
      && (l.m_x == r.m_x)
      && (l.m_drawScale == r.m_drawScale)
      && (l.m_tileScale == r.m_tileScale);
}

int Tiler::drawScale(ScreenBase const & s) const
{
  ScreenBase tmpS = s;
  tmpS.Rotate(-tmpS.GetAngle());

  size_t tileSize = min(static_cast<size_t>(m_tileSize / 1.05), (size_t)512);

  m2::RectD glbRect;
  m2::PointD pxCenter = tmpS.PixelRect().Center();
  tmpS.PtoG(m2::RectD(pxCenter - m2::PointD(tileSize / 2, tileSize / 2),
                      pxCenter + m2::PointD(tileSize / 2, tileSize / 2)),
            glbRect);

  double glbRectSize = min(glbRect.SizeX(), glbRect.SizeY());

  int res = static_cast<int>(ceil(log((MercatorBounds::maxX - MercatorBounds::minX) / glbRectSize) / log(2.0)));

  return res > scales::GetUpperScale() ? scales::GetUpperScale() : res;
}

int Tiler::tileScale(ScreenBase const & s) const
{
  ScreenBase tmpS = s;
  tmpS.Rotate(-tmpS.GetAngle());

  /// slightly smaller than original to produce "antialiasing" effect using bilinear filtration.
  size_t tileSize = static_cast<size_t>(m_tileSize / 1.05);

  m2::RectD glbRect;
  m2::PointD pxCenter = tmpS.PixelRect().Center();
  tmpS.PtoG(m2::RectD(pxCenter - m2::PointD(tileSize / 2, tileSize / 2),
                      pxCenter + m2::PointD(tileSize / 2, tileSize / 2)),
            glbRect);

  double glbRectSize = min(glbRect.SizeX(), glbRect.SizeY());

  int res = static_cast<int>(ceil(log((MercatorBounds::maxX - MercatorBounds::minX) / glbRectSize) / log(2.0)));

  return res;
}

void Tiler::seed(ScreenBase const & screen, m2::PointD const & centerPt)
{
  if (screen != m_screen)
    ++m_sequenceID;

  m_screen = screen;

  m_drawScale = drawScale(screen);
  m_tileScale = tileScale(screen);

  double rectSizeX = (MercatorBounds::maxX - MercatorBounds::minX) / (1 << m_tileScale);
  double rectSizeY = (MercatorBounds::maxY - MercatorBounds::minY) / (1 << m_tileScale);

  m2::AnyRectD const globalRect = m_screen.GlobalRect();
  m2::RectD const clipRect = m_screen.ClipRect();

  int minTileX = static_cast<int>(floor(clipRect.minX() / rectSizeX));
  int maxTileX = static_cast<int>(ceil(clipRect.maxX() / rectSizeX));
  int minTileY = static_cast<int>(floor(clipRect.minY() / rectSizeY));
  int maxTileY = static_cast<int>(ceil(clipRect.maxY() / rectSizeY));

  /// clearing previous coverage
  m_coverage.clear();

  /// generating new coverage

  for (int tileY = minTileY; tileY < maxTileY; ++tileY)
    for (int tileX = minTileX; tileX < maxTileX; ++tileX)
    {
      m2::RectD tileRect(tileX * rectSizeX,
                         tileY * rectSizeY,
                         (tileX + 1) * rectSizeX,
                         (tileY + 1) * rectSizeY);

      if (globalRect.IsIntersect(m2::AnyRectD(tileRect)))
        m_coverage.push_back(RectInfo(m_drawScale, m_tileScale, tileX, tileY));
    }

  /// sorting coverage elements
  sort(m_coverage.begin(), m_coverage.end(), LessByDistance(centerPt));
}

Tiler::Tiler(size_t tileSize, size_t scaleEtalonSize)
  : m_sequenceID(0), m_tileSize(tileSize), m_scaleEtalonSize(scaleEtalonSize)
{}

void Tiler::visibleTiles(vector<RectInfo> & tiles)
{
  tiles = m_coverage;
}

size_t Tiler::sequenceID() const
{
  return m_sequenceID;
}
<commit_msg>computing screen coverage on enlarged screen rect. has an effect of "pre-caching".<commit_after>#include "../base/SRC_FIRST.hpp"
#include "tiler.hpp"
#include "../indexer/mercator.hpp"
#include "../indexer/scales.hpp"
#include "../base/logging.hpp"
#include "../platform/platform.hpp"

Tiler::RectInfo::RectInfo()
  : m_drawScale(0), m_tileScale(0), m_x(0), m_y(0)
{}

Tiler::RectInfo::RectInfo(int drawScale, int tileScale, int x, int y)
  : m_drawScale(drawScale), m_tileScale(tileScale), m_x(x), m_y(y)
{
  initRect();
}

void Tiler::RectInfo::initRect()
{
  int k = 1 << m_tileScale;

  double rectSizeX = (MercatorBounds::maxX - MercatorBounds::minX) / k;
  double rectSizeY = (MercatorBounds::maxY - MercatorBounds::minY) / k;

  m_rect.setMinX(m_x * rectSizeX);
  m_rect.setMaxX((m_x + 1) * rectSizeX);
  m_rect.setMinY(m_y * rectSizeY);
  m_rect.setMaxY((m_y + 1) * rectSizeY);
}

LessByDistance::LessByDistance(m2::PointD const & pt)
  : m_pt(pt)
{
}

bool LessByDistance::operator()(Tiler::RectInfo const & l, Tiler::RectInfo const & r)
{
  return l.m_rect.Center().Length(m_pt) < r.m_rect.Center().Length(m_pt);
}

bool operator<(Tiler::RectInfo const & l, Tiler::RectInfo const & r)
{
  if (l.m_y != r.m_y)
    return l.m_y < r.m_y;
  if (l.m_x != r.m_x)
    return l.m_x < r.m_x;
  if (l.m_drawScale != r.m_drawScale)
    return l.m_drawScale < r.m_drawScale;
  if (l.m_tileScale != r.m_tileScale)
    return l.m_tileScale < r.m_tileScale;
  return false;
}

bool operator==(Tiler::RectInfo const & l, Tiler::RectInfo const & r)
{
  return (l.m_y == r.m_y)
      && (l.m_x == r.m_x)
      && (l.m_drawScale == r.m_drawScale)
      && (l.m_tileScale == r.m_tileScale);
}

int Tiler::drawScale(ScreenBase const & s) const
{
  ScreenBase tmpS = s;
  tmpS.Rotate(-tmpS.GetAngle());

  size_t tileSize = min(static_cast<size_t>(m_tileSize / 1.05), (size_t)512);

  m2::RectD glbRect;
  m2::PointD pxCenter = tmpS.PixelRect().Center();
  tmpS.PtoG(m2::RectD(pxCenter - m2::PointD(tileSize / 2, tileSize / 2),
                      pxCenter + m2::PointD(tileSize / 2, tileSize / 2)),
            glbRect);

  double glbRectSize = min(glbRect.SizeX(), glbRect.SizeY());

  int res = static_cast<int>(ceil(log((MercatorBounds::maxX - MercatorBounds::minX) / glbRectSize) / log(2.0)));

  return res > scales::GetUpperScale() ? scales::GetUpperScale() : res;
}

int Tiler::tileScale(ScreenBase const & s) const
{
  ScreenBase tmpS = s;
  tmpS.Rotate(-tmpS.GetAngle());

  /// slightly smaller than original to produce "antialiasing" effect using bilinear filtration.
  size_t tileSize = static_cast<size_t>(m_tileSize / 1.05);

  m2::RectD glbRect;
  m2::PointD pxCenter = tmpS.PixelRect().Center();
  tmpS.PtoG(m2::RectD(pxCenter - m2::PointD(tileSize / 2, tileSize / 2),
                      pxCenter + m2::PointD(tileSize / 2, tileSize / 2)),
            glbRect);

  double glbRectSize = min(glbRect.SizeX(), glbRect.SizeY());

  int res = static_cast<int>(ceil(log((MercatorBounds::maxX - MercatorBounds::minX) / glbRectSize) / log(2.0)));

  return res;
}

void Tiler::seed(ScreenBase const & screen, m2::PointD const & centerPt)
{
  if (screen != m_screen)
    ++m_sequenceID;

  m_screen = screen;

  m_drawScale = drawScale(screen);
  m_tileScale = tileScale(screen);

  double rectSizeX = (MercatorBounds::maxX - MercatorBounds::minX) / (1 << m_tileScale);
  double rectSizeY = (MercatorBounds::maxY - MercatorBounds::minY) / (1 << m_tileScale);

  /// calculating coverage on the global rect, which corresponds to the
  /// pixel rect, which is square with size of nearest power-of-two

  m2::RectD pxRect = m_screen.PixelRect();

  int tileSize = GetPlatform().TileSize();

  int pxWidthInTiles = (pxRect.SizeX() + tileSize - 1) / tileSize;
  int pxHeightInTiles = (pxRect.SizeY() + tileSize - 1) / tileSize;

  pxWidthInTiles += 1;
  pxHeightInTiles += 1;

  double glbHalfSizeX = m_screen.PtoG(pxRect.Center() - m2::PointD(pxWidthInTiles * tileSize / 2, 0)).Length(m_screen.PtoG(pxRect.Center()));
  double glbHalfSizeY = m_screen.PtoG(pxRect.Center() - m2::PointD(0, pxHeightInTiles * tileSize / 2)).Length(m_screen.PtoG(pxRect.Center()));

  m2::AnyRectD globalRect(m_screen.PtoG(m_screen.PixelRect().Center()),
                          m_screen.GlobalRect().angle(),
                          m2::RectD(-glbHalfSizeX, -glbHalfSizeY, glbHalfSizeX, glbHalfSizeY));

  m2::RectD const clipRect = globalRect.GetGlobalRect();

  int minTileX = static_cast<int>(floor(clipRect.minX() / rectSizeX));
  int maxTileX = static_cast<int>(ceil(clipRect.maxX() / rectSizeX));
  int minTileY = static_cast<int>(floor(clipRect.minY() / rectSizeY));
  int maxTileY = static_cast<int>(ceil(clipRect.maxY() / rectSizeY));

  /// clearing previous coverage
  m_coverage.clear();

  /// generating new coverage

  for (int tileY = minTileY; tileY < maxTileY; ++tileY)
    for (int tileX = minTileX; tileX < maxTileX; ++tileX)
    {
      m2::RectD tileRect(tileX * rectSizeX,
                         tileY * rectSizeY,
                         (tileX + 1) * rectSizeX,
                         (tileY + 1) * rectSizeY);

      if (globalRect.IsIntersect(m2::AnyRectD(tileRect)))
        m_coverage.push_back(RectInfo(m_drawScale, m_tileScale, tileX, tileY));
    }

  /// sorting coverage elements
  sort(m_coverage.begin(), m_coverage.end(), LessByDistance(centerPt));
}

Tiler::Tiler(size_t tileSize, size_t scaleEtalonSize)
  : m_sequenceID(0), m_tileSize(tileSize), m_scaleEtalonSize(scaleEtalonSize)
{}

void Tiler::visibleTiles(vector<RectInfo> & tiles)
{
  tiles = m_coverage;
}

size_t Tiler::sequenceID() const
{
  return m_sequenceID;
}
<|endoftext|>


<commit_before>/**
 * @file main.cc
 *
 * Implementation of the AllkNN executable.  Allows some number of standard
 * options.
 *
 * @author Ryan Curtin
 */
#include <fastlib/fastlib.h>
#include "allknn.h"

#include <string>
#include <errno.h>

#include <armadillo>

using namespace std;

// Define our input parameters that this program will take.
PARAM_MODULE("allknn", "The all-k-nearest-neighbors computation.");
PARAM_STRING_REQ("reference_file", "CSV file containing the reference dataset.",
    "allknn");
PARAM_STRING("query_file", "CSV file containing query points (optional).",
    "allknn", "");
PARAM_STRING_REQ("output_file", "File to output CSV-formatted results into.",
    "allknn");
PARAM_INT("k", "Number of nearest neighbors to compute.", "allknn", 5);
PARAM_INT("leaf_size", "Leaf size for kd-tree calculation.", "allknn", 20);
PARAM_BOOL("single_mode", "If true, use single-tree mode (instead of "
    "dual-tree).", "allknn", false);
PARAM_BOOL("naive_mode", "If true, use naive computation (no trees).  This "
    "overrides single_mode.", "allknn", false);

using namespace mlpack;
using namespace mlpack::allknn;

int main(int argc, char *argv[]) {
  // Give IO the command line parameters the user passed in.
  IO::ParseCommandLine(argc, argv);

  std::string reference_file =
      IO::GetValue<std::string>("allknn/reference_file");
  std::string output_file = IO::GetValue<std::string>("allknn/output_file");

  arma::mat reference_data;

  bool single_mode = IO::GetValue<bool>("allknn/single_mode");
  bool naive_mode = IO::GetValue<bool>("allknn/naive_mode");
  index_t knn_options = (single_mode ? AllkNN::MODE_SINGLE : 0) |
      (naive_mode ? AllkNN::NAIVE : 0);

  arma::Col<index_t> neighbors;
  arma::vec distances;

  if (data::Load(reference_file.c_str(), reference_data) == SUCCESS_FAIL)
    IO::Fatal << "Reference file " << reference_file << "not found." << endl;

  IO::Info << "Loaded reference data from " << reference_file << endl;

  // Sanity check on k value: must be greater than 0, must be less than the
  // number of reference points.
  int k = IO::GetValue<int>("allknn/k");
  if ((k <= 0) || (k >= reference_data.n_cols)) {
    IO::Fatal << "Invalid k: " << k << "; must be greater than 0 and less ";
    IO::Fatal << "than the number of reference points (";
    IO::Fatal << reference_data.n_cols << ")." << endl;
  }

  // Sanity check on leaf size.
  if (IO::GetValue<int>("allknn/leaf_size") <= 0) {
    IO::Fatal << "Invalid leaf size: " << IO::GetValue<int>("allknn/leaf_size")
        << endl;
  }

  AllkNN* allknn = NULL;

  if (IO::CheckValue("query_file")) {
    std::string query_file = IO::GetValue<std::string>("query_file");
    arma::mat query_data;

    if (data::Load(query_file.c_str(), query_data) == SUCCESS_FAIL)
      IO::Fatal << "Query file " << query_file << " not found" << endl;

    IO::Info << "Query data loaded from " << query_file << endl;

    IO::Info << "Building query and reference trees..." << endl;
    allknn = new AllkNN(query_data, reference_data, (datanode*) NULL,
        knn_options);

  } else {
    IO::Info << "Building reference tree..." << endl;
    allknn = new AllkNN(reference_data, (datanode*) NULL, knn_options);
  }

  IO::Info << "Tree(s) built." << endl;

  IO::Info << "Computing " << k << " nearest neighbors..." << endl;
  allknn->ComputeNeighbors(neighbors, distances);

  IO::Info << "Neighbors computed." << endl;
  IO::Info << "Exporting results..." << endl;

  // Should be using data::Save or a related function instead of being written
  // by hand.
  FILE *fp = fopen(output_file.c_str(), "w");
  if (fp == NULL) {
    IO::Fatal << "Error while opening " << output_file << ": " <<
        strerror(errno) << endl;
  }

  for(index_t i = 0; i < neighbors.n_elem / k; i++) {
    for(int j = 0; j < k; j++) {
      fprintf(fp, "%"LI"d %"LI"d %lg\n", i, neighbors[i * k + j], distances[i * k + j]);
    }
  }
  fclose(fp);

  delete allknn;
}
<commit_msg>Change BOOL to FLAG<commit_after>/**
 * @file main.cc
 *
 * Implementation of the AllkNN executable.  Allows some number of standard
 * options.
 *
 * @author Ryan Curtin
 */
#include <fastlib/fastlib.h>
#include "allknn.h"

#include <string>
#include <errno.h>

#include <armadillo>

using namespace std;

// Define our input parameters that this program will take.
PARAM_MODULE("allknn", "The all-k-nearest-neighbors computation.");
PARAM_STRING_REQ("reference_file", "CSV file containing the reference dataset.",
    "allknn");
PARAM_STRING("query_file", "CSV file containing query points (optional).",
    "allknn", "");
PARAM_STRING_REQ("output_file", "File to output CSV-formatted results into.",
    "allknn");
PARAM_INT("k", "Number of nearest neighbors to compute.", "allknn", 5);
PARAM_INT("leaf_size", "Leaf size for kd-tree calculation.", "allknn", 20);
PARAM_FLAG("single_mode", "If true, use single-tree mode (instead of "
    "dual-tree).", "allknn", false);
PARAM_FLAG("naive_mode", "If true, use naive computation (no trees).  This "
    "overrides single_mode.", "allknn", false);

using namespace mlpack;
using namespace mlpack::allknn;

int main(int argc, char *argv[]) {
  // Give IO the command line parameters the user passed in.
  IO::ParseCommandLine(argc, argv);

  std::string reference_file =
      IO::GetValue<std::string>("allknn/reference_file");
  std::string output_file = IO::GetValue<std::string>("allknn/output_file");

  arma::mat reference_data;

  bool single_mode = IO::GetValue<bool>("allknn/single_mode");
  bool naive_mode = IO::GetValue<bool>("allknn/naive_mode");
  index_t knn_options = (single_mode ? AllkNN::MODE_SINGLE : 0) |
      (naive_mode ? AllkNN::NAIVE : 0);

  arma::Col<index_t> neighbors;
  arma::vec distances;

  if (data::Load(reference_file.c_str(), reference_data) == SUCCESS_FAIL)
    IO::Fatal << "Reference file " << reference_file << "not found." << endl;

  IO::Info << "Loaded reference data from " << reference_file << endl;

  // Sanity check on k value: must be greater than 0, must be less than the
  // number of reference points.
  int k = IO::GetValue<int>("allknn/k");
  if ((k <= 0) || (k >= reference_data.n_cols)) {
    IO::Fatal << "Invalid k: " << k << "; must be greater than 0 and less ";
    IO::Fatal << "than the number of reference points (";
    IO::Fatal << reference_data.n_cols << ")." << endl;
  }

  // Sanity check on leaf size.
  if (IO::GetValue<int>("allknn/leaf_size") <= 0) {
    IO::Fatal << "Invalid leaf size: " << IO::GetValue<int>("allknn/leaf_size")
        << endl;
  }

  AllkNN* allknn = NULL;

  if (IO::CheckValue("query_file")) {
    std::string query_file = IO::GetValue<std::string>("query_file");
    arma::mat query_data;

    if (data::Load(query_file.c_str(), query_data) == SUCCESS_FAIL)
      IO::Fatal << "Query file " << query_file << " not found" << endl;

    IO::Info << "Query data loaded from " << query_file << endl;

    IO::Info << "Building query and reference trees..." << endl;
    allknn = new AllkNN(query_data, reference_data, (datanode*) NULL,
        knn_options);

  } else {
    IO::Info << "Building reference tree..." << endl;
    allknn = new AllkNN(reference_data, (datanode*) NULL, knn_options);
  }

  IO::Info << "Tree(s) built." << endl;

  IO::Info << "Computing " << k << " nearest neighbors..." << endl;
  allknn->ComputeNeighbors(neighbors, distances);

  IO::Info << "Neighbors computed." << endl;
  IO::Info << "Exporting results..." << endl;

  // Should be using data::Save or a related function instead of being written
  // by hand.
  FILE *fp = fopen(output_file.c_str(), "w");
  if (fp == NULL) {
    IO::Fatal << "Error while opening " << output_file << ": " <<
        strerror(errno) << endl;
  }

  for(index_t i = 0; i < neighbors.n_elem / k; i++) {
    for(int j = 0; j < k; j++) {
      fprintf(fp, "%"LI"d %"LI"d %lg\n", i, neighbors[i * k + j], distances[i * k + j]);
    }
  }
  fclose(fp);

  delete allknn;
}
<|endoftext|>