<commit_before>#include "../gtest.h"
#include "common.hpp"

#include <iostream>
#include <ostream>
// (Pending abstraction of threading interface)
#include <arbor/version.hpp>

#include "threading/threading.hpp"
#include "threading/enumerable_thread_specific.hpp"

using namespace arb::threading::impl;
using namespace arb::threading;
using namespace arb;
namespace {

std::atomic<int> nmove{0};
std::atomic<int> ncopy{0};

void reset() {
    nmove = 0;
    ncopy = 0;
}

struct ftor {

    ftor() {}

    ftor(ftor&& other) {
        ++nmove;
    }

    ftor(const ftor& other) {
        ++ncopy;
    }

    void operator()() const {}
};

struct ftor_wait {
    int duration_us = 100;

    ftor_wait() {}
    ftor_wait(int us): duration_us(us) {}

    void operator()() const {
        auto duration = std::chrono::microseconds(duration_us);
        std::this_thread::sleep_for(duration);
    }
};

struct ftor_parallel_wait {

    ftor_parallel_wait(task_system* ts): ts{ts} {}

    void operator()() const {
        auto nthreads = ts->get_num_threads();
        auto duration = std::chrono::microseconds(100);
        parallel_for::apply(0, nthreads, ts, [=](int i){ std::this_thread::sleep_for(duration);});
    }

    task_system* ts;
};

}

TEST(task_system, test_copy) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);

        ftor f;
        ts.async(f, 0);

        // Copy into new ftor and move ftor into a task (std::function<void()>)
        EXPECT_EQ(1, nmove);
        EXPECT_EQ(1, ncopy);
        reset();
    }
}

TEST(task_system, test_move) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);

        ftor f;
        ts.async(std::move(f), 0);

        // Move into new ftor and move ftor into a task (std::function<void()>)
        EXPECT_LE(nmove, 2);
        EXPECT_LE(ncopy, 1);
        reset();
    }
}

TEST(notification_queue, test_copy) {
    notification_queue q;

    ftor f;
    q.push({task(f), 0});

    // Copy into new ftor and move ftor into a task (std::function<void()>)
    EXPECT_EQ(1, nmove);
    EXPECT_EQ(1, ncopy);
    reset();
}

TEST(notification_queue, test_move) {
    notification_queue q;

    ftor f;

    // Move into new ftor and move ftor into a task (std::function<void()>)
    q.push({task(std::move(f)), 1});
    EXPECT_LE(nmove, 2);
    EXPECT_LE(ncopy, 1);
    reset();
}

TEST(task_group, test_copy) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);
        task_group g(&ts);

        ftor f;
        g.run(f);
        g.wait();

        // Copy into "wrap" and move wrap into a task (std::function<void()>)
        EXPECT_EQ(1, nmove);
        EXPECT_EQ(1, ncopy);
        reset();
    }
}

TEST(task_group, test_move) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);
        task_group g(&ts);

        ftor f;
        g.run(std::move(f));
        g.wait();

        // Move into wrap and move wrap into a task (std::function<void()>)
        EXPECT_LE(nmove, 2);
        EXPECT_LE(ncopy, 1);
        reset();
    }
}

TEST(task_group, individual_tasks) {
    // Simple check for deadlock
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);
        task_group g(&ts);

        ftor_wait f;
        for (int i = 0; i < 32 * nthreads; i++) {
            g.run(f);
        }
        g.wait();
    }
}

TEST(task_group, parallel_for_sleep) {
    // Simple check for deadlock for nested parallelism
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);
        task_group g(&ts);

        ftor_parallel_wait f(&ts);
        for (int i = 0; i < nthreads; i++) {
            g.run(f);
        }
        g.wait();
    }
}

TEST(task_group, parallel_for) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);
        for (int n = 0; n < 10000; n = !n ? 1 : 2 * n) {
            std::vector<int> v(n, -1);
            parallel_for::apply(0, n, &ts, [&](int i) { v[i] = i; });
            for (int i = 0; i < n; i++) {
                EXPECT_EQ(i, v[i]);
            }
        }
    }
}

TEST(task_group, manual_nested_parallel_for) {
    // Check for deadlock or stack overflow
    const int ntasks = 100000;
    {
        for (int nthreads = 1; nthreads < 20; nthreads *= 4) {
            std::vector<int> v(ntasks);
            task_system ts(nthreads);

            auto nested = [&](int j) {
              task_group g1(&ts);
              g1.run([&](){v[j] = j;});
              g1.wait();
            };

            task_group g0(&ts);
            for (int i = 0; i < ntasks; i++) {
                g0.run([=](){nested(i);});
            }
            g0.wait();
            for (int i = 0; i < ntasks; i++) {
                EXPECT_EQ(i, v[i]);
            }
        }
    }
    {
        for (int nthreads = 1; nthreads < 20; nthreads *= 4) {
            std::vector<int> v(ntasks);
            task_system ts(nthreads);

            auto double_nested = [&](int i) {
              task_group g2(&ts);
              g2.run([&](){v[i] = i;});
              g2.wait();
            };

            auto nested = [&](int i) {
              task_group g1(&ts);
              g1.run([=](){double_nested(i);});
              g1.wait();
            };

            task_group g0(&ts);
            for (int i = 0; i < ntasks; i++) {
                g0.run([=](){nested(i);});
            }
            g0.wait();
            for (int i = 0; i < ntasks; i++) {
                EXPECT_EQ(i, v[i]);
            }
        }
    }
}

TEST(task_group, nested_parallel_for) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);
        for (int m = 1; m < 512; m *= 2) {
            for (int n = 0; n < 1000; n = !n ? 1 : 2 * n) {
                std::vector<std::vector<int>> v(n, std::vector<int>(m, -1));
                parallel_for::apply(0, n, &ts, [&](int i) {
                    auto& w = v[i];
                    parallel_for::apply(0, m, &ts, [&](int j) { w[j] = i + j; });
                });
                for (int i = 0; i < n; i++) {
                    for (int j = 0; j < m; j++) {
                        EXPECT_EQ(i + j, v[i][j]);
                    }
                }
            }
        }
    }
}

TEST(task_group, multi_nested_parallel_for) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);
        for (int m = 1; m < 512; m *= 2) {
            for (int n = 0; n < 128; n = !n ? 1 : 2 * n) {
                for (int p = 0; p < 16; p = !p ? 1 : 2 * p) {
                    std::vector<std::vector<std::vector<int>>> v(n, std::vector<std::vector<int>>(m, std::vector<int>(p, -1)));
                    parallel_for::apply(0, n, &ts, [&](int i) {
                        auto& w = v[i];
                        parallel_for::apply(0, m, &ts, [&](int j) {
                            auto& u = w[j];
                            parallel_for::apply(0, p, &ts, [&](int k) {
                                u[k] = i + j + k;
                            });
                        });
                    });
                    for (int i = 0; i < n; i++) {
                        for (int j = 0; j < m; j++) {
                            for (int k = 0; k < p; k++) {
                                EXPECT_EQ(i + j + k, v[i][j][k]);
                            }
                        }
                    }
                }
            }
        }
    }
}

TEST(task_group, nested_parallel_for_unbalanced) {
    // Top level parallel for has many more tasks than lower level
    const int ntasks = 100000;
    {
        // Default batching
        for (int nthreads = 1; nthreads < 20; nthreads *= 4) {
            task_system ts(nthreads);
            std::vector<int> v(ntasks);
            parallel_for::apply(0, ntasks, &ts, [&](int i) {
                parallel_for::apply(0, 1, &ts, [&](int j) { v[i] = i; });
            });
            for (int i = 0; i < ntasks; i++) {
                EXPECT_EQ(i, v[i]);
            }
        }
        // 128 tasks per batch
        const int batch_size = 128;
        for (int nthreads = 1; nthreads < 20; nthreads *= 4) {
            task_system ts(nthreads);
            std::vector<int> v(ntasks);
            parallel_for::apply(0, ntasks, batch_size, &ts, [&](int i) {
              parallel_for::apply(0, 1, batch_size, &ts, [&](int j) { v[i] = i; });
            });
            for (int i = 0; i < ntasks; i++) {
                EXPECT_EQ(i, v[i]);
            }
        }
    }
    // lower level parallel for has many more tasks than top level
    {
        // Default batching
        for (int nthreads = 1; nthreads < 20; nthreads *= 4) {
            task_system ts(nthreads);
            std::vector<int> v(ntasks);
            parallel_for::apply(0, 1, &ts, [&](int i) {
                parallel_for::apply(0, ntasks, &ts, [&](int j) { v[j] = j; });
            });
            for (int i = 0; i < ntasks; i++) {
                EXPECT_EQ(i, v[i]);
            }
        }
        // 128 tasks per batch
        const int batch_size = 128;
        for (int nthreads = 1; nthreads < 20; nthreads *= 4) {
            task_system ts(nthreads);
            std::vector<int> v(ntasks);
            parallel_for::apply(0, 1, batch_size, &ts, [&](int i) {
                parallel_for::apply(0, ntasks, batch_size, &ts, [&](int j) { v[j] = j; });
            });
            for (int i = 0; i < ntasks; i++) {
                EXPECT_EQ(i, v[i]);
            }
        }
    }
}

TEST(task_group, multi_nested_parallel_for_unbalanced) {
    // Top level parallel for has many more tasks than lower level
    const int ntasks = 100000;
    for (int nthreads = 1; nthreads < 20; nthreads*=4) {
        task_system ts(nthreads);
        std::vector<int> v(ntasks);
        parallel_for::apply(0, ntasks, &ts, [&](int i) {
            parallel_for::apply(0, 1, &ts, [&](int j) {
                parallel_for::apply(0, 1, &ts, [&](int k) {
                    v[i] = i;
                });
            });
        });
        for (int i = 0; i < ntasks; i++) {
            EXPECT_EQ(i, v[i]);
        }
    }
    // lower level parallel for has many more tasks than top level
    for (int nthreads = 1; nthreads < 20; nthreads*=4) {
        task_system ts(nthreads);
        std::vector<int> v(ntasks);
        parallel_for::apply(0, 1, &ts, [&](int i) {
            parallel_for::apply(0, 1, &ts, [&](int j) {
                parallel_for::apply(0, ntasks, &ts, [&](int k) {
                    v[k] = k;
                });
            });
        });
        for (int i = 0; i < ntasks; i++) {
            EXPECT_EQ(i, v[i]);
        }
    }
}

TEST(enumerable_thread_specific, test) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system_handle ts = task_system_handle(new task_system(nthreads));
        enumerable_thread_specific<int> buffers(ts);
        task_group g(ts.get());

        for (int i = 0; i < 100000; i++) {
            g.run([&]() {
                auto& buf = buffers.local();
                buf++;
            });
        }
        g.wait();

        int sum = 0;
        for (auto b: buffers) {
            sum += b;
        }

        EXPECT_EQ(100000, sum);
    }
}
<commit_msg>remove checks for move ctor when it could be elided (#1899)<commit_after>#include "../gtest.h"
#include "common.hpp"

#include <iostream>
#include <ostream>
// (Pending abstraction of threading interface)
#include <arbor/version.hpp>

#include "threading/threading.hpp"
#include "threading/enumerable_thread_specific.hpp"

using namespace arb::threading::impl;
using namespace arb::threading;
using namespace arb;
namespace {

std::atomic<int> nmove{0};
std::atomic<int> ncopy{0};

void reset() {
    nmove = 0;
    ncopy = 0;
}

struct ftor {

    ftor() {}

    ftor(ftor&& other) {
        ++nmove;
    }

    ftor(const ftor& other) {
        ++ncopy;
    }

    void operator()() const {}
};

struct ftor_wait {
    int duration_us = 100;

    ftor_wait() {}
    ftor_wait(int us): duration_us(us) {}

    void operator()() const {
        auto duration = std::chrono::microseconds(duration_us);
        std::this_thread::sleep_for(duration);
    }
};

struct ftor_parallel_wait {

    ftor_parallel_wait(task_system* ts): ts{ts} {}

    void operator()() const {
        auto nthreads = ts->get_num_threads();
        auto duration = std::chrono::microseconds(100);
        parallel_for::apply(0, nthreads, ts, [=](int i){ std::this_thread::sleep_for(duration);});
    }

    task_system* ts;
};

}

TEST(task_system, test_copy) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);

        ftor f;
        ts.async(f, 0);

        // Copy into new ftor and move ftor into a task (std::function<void()>)
        // move ctor is elided with some compilers
        //EXPECT_EQ(1, nmove);
        EXPECT_EQ(1, ncopy);
        reset();
    }
}

TEST(task_system, test_move) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);

        ftor f;
        ts.async(std::move(f), 0);

        // Move into new ftor and move ftor into a task (std::function<void()>)
        EXPECT_LE(nmove, 2);
        EXPECT_LE(ncopy, 1);
        reset();
    }
}

TEST(notification_queue, test_copy) {
    notification_queue q;

    ftor f;
    q.push({task(f), 0});

    // Copy into new ftor and move ftor into a task (std::function<void()>)
    // move ctor is elided with some compilers
    //EXPECT_EQ(1, nmove);
    EXPECT_EQ(1, ncopy);
    reset();
}

TEST(notification_queue, test_move) {
    notification_queue q;

    ftor f;

    // Move into new ftor and move ftor into a task (std::function<void()>)
    q.push({task(std::move(f)), 1});
    EXPECT_LE(nmove, 2);
    EXPECT_LE(ncopy, 1);
    reset();
}

TEST(task_group, test_copy) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);
        task_group g(&ts);

        ftor f;
        g.run(f);
        g.wait();

        // Copy into "wrap" and move wrap into a task (std::function<void()>)
        EXPECT_EQ(1, nmove);
        EXPECT_EQ(1, ncopy);
        reset();
    }
}

TEST(task_group, test_move) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);
        task_group g(&ts);

        ftor f;
        g.run(std::move(f));
        g.wait();

        // Move into wrap and move wrap into a task (std::function<void()>)
        EXPECT_LE(nmove, 2);
        EXPECT_LE(ncopy, 1);
        reset();
    }
}

TEST(task_group, individual_tasks) {
    // Simple check for deadlock
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);
        task_group g(&ts);

        ftor_wait f;
        for (int i = 0; i < 32 * nthreads; i++) {
            g.run(f);
        }
        g.wait();
    }
}

TEST(task_group, parallel_for_sleep) {
    // Simple check for deadlock for nested parallelism
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);
        task_group g(&ts);

        ftor_parallel_wait f(&ts);
        for (int i = 0; i < nthreads; i++) {
            g.run(f);
        }
        g.wait();
    }
}

TEST(task_group, parallel_for) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);
        for (int n = 0; n < 10000; n = !n ? 1 : 2 * n) {
            std::vector<int> v(n, -1);
            parallel_for::apply(0, n, &ts, [&](int i) { v[i] = i; });
            for (int i = 0; i < n; i++) {
                EXPECT_EQ(i, v[i]);
            }
        }
    }
}

TEST(task_group, manual_nested_parallel_for) {
    // Check for deadlock or stack overflow
    const int ntasks = 100000;
    {
        for (int nthreads = 1; nthreads < 20; nthreads *= 4) {
            std::vector<int> v(ntasks);
            task_system ts(nthreads);

            auto nested = [&](int j) {
              task_group g1(&ts);
              g1.run([&](){v[j] = j;});
              g1.wait();
            };

            task_group g0(&ts);
            for (int i = 0; i < ntasks; i++) {
                g0.run([=](){nested(i);});
            }
            g0.wait();
            for (int i = 0; i < ntasks; i++) {
                EXPECT_EQ(i, v[i]);
            }
        }
    }
    {
        for (int nthreads = 1; nthreads < 20; nthreads *= 4) {
            std::vector<int> v(ntasks);
            task_system ts(nthreads);

            auto double_nested = [&](int i) {
              task_group g2(&ts);
              g2.run([&](){v[i] = i;});
              g2.wait();
            };

            auto nested = [&](int i) {
              task_group g1(&ts);
              g1.run([=](){double_nested(i);});
              g1.wait();
            };

            task_group g0(&ts);
            for (int i = 0; i < ntasks; i++) {
                g0.run([=](){nested(i);});
            }
            g0.wait();
            for (int i = 0; i < ntasks; i++) {
                EXPECT_EQ(i, v[i]);
            }
        }
    }
}

TEST(task_group, nested_parallel_for) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);
        for (int m = 1; m < 512; m *= 2) {
            for (int n = 0; n < 1000; n = !n ? 1 : 2 * n) {
                std::vector<std::vector<int>> v(n, std::vector<int>(m, -1));
                parallel_for::apply(0, n, &ts, [&](int i) {
                    auto& w = v[i];
                    parallel_for::apply(0, m, &ts, [&](int j) { w[j] = i + j; });
                });
                for (int i = 0; i < n; i++) {
                    for (int j = 0; j < m; j++) {
                        EXPECT_EQ(i + j, v[i][j]);
                    }
                }
            }
        }
    }
}

TEST(task_group, multi_nested_parallel_for) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system ts(nthreads);
        for (int m = 1; m < 512; m *= 2) {
            for (int n = 0; n < 128; n = !n ? 1 : 2 * n) {
                for (int p = 0; p < 16; p = !p ? 1 : 2 * p) {
                    std::vector<std::vector<std::vector<int>>> v(n, std::vector<std::vector<int>>(m, std::vector<int>(p, -1)));
                    parallel_for::apply(0, n, &ts, [&](int i) {
                        auto& w = v[i];
                        parallel_for::apply(0, m, &ts, [&](int j) {
                            auto& u = w[j];
                            parallel_for::apply(0, p, &ts, [&](int k) {
                                u[k] = i + j + k;
                            });
                        });
                    });
                    for (int i = 0; i < n; i++) {
                        for (int j = 0; j < m; j++) {
                            for (int k = 0; k < p; k++) {
                                EXPECT_EQ(i + j + k, v[i][j][k]);
                            }
                        }
                    }
                }
            }
        }
    }
}

TEST(task_group, nested_parallel_for_unbalanced) {
    // Top level parallel for has many more tasks than lower level
    const int ntasks = 100000;
    {
        // Default batching
        for (int nthreads = 1; nthreads < 20; nthreads *= 4) {
            task_system ts(nthreads);
            std::vector<int> v(ntasks);
            parallel_for::apply(0, ntasks, &ts, [&](int i) {
                parallel_for::apply(0, 1, &ts, [&](int j) { v[i] = i; });
            });
            for (int i = 0; i < ntasks; i++) {
                EXPECT_EQ(i, v[i]);
            }
        }
        // 128 tasks per batch
        const int batch_size = 128;
        for (int nthreads = 1; nthreads < 20; nthreads *= 4) {
            task_system ts(nthreads);
            std::vector<int> v(ntasks);
            parallel_for::apply(0, ntasks, batch_size, &ts, [&](int i) {
              parallel_for::apply(0, 1, batch_size, &ts, [&](int j) { v[i] = i; });
            });
            for (int i = 0; i < ntasks; i++) {
                EXPECT_EQ(i, v[i]);
            }
        }
    }
    // lower level parallel for has many more tasks than top level
    {
        // Default batching
        for (int nthreads = 1; nthreads < 20; nthreads *= 4) {
            task_system ts(nthreads);
            std::vector<int> v(ntasks);
            parallel_for::apply(0, 1, &ts, [&](int i) {
                parallel_for::apply(0, ntasks, &ts, [&](int j) { v[j] = j; });
            });
            for (int i = 0; i < ntasks; i++) {
                EXPECT_EQ(i, v[i]);
            }
        }
        // 128 tasks per batch
        const int batch_size = 128;
        for (int nthreads = 1; nthreads < 20; nthreads *= 4) {
            task_system ts(nthreads);
            std::vector<int> v(ntasks);
            parallel_for::apply(0, 1, batch_size, &ts, [&](int i) {
                parallel_for::apply(0, ntasks, batch_size, &ts, [&](int j) { v[j] = j; });
            });
            for (int i = 0; i < ntasks; i++) {
                EXPECT_EQ(i, v[i]);
            }
        }
    }
}

TEST(task_group, multi_nested_parallel_for_unbalanced) {
    // Top level parallel for has many more tasks than lower level
    const int ntasks = 100000;
    for (int nthreads = 1; nthreads < 20; nthreads*=4) {
        task_system ts(nthreads);
        std::vector<int> v(ntasks);
        parallel_for::apply(0, ntasks, &ts, [&](int i) {
            parallel_for::apply(0, 1, &ts, [&](int j) {
                parallel_for::apply(0, 1, &ts, [&](int k) {
                    v[i] = i;
                });
            });
        });
        for (int i = 0; i < ntasks; i++) {
            EXPECT_EQ(i, v[i]);
        }
    }
    // lower level parallel for has many more tasks than top level
    for (int nthreads = 1; nthreads < 20; nthreads*=4) {
        task_system ts(nthreads);
        std::vector<int> v(ntasks);
        parallel_for::apply(0, 1, &ts, [&](int i) {
            parallel_for::apply(0, 1, &ts, [&](int j) {
                parallel_for::apply(0, ntasks, &ts, [&](int k) {
                    v[k] = k;
                });
            });
        });
        for (int i = 0; i < ntasks; i++) {
            EXPECT_EQ(i, v[i]);
        }
    }
}

TEST(enumerable_thread_specific, test) {
    for (int nthreads = 1; nthreads < 20; nthreads*=2) {
        task_system_handle ts = task_system_handle(new task_system(nthreads));
        enumerable_thread_specific<int> buffers(ts);
        task_group g(ts.get());

        for (int i = 0; i < 100000; i++) {
            g.run([&]() {
                auto& buf = buffers.local();
                buf++;
            });
        }
        g.wait();

        int sum = 0;
        for (auto b: buffers) {
            sum += b;
        }

        EXPECT_EQ(100000, sum);
    }
}
<|endoftext|>


<commit_before>#include <memory>
#include <cassert>

#include "eval.hh"
#include "util.hh"
#include "keyword.hh"
#include "symbol.hh"
#include "cons.hh"
#include "function.hh"
#include "printer.hh"

using namespace std;

enum class VM_op : int{
  nop = 0,
    if_
};

namespace {

void funcall(const Function* fun, Lisp_ptr args){
  const auto& argi = fun->arg_info();
  const auto env = fun->closure();

  const auto set_arg = [&](Symbol* sym, Lisp_ptr p){
    if(env){
      VM.local_set(sym, p);
    }else{
      VM.stack().push(p);
    }
  };

  Lisp_ptr arg_name = argi.head;

  VM.return_value() = {};

  if(env){
    VM.enter_frame(push_frame(env));
  }

  // push args
  int argc = 0;

  if(!do_list(args,
              [&](Cons* cell) -> bool{
                assert(argc <= argi.required_args);

                if(argc == argi.required_args)
                  return false;

                VM.code().push(cell->car());
                eval();
                auto evaled = VM.return_value();
                if(!evaled){
                  fprintf(stderr, "eval error: evaluating func's arg failed!!\n");
                  return false;
                }

                auto arg_name_cell = arg_name.get<Cons*>();
                set_arg(arg_name_cell->car().get<Symbol*>(), evaled);
                arg_name = arg_name_cell->cdr();
                ++argc;

                return true;
              },
              [&](Lisp_ptr dot_cdr) -> bool{
                assert(argc <= argi.required_args);

                if(argc < argi.required_args){
                  fprintf(stderr, "eval error: internal argument counter mismatched!! (read %d args)\n",
                          argc);
                  return false;
                }

                if(argi.variadic){
                  set_arg(arg_name.get<Symbol*>(), dot_cdr);
                  ++argc;
                  return true;
                }else{
                  if(!nullp(dot_cdr)){
                    fprintf(stderr, "funcall error: argcount mismatch! (more than required %d)\n",
                            argi.required_args);
                    return false;
                  }

                  return true;
                }
              })){
    goto end;
  }

  // real call
  switch(fun->type()){
  case Function::Type::interpreted:
    do_list(fun->get<Lisp_ptr>(),
            [&](Cons* cell) -> bool {
              VM.code().push(cell->car());
              eval();
              return true;
            },
            [&](Lisp_ptr last_cdr){
              if(!nullp(last_cdr)){
                fprintf(stderr, "eval error: body has dot list!\n");
                VM.return_value() = {};
              }
            });
    break;
  case Function::Type::native:
    fun->get<Function::NativeFunc>()();
    if(!VM.return_value())
      fprintf(stderr, "eval error: native func returned undef!\n");
    break;
  default:
    UNEXP_DEFAULT();
  }

 end:
  if(env){
    VM.leave_frame();
  }else{
    // cleaned by native func
  }
}

void eval_lambda(const Cons* rest){
  VM.return_value() = {};

  auto args = rest->car();
  if(rest->cdr().tag() != Ptr_tag::cons){
    fprintf(stderr, "eval error: lambda has invalid body!\n");
    return;
  }

  auto arg_info = parse_func_arg(args);
  if(!arg_info){
    fprintf(stderr, "eval error: lambda has invalid args!\n");
    return;
  }

  auto code = rest->cdr();
  if(!code.get<Cons*>()){
    fprintf(stderr, "eval error: lambda has no body!\n");
    return;
  }

  auto fun = new Function(code, arg_info, VM.frame());

  VM.return_value() = Lisp_ptr{fun};
  return;
}

void eval_if(Cons* rest){
  VM.return_value() = {};

  // extracting
  Lisp_ptr test, conseq, alt;

  int len =
  bind_cons_list(Lisp_ptr(rest),
                 [&](Cons* c){
                   test = c->car();
                 },
                 [&](Cons* c){
                   conseq = c->car();
                 },
                 [&](Cons* c){
                   alt = c->car();
                 });

  switch(len){
  case 1:
    fprintf(stderr, "eval error: informal if expr! (no test expr)\n");
    return;
  case 2: case 3: // successed
    break;
  default:
    fprintf(stderr, "eval error: informal if expr! (more than %d exprs)\n", len);
    return;
  }

  // evaluating
  VM.code().push(Lisp_ptr(VM_op::if_));
  VM.code().push(test);

  VM.stack().push(alt);
  VM.stack().push(conseq);
}

void vm_op_if(){
  auto test_result = VM.return_value();

  if(test_result.get<bool>()){
    VM.code().push(VM.stack().top());
    VM.stack().pop();
    VM.stack().pop();
  }else{
    VM.stack().pop();
    VM.code().push(VM.stack().top());
    VM.stack().pop();
  }
}

void eval_set(Cons* rest){
  VM.return_value() = {};

  // extracting
  Symbol* var = nullptr;
  Lisp_ptr val;

  int len =
    bind_cons_list(Lisp_ptr(rest),
                   [&](Cons* c){
                     var = c->car().get<Symbol*>();
                   },
                   [&](Cons* c){
                     val = c->car();
                   });

  if(!var){
    fprintf(stderr, "eval error: set!'s first element is not symbol!\n");
    return;
  }

  if(var->to_keyword() != Keyword::not_keyword){
    fprintf(stderr, "eval error: set!'s first element is Keyword (%s)!\n",
            var->name().c_str());
    return;
  }

  if(!val){
    fprintf(stderr, "eval error: no value is supplied for set!\n");
    return;
  }

  if(len > 2){
    fprintf(stderr, "eval error: informal set! expr! (more than %d exprs)\n", len);
    return;
  }

  // evaluating
  VM.code().push(val);
  eval();
  VM.set(var, VM.return_value());
  return;
}

void eval_define(const Cons* rest){
  static constexpr auto test_varname = [](Symbol* var) -> bool{
    if(!var){
      fprintf(stderr, "eval error: defined variable name is not a symbol!\n");
      return false;
    }

    if(var->to_keyword() != Keyword::not_keyword){
      fprintf(stderr, "eval error: define's first element is Keyword (%s)!\n",
              var->name().c_str());
      return false;
    }

    return true;
  };

  Symbol* var = nullptr;
  Lisp_ptr value;
  unique_ptr<Function> func_value;

  VM.return_value() = {};

  // extracting
  auto first = rest->car();

  switch(first.tag()){
  case Ptr_tag::symbol: {
    var = first.get<Symbol*>();
    if(!test_varname(var))
      return;

    auto val_l = rest->cdr().get<Cons*>();
    if(!val_l){
      fprintf(stderr, "eval error: definition has empty expr!\n");
      return;
    }
    if(val_l->cdr().tag() != Ptr_tag::cons
       || val_l->cdr().get<Cons*>()){
      fprintf(stderr, "eval error: definition has extra expr!\n");
      return;
    }

    VM.code().push(val_l->car());
    eval();
    value = VM.return_value();
    break;
  }

  case Ptr_tag::cons: {
    auto lis = first.get<Cons*>();
    if(!lis){
      fprintf(stderr, "eval error: defined variable is not found!\n");
      return;
    }

    var = lis->car().get<Symbol*>();
    if(!test_varname(var))
      return;

    const auto& arg_info = parse_func_arg(lis->cdr());
    if(!arg_info){
      fprintf(stderr, "eval error: defined function argument is informal!\n");
      return;
    }

    auto code = rest->cdr();
    if(!code.get<Cons*>()){
      fprintf(stderr, "eval error: definition has empty body!\n");
      return;
    }

    func_value = unique_ptr<Function>(new Function(code, arg_info, VM.frame()));
    value = Lisp_ptr(func_value.get());
    break;
  }

  default:
    fprintf(stderr, "eval error: informal define syntax!\n");
    return;
  }

  // assignment
  VM.set(var, value);
  func_value.release();

  VM.return_value() = value;
  return;
}

} // namespace

void eval(){
  while(!VM.code().empty()){
    auto p = VM.code().top();
    VM.code().pop();

    switch(p.tag()){
    case Ptr_tag::symbol: {
      auto sym = p.get<Symbol*>();
      if(sym->to_keyword() != Keyword::not_keyword){
        fprintf(stderr, "eval error: symbol '%s' is keyword!!\n", sym->name().c_str());
        VM.return_value() = {};
        break;
      }

      VM.return_value() = VM.find(sym);
      break;
    }

    case Ptr_tag::cons: {
      auto c = p.get<Cons*>();
      auto first = c->car();

      // special operator?
      if(first.tag() == Ptr_tag::symbol){
        auto sym = first.get<Symbol*>();
        auto k = sym->to_keyword();

        if(k != Keyword::not_keyword){
          Cons* r = c->cdr().get<Cons*>();
          if(!r){
            fprintf(stderr, "eval error: expresssion (<KEYWORD>%s) is informal!\n",
                    (c->cdr().tag() == Ptr_tag::cons) ? "" : ". #");
            VM.return_value() = {};
            break;
          }

          switch(k){
          case Keyword::quote:  VM.return_value() = r->car(); break;
          case Keyword::lambda: eval_lambda(r); break;
          case Keyword::if_:    eval_if(r); break;
          case Keyword::set_:   eval_set(r); break;
          case Keyword::define: eval_define(r); break;

          case Keyword::cond:
          case Keyword::case_:
          case Keyword::and_:
          case Keyword::or_:
          case Keyword::let:
          case Keyword::let_star:
          case Keyword::letrec:
          case Keyword::begin:
          case Keyword::do_:
          case Keyword::delay:
          case Keyword::quasiquote:
            fprintf(stderr, "eval error: '%s' is under development...\n",
                    sym->name().c_str());
            VM.return_value() = {};
            break;

          case Keyword::else_:
          case Keyword::r_arrow:
          case Keyword::unquote:
          case Keyword::unquote_splicing:
            fprintf(stderr, "eval error: '%s' cannot be used as operator!!\n",
                    sym->name().c_str());
            VM.return_value() = {};
            break;

          default:
            UNEXP_DEFAULT();
          }
          break;
        }else{
          // macro call?
          //  try to find macro-function from symbol
          //    found -> macro expansion
          //    not found -> goto function calling
          ;
        }
      }

      // procedure call?
      VM.code().push(first);
      eval();
      auto proc = VM.return_value();
      if(proc.tag() != Ptr_tag::function){
        fprintf(stderr, "eval error: (# # ...)'s first element is not procedure (%s)\n",
                stringify(proc.tag()));
        VM.return_value() = {};
        break;
      }

      funcall(proc.get<Function*>(), c->cdr());
      break;
    }

    case Ptr_tag::vm_op:
      switch(p.get<VM_op>()){
      case VM_op::nop:
        break;
      case VM_op::if_:
        vm_op_if();
        break;
      default:
        UNEXP_DEFAULT();
      }
      break;

    default: // almost self-evaluating
      VM.return_value() = p;
      break;
    }
  }
}
<commit_msg>nonrecursive set<commit_after>#include <memory>
#include <cassert>

#include "eval.hh"
#include "util.hh"
#include "keyword.hh"
#include "symbol.hh"
#include "cons.hh"
#include "function.hh"
#include "printer.hh"

using namespace std;

enum class VM_op : int{
  nop = 0,
    if_,
    set_
};

namespace {

void funcall(const Function* fun, Lisp_ptr args){
  const auto& argi = fun->arg_info();
  const auto env = fun->closure();

  const auto set_arg = [&](Symbol* sym, Lisp_ptr p){
    if(env){
      VM.local_set(sym, p);
    }else{
      VM.stack().push(p);
    }
  };

  Lisp_ptr arg_name = argi.head;

  VM.return_value() = {};

  if(env){
    VM.enter_frame(push_frame(env));
  }

  // push args
  int argc = 0;

  if(!do_list(args,
              [&](Cons* cell) -> bool{
                assert(argc <= argi.required_args);

                if(argc == argi.required_args)
                  return false;

                VM.code().push(cell->car());
                eval();
                auto evaled = VM.return_value();
                if(!evaled){
                  fprintf(stderr, "eval error: evaluating func's arg failed!!\n");
                  return false;
                }

                auto arg_name_cell = arg_name.get<Cons*>();
                set_arg(arg_name_cell->car().get<Symbol*>(), evaled);
                arg_name = arg_name_cell->cdr();
                ++argc;

                return true;
              },
              [&](Lisp_ptr dot_cdr) -> bool{
                assert(argc <= argi.required_args);

                if(argc < argi.required_args){
                  fprintf(stderr, "eval error: internal argument counter mismatched!! (read %d args)\n",
                          argc);
                  return false;
                }

                if(argi.variadic){
                  set_arg(arg_name.get<Symbol*>(), dot_cdr);
                  ++argc;
                  return true;
                }else{
                  if(!nullp(dot_cdr)){
                    fprintf(stderr, "funcall error: argcount mismatch! (more than required %d)\n",
                            argi.required_args);
                    return false;
                  }

                  return true;
                }
              })){
    goto end;
  }

  // real call
  switch(fun->type()){
  case Function::Type::interpreted:
    do_list(fun->get<Lisp_ptr>(),
            [&](Cons* cell) -> bool {
              VM.code().push(cell->car());
              eval();
              return true;
            },
            [&](Lisp_ptr last_cdr){
              if(!nullp(last_cdr)){
                fprintf(stderr, "eval error: body has dot list!\n");
                VM.return_value() = {};
              }
            });
    break;
  case Function::Type::native:
    fun->get<Function::NativeFunc>()();
    if(!VM.return_value())
      fprintf(stderr, "eval error: native func returned undef!\n");
    break;
  default:
    UNEXP_DEFAULT();
  }

 end:
  if(env){
    VM.leave_frame();
  }else{
    // cleaned by native func
  }
}

void eval_lambda(const Cons* rest){
  VM.return_value() = {};

  auto args = rest->car();
  if(rest->cdr().tag() != Ptr_tag::cons){
    fprintf(stderr, "eval error: lambda has invalid body!\n");
    return;
  }

  auto arg_info = parse_func_arg(args);
  if(!arg_info){
    fprintf(stderr, "eval error: lambda has invalid args!\n");
    return;
  }

  auto code = rest->cdr();
  if(!code.get<Cons*>()){
    fprintf(stderr, "eval error: lambda has no body!\n");
    return;
  }

  auto fun = new Function(code, arg_info, VM.frame());

  VM.return_value() = Lisp_ptr{fun};
  return;
}

void eval_if(Cons* rest){
  VM.return_value() = {};

  // extracting
  Lisp_ptr test, conseq, alt;

  int len =
  bind_cons_list(Lisp_ptr(rest),
                 [&](Cons* c){
                   test = c->car();
                 },
                 [&](Cons* c){
                   conseq = c->car();
                 },
                 [&](Cons* c){
                   alt = c->car();
                 });

  switch(len){
  case 1:
    fprintf(stderr, "eval error: informal if expr! (no test expr)\n");
    return;
  case 2: case 3: // successed
    break;
  default:
    fprintf(stderr, "eval error: informal if expr! (more than %d exprs)\n", len);
    return;
  }

  // evaluating
  VM.code().push(Lisp_ptr(VM_op::if_));
  VM.code().push(test);

  VM.stack().push(alt);
  VM.stack().push(conseq);
}

void vm_op_if(){
  auto test_result = VM.return_value();

  if(test_result.get<bool>()){
    VM.code().push(VM.stack().top());
    VM.stack().pop();
    VM.stack().pop();
  }else{
    VM.stack().pop();
    VM.code().push(VM.stack().top());
    VM.stack().pop();
  }
}

void eval_set(Cons* rest){
  VM.return_value() = {};

  // extracting
  Symbol* var = nullptr;
  Lisp_ptr val;

  int len =
    bind_cons_list(Lisp_ptr(rest),
                   [&](Cons* c){
                     var = c->car().get<Symbol*>();
                   },
                   [&](Cons* c){
                     val = c->car();
                   });

  if(!var){
    fprintf(stderr, "eval error: set!'s first element is not symbol!\n");
    return;
  }

  if(var->to_keyword() != Keyword::not_keyword){
    fprintf(stderr, "eval error: set!'s first element is Keyword (%s)!\n",
            var->name().c_str());
    return;
  }

  if(!val){
    fprintf(stderr, "eval error: no value is supplied for set!\n");
    return;
  }

  if(len > 2){
    fprintf(stderr, "eval error: informal set! expr! (more than %d exprs)\n", len);
    return;
  }

  // evaluating
  VM.code().push(Lisp_ptr{VM_op::set_});
  VM.code().push(val);
  VM.stack().push(Lisp_ptr{var});
}

void vm_op_set(){
  auto var = VM.stack().top().get<Symbol*>();
  VM.stack().pop();
  if(!var){
    fprintf(stderr, "eval error: internal error occured (set!'s varname is dismissed)\n");
    return;
  }

  VM.set(var, VM.return_value());
}

void eval_define(const Cons* rest){
  static constexpr auto test_varname = [](Symbol* var) -> bool{
    if(!var){
      fprintf(stderr, "eval error: defined variable name is not a symbol!\n");
      return false;
    }

    if(var->to_keyword() != Keyword::not_keyword){
      fprintf(stderr, "eval error: define's first element is Keyword (%s)!\n",
              var->name().c_str());
      return false;
    }

    return true;
  };

  Symbol* var = nullptr;
  Lisp_ptr value;
  unique_ptr<Function> func_value;

  VM.return_value() = {};

  // extracting
  auto first = rest->car();

  switch(first.tag()){
  case Ptr_tag::symbol: {
    var = first.get<Symbol*>();
    if(!test_varname(var))
      return;

    auto val_l = rest->cdr().get<Cons*>();
    if(!val_l){
      fprintf(stderr, "eval error: definition has empty expr!\n");
      return;
    }
    if(val_l->cdr().tag() != Ptr_tag::cons
       || val_l->cdr().get<Cons*>()){
      fprintf(stderr, "eval error: definition has extra expr!\n");
      return;
    }

    VM.code().push(val_l->car());
    eval();
    value = VM.return_value();
    break;
  }

  case Ptr_tag::cons: {
    auto lis = first.get<Cons*>();
    if(!lis){
      fprintf(stderr, "eval error: defined variable is not found!\n");
      return;
    }

    var = lis->car().get<Symbol*>();
    if(!test_varname(var))
      return;

    const auto& arg_info = parse_func_arg(lis->cdr());
    if(!arg_info){
      fprintf(stderr, "eval error: defined function argument is informal!\n");
      return;
    }

    auto code = rest->cdr();
    if(!code.get<Cons*>()){
      fprintf(stderr, "eval error: definition has empty body!\n");
      return;
    }

    func_value = unique_ptr<Function>(new Function(code, arg_info, VM.frame()));
    value = Lisp_ptr(func_value.get());
    break;
  }

  default:
    fprintf(stderr, "eval error: informal define syntax!\n");
    return;
  }

  // assignment
  VM.set(var, value);
  func_value.release();

  VM.return_value() = value;
  return;
}

} // namespace

void eval(){
  while(!VM.code().empty()){
    auto p = VM.code().top();
    VM.code().pop();

    switch(p.tag()){
    case Ptr_tag::symbol: {
      auto sym = p.get<Symbol*>();
      if(sym->to_keyword() != Keyword::not_keyword){
        fprintf(stderr, "eval error: symbol '%s' is keyword!!\n", sym->name().c_str());
        VM.return_value() = {};
        break;
      }

      VM.return_value() = VM.find(sym);
      break;
    }

    case Ptr_tag::cons: {
      auto c = p.get<Cons*>();
      auto first = c->car();

      // special operator?
      if(first.tag() == Ptr_tag::symbol){
        auto sym = first.get<Symbol*>();
        auto k = sym->to_keyword();

        if(k != Keyword::not_keyword){
          Cons* r = c->cdr().get<Cons*>();
          if(!r){
            fprintf(stderr, "eval error: expresssion (<KEYWORD>%s) is informal!\n",
                    (c->cdr().tag() == Ptr_tag::cons) ? "" : ". #");
            VM.return_value() = {};
            break;
          }

          switch(k){
          case Keyword::quote:  VM.return_value() = r->car(); break;
          case Keyword::lambda: eval_lambda(r); break;
          case Keyword::if_:    eval_if(r); break;
          case Keyword::set_:   eval_set(r); break;
          case Keyword::define: eval_define(r); break;

          case Keyword::cond:
          case Keyword::case_:
          case Keyword::and_:
          case Keyword::or_:
          case Keyword::let:
          case Keyword::let_star:
          case Keyword::letrec:
          case Keyword::begin:
          case Keyword::do_:
          case Keyword::delay:
          case Keyword::quasiquote:
            fprintf(stderr, "eval error: '%s' is under development...\n",
                    sym->name().c_str());
            VM.return_value() = {};
            break;

          case Keyword::else_:
          case Keyword::r_arrow:
          case Keyword::unquote:
          case Keyword::unquote_splicing:
            fprintf(stderr, "eval error: '%s' cannot be used as operator!!\n",
                    sym->name().c_str());
            VM.return_value() = {};
            break;

          default:
            UNEXP_DEFAULT();
          }
          break;
        }else{
          // macro call?
          //  try to find macro-function from symbol
          //    found -> macro expansion
          //    not found -> goto function calling
          ;
        }
      }

      // procedure call?
      VM.code().push(first);
      eval();
      auto proc = VM.return_value();
      if(proc.tag() != Ptr_tag::function){
        fprintf(stderr, "eval error: (# # ...)'s first element is not procedure (%s)\n",
                stringify(proc.tag()));
        VM.return_value() = {};
        break;
      }

      funcall(proc.get<Function*>(), c->cdr());
      break;
    }

    case Ptr_tag::vm_op:
      switch(p.get<VM_op>()){
      case VM_op::nop:
        break;
      case VM_op::if_:
        vm_op_if();
        break;
      case VM_op::set_:
        vm_op_set();
        break;
      default:
        UNEXP_DEFAULT();
      }
      break;

    default: // almost self-evaluating
      VM.return_value() = p;
      break;
    }
  }
}
<|endoftext|>


<commit_before>/* Gobby - GTK-based collaborative text editor
 * Copyright (C) 2008-2014 Armin Burgmeier <armin@arbur.net>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _GOBBY_TEXTSESSIONUSERVIEW_HPP_
#define _GOBBY_TEXTSESSIENUSERVIEW_HPP_

#include "core/sessionuserview.hpp"
#include "core/textsessionview.hpp"
#include "core/preferences.hpp"
#include "core/userlist.hpp"

// Allows a user in the user list to be double-clicked at, scrolling
// the text view to that user's cursor.
namespace Gobby
{

class TextSessionUserView: public SessionUserView
{
public:
	TextSessionUserView(TextSessionView& view, bool show_disconnected,
	                    Preferences::Option<bool>& userlist_view,
	                    Preferences::Option<unsigned int>& w);

	TextSessionView& get_session_view()
	{
		return static_cast<TextSessionView&>(
			SessionUserView::get_session_view());
	}

	const TextSessionView& get_session_view() const
	{
		return static_cast<const TextSessionView&>(
			SessionUserView::get_session_view());
	}

protected:
	void on_user_activated(InfUser* user);
};

}

#endif // _GOBBY_TEXTSESSIONUSERVIEW_HPP_
<commit_msg>Fix typo in header guard<commit_after>/* Gobby - GTK-based collaborative text editor
 * Copyright (C) 2008-2014 Armin Burgmeier <armin@arbur.net>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _GOBBY_TEXTSESSIONUSERVIEW_HPP_
#define _GOBBY_TEXTSESSIONUSERVIEW_HPP_

#include "core/sessionuserview.hpp"
#include "core/textsessionview.hpp"
#include "core/preferences.hpp"
#include "core/userlist.hpp"

// Allows a user in the user list to be double-clicked at, scrolling
// the text view to that user's cursor.
namespace Gobby
{

class TextSessionUserView: public SessionUserView
{
public:
	TextSessionUserView(TextSessionView& view, bool show_disconnected,
	                    Preferences::Option<bool>& userlist_view,
	                    Preferences::Option<unsigned int>& w);

	TextSessionView& get_session_view()
	{
		return static_cast<TextSessionView&>(
			SessionUserView::get_session_view());
	}

	const TextSessionView& get_session_view() const
	{
		return static_cast<const TextSessionView&>(
			SessionUserView::get_session_view());
	}

protected:
	void on_user_activated(InfUser* user);
};

}

#endif // _GOBBY_TEXTSESSIONUSERVIEW_HPP_
<|endoftext|>


<commit_before>#include <v8.h>
#include <functional>
#include <bastian.h>
#include "test-v8-common.h"

using namespace v8;

static v8::Isolate* isolate = v8::Isolate::New();
static v8::Isolate::Scope isolatescope(isolate);
static v8::HandleScope handle_scope(isolate);

TestContext::TestContext() {
  // Create a new Isolate and make it the current one.
  global_ = ObjectTemplate::New(v8::Isolate::GetCurrent());
}

void TestContext::AddFunction(const char * export_name, void (*func)(const v8::FunctionCallbackInfo<v8::Value>&)) {
  global_->Set(
    v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), export_name),
    FunctionTemplate::New(v8::Isolate::GetCurrent(), func));
}

void TestContext::RunJS(const char * rawSource) {
  // Create a new context.
  Local<Context> context = Context::New(v8::Isolate::GetCurrent(), NULL, global_);

  // Enter the context for compiling and running the hello world script.
  Context::Scope context_scope(context);

  Local<v8::String> source = v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), rawSource);
  Local<Script> script = Script::Compile(source);

  script->Run();
}
<commit_msg>remove using namespace v8 in test<commit_after>#include <v8.h>
#include <functional>
#include <bastian.h>
#include "test-v8-common.h"

TestContext::TestContext() {
  global_ = v8::ObjectTemplate::New(v8::Isolate::GetCurrent());
}

void TestContext::AddFunction(const char * export_name, void (*func)(const v8::FunctionCallbackInfo<v8::Value>&)) {
  global_->Set(
    v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), export_name),
    v8::FunctionTemplate::New(v8::Isolate::GetCurrent(), func));
}

void TestContext::RunJS(const char * rawSource) {
  // Create a new context.
  v8::Local<v8::Context> context = v8::Context::New(v8::Isolate::GetCurrent(), NULL, global_);

  // Enter the context for compiling and running the hello world script.
  v8::Context::Scope context_scope(context);

  v8::Local<v8::String> source = v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), rawSource);
  v8::Local<v8::Script> script = v8::Script::Compile(source);

  script->Run();
}
<|endoftext|>


<commit_before>/*
* Copyright (c) 2003-2015 Rony Shapiro <ronys@users.sourceforge.net>.
* All rights reserved. Use of the code is allowed under the
* Artistic License 2.0 terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

/**
 * \file Windows-specific implementation of debug.h
 */

// TRACE replacement
#include "../debug.h"
#include "../../core/util.h"
#include <wtypes.h>

#if defined(_DEBUG) || defined(DEBUG)

#include <stdio.h>

// Debug output - Same usage as MFC TRACE
// Adds timestamp to start of every piece of debug output
void pws_os::Trace(LPCTSTR lpszFormat, ...)
{
  va_list args;
  va_start(args, lpszFormat);

  stringT sTimeStamp;
  PWSUtil::GetTimeStamp(sTimeStamp);
  int num_required, num_written;

  num_required = GetStringBufSize(lpszFormat, args);
  va_end(args);//after using args we should reset list
  va_start(args, lpszFormat);

  TCHAR *szBuffer = new TCHAR[num_required];
  num_written = _vsntprintf_s(szBuffer, num_required, num_required - 1, lpszFormat, args);
  _ASSERT(num_required == num_written + 1);
  szBuffer[num_required - 1] = '\0';

  const size_t N = num_written + sTimeStamp.length() + 2;
  TCHAR *szDebugString = new TCHAR[N];

  _tcscpy_s(szDebugString, N, sTimeStamp.c_str());
  _tcscat_s(szDebugString, N, _T(" "));
  _tcscat_s(szDebugString, N, szBuffer);

  OutputDebugString(szDebugString);
  delete[] szDebugString;
  delete[] szBuffer;
  va_end(args);
}

void pws_os::Trace0(LPCTSTR lpszFormat)
{
  stringT sTimeStamp;
  PWSUtil::GetTimeStamp(sTimeStamp);

  const size_t N = _tcslen(lpszFormat) + sTimeStamp.length() + 2;
  TCHAR *szDebugString = new TCHAR[N];

  _tcscpy_s(szDebugString, N, sTimeStamp.c_str());
  _tcscat_s(szDebugString, N, _T(" "));
  _tcscat_s(szDebugString, N, lpszFormat);

  OutputDebugString(szDebugString);
  delete [] szDebugString;
}
#else   /* _DEBUG || DEBUG */
void pws_os::Trace(LPCTSTR , ...)
{
//  Do nothing in non-Debug mode
}
void pws_os::Trace0(LPCTSTR )
{
//  Do nothing in non-Debug mode
}
#endif  /* _DEBUG || DEBUG */

#if defined(_DEBUG) || defined(DEBUG)
#include "windows.h"
#include "../debug.h"
#include "../../core/StringX.h"

// This routine uses Windows functions
DWORD pws_os::IssueError(const stringT &csFunction, bool bMsgBox)
{
  LPVOID lpMsgBuf;
  LPVOID lpDisplayBuf;

  const DWORD dwLastError = GetLastError();

  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                dwLastError,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR) &lpMsgBuf,
                0, NULL);

  lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
       (lstrlen((LPCTSTR)lpMsgBuf) + csFunction.length() + 40) * sizeof(TCHAR));
  wsprintf((LPTSTR)lpDisplayBuf, TEXT("%s failed with error %d: %s"),
           csFunction.c_str(), dwLastError, lpMsgBuf);
  if (bMsgBox)
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
  else
    OutputDebugString((LPCTSTR)lpDisplayBuf);

  LocalFree(lpMsgBuf);
  LocalFree(lpDisplayBuf);

  return dwLastError;
}

void pws_os::HexDump(unsigned char *pmemory, const int &length,
                       const stringT &cs_prefix, const int &maxnum)
{
  TCHAR szBuffer[256];
  unsigned char *pmem;
  stringT cs_outbuff, cs_hexbuff, cs_charbuff;
  int i, j, len(length);
  unsigned char c;

  pmem = pmemory;
  while (len > 0) {
    // Show offset for this line.
    cs_charbuff.clear();
    cs_hexbuff.clear();
    Format(cs_outbuff, _T("%s: %08x *"), cs_prefix.c_str(), pmem);

    // Format hex portion of line and save chars for ascii portion
    if (len > maxnum)
      j = maxnum;
    else
      j = len;

    for (i = 0; i < j; i++) {
      c = *pmem++;

      if ((i % 4) == 0 && i != 0)
        cs_outbuff += _T(' ');

      Format(cs_hexbuff, _T("%02x"), c);
      cs_outbuff += cs_hexbuff;

      if (c >= 32 && c < 127)
        cs_charbuff += (TCHAR)c;
      else
        cs_charbuff += _T('.');
    }

    j = maxnum - j;

    // Fill out hex portion of short lines.
    for (i = j; i > 0; i--) {
      if ((i % 4) != 0)
        cs_outbuff += _T("  ");
      else
        cs_outbuff += _T("   ");
    }

    // Add ASCII character portion to line.
    cs_outbuff += _T("* |");
    cs_outbuff += cs_charbuff;

    // Fill out end of short lines.
    for (i = j; i > 0; i--)
      cs_outbuff += _T(' ');

    cs_outbuff += _T('|');

    // Next line
    len -= maxnum;

    _stprintf_s(szBuffer, sizeof(szBuffer) / sizeof(TCHAR),
                _T("%s\n"), cs_outbuff.c_str());
    OutputDebugString(szBuffer);
  };
}
#else  /* _DEBUG or DEBUG */
DWORD pws_os::IssueError(const stringT &, bool )
{
//  Do nothing in non-Debug mode
  return 0;
}

void pws_os::HexDump(unsigned char *, const int &,
                     const stringT &, const int &)
{
//  Do nothing in non-Debug mode
}
#endif  /* _DEBUG or DEBUG */
<commit_msg>Unify ASSERT usage in Windows build<commit_after>/*
* Copyright (c) 2003-2015 Rony Shapiro <ronys@users.sourceforge.net>.
* All rights reserved. Use of the code is allowed under the
* Artistic License 2.0 terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

/**
 * \file Windows-specific implementation of debug.h
 */

// TRACE replacement
#include "../debug.h"
#include "../../core/util.h"
#include <wtypes.h>

#if defined(_DEBUG) || defined(DEBUG)

#include <stdio.h>

// Debug output - Same usage as MFC TRACE
// Adds timestamp to start of every piece of debug output
void pws_os::Trace(LPCTSTR lpszFormat, ...)
{
  va_list args;
  va_start(args, lpszFormat);

  stringT sTimeStamp;
  PWSUtil::GetTimeStamp(sTimeStamp);
  int num_required, num_written;

  num_required = GetStringBufSize(lpszFormat, args);
  va_end(args);//after using args we should reset list
  va_start(args, lpszFormat);

  TCHAR *szBuffer = new TCHAR[num_required];
  num_written = _vsntprintf_s(szBuffer, num_required, num_required - 1, lpszFormat, args);
  ASSERT(num_required == num_written + 1);
  szBuffer[num_required - 1] = '\0';

  const size_t N = num_written + sTimeStamp.length() + 2;
  TCHAR *szDebugString = new TCHAR[N];

  _tcscpy_s(szDebugString, N, sTimeStamp.c_str());
  _tcscat_s(szDebugString, N, _T(" "));
  _tcscat_s(szDebugString, N, szBuffer);

  OutputDebugString(szDebugString);
  delete[] szDebugString;
  delete[] szBuffer;
  va_end(args);
}

void pws_os::Trace0(LPCTSTR lpszFormat)
{
  stringT sTimeStamp;
  PWSUtil::GetTimeStamp(sTimeStamp);

  const size_t N = _tcslen(lpszFormat) + sTimeStamp.length() + 2;
  TCHAR *szDebugString = new TCHAR[N];

  _tcscpy_s(szDebugString, N, sTimeStamp.c_str());
  _tcscat_s(szDebugString, N, _T(" "));
  _tcscat_s(szDebugString, N, lpszFormat);

  OutputDebugString(szDebugString);
  delete [] szDebugString;
}
#else   /* _DEBUG || DEBUG */
void pws_os::Trace(LPCTSTR , ...)
{
//  Do nothing in non-Debug mode
}
void pws_os::Trace0(LPCTSTR )
{
//  Do nothing in non-Debug mode
}
#endif  /* _DEBUG || DEBUG */

#if defined(_DEBUG) || defined(DEBUG)
#include "windows.h"
#include "../debug.h"
#include "../../core/StringX.h"

// This routine uses Windows functions
DWORD pws_os::IssueError(const stringT &csFunction, bool bMsgBox)
{
  LPVOID lpMsgBuf;
  LPVOID lpDisplayBuf;

  const DWORD dwLastError = GetLastError();

  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                dwLastError,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR) &lpMsgBuf,
                0, NULL);

  lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
       (lstrlen((LPCTSTR)lpMsgBuf) + csFunction.length() + 40) * sizeof(TCHAR));
  wsprintf((LPTSTR)lpDisplayBuf, TEXT("%s failed with error %d: %s"),
           csFunction.c_str(), dwLastError, lpMsgBuf);
  if (bMsgBox)
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
  else
    OutputDebugString((LPCTSTR)lpDisplayBuf);

  LocalFree(lpMsgBuf);
  LocalFree(lpDisplayBuf);

  return dwLastError;
}

void pws_os::HexDump(unsigned char *pmemory, const int &length,
                       const stringT &cs_prefix, const int &maxnum)
{
  TCHAR szBuffer[256];
  unsigned char *pmem;
  stringT cs_outbuff, cs_hexbuff, cs_charbuff;
  int i, j, len(length);
  unsigned char c;

  pmem = pmemory;
  while (len > 0) {
    // Show offset for this line.
    cs_charbuff.clear();
    cs_hexbuff.clear();
    Format(cs_outbuff, _T("%s: %08x *"), cs_prefix.c_str(), pmem);

    // Format hex portion of line and save chars for ascii portion
    if (len > maxnum)
      j = maxnum;
    else
      j = len;

    for (i = 0; i < j; i++) {
      c = *pmem++;

      if ((i % 4) == 0 && i != 0)
        cs_outbuff += _T(' ');

      Format(cs_hexbuff, _T("%02x"), c);
      cs_outbuff += cs_hexbuff;

      if (c >= 32 && c < 127)
        cs_charbuff += (TCHAR)c;
      else
        cs_charbuff += _T('.');
    }

    j = maxnum - j;

    // Fill out hex portion of short lines.
    for (i = j; i > 0; i--) {
      if ((i % 4) != 0)
        cs_outbuff += _T("  ");
      else
        cs_outbuff += _T("   ");
    }

    // Add ASCII character portion to line.
    cs_outbuff += _T("* |");
    cs_outbuff += cs_charbuff;

    // Fill out end of short lines.
    for (i = j; i > 0; i--)
      cs_outbuff += _T(' ');

    cs_outbuff += _T('|');

    // Next line
    len -= maxnum;

    _stprintf_s(szBuffer, sizeof(szBuffer) / sizeof(TCHAR),
                _T("%s\n"), cs_outbuff.c_str());
    OutputDebugString(szBuffer);
  };
}
#else  /* _DEBUG or DEBUG */
DWORD pws_os::IssueError(const stringT &, bool )
{
//  Do nothing in non-Debug mode
  return 0;
}

void pws_os::HexDump(unsigned char *, const int &,
                     const stringT &, const int &)
{
//  Do nothing in non-Debug mode
}
#endif  /* _DEBUG or DEBUG */
<|endoftext|>


<commit_before>#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
#include <math>

static const float tol = 0.000000000000001f;

void normalize(double &vec[2]){
    float m = sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
    if(tol <= m) m = 1;
    vec[0] /= m;
    vec[1] /= m;

    if(fabs(vec[0]) < tol) vec[0] = 0.0f;
    if(fabs(vec[1]) < tol) vec[1] = 0.0f;
}

int main(int argc, char *argv[]){
    ros::NodeHandle node;
    ros::Publisher cmd_pub = node.advertise<geometry_msgs::Twist>("cmd_vel", 10);

    ros::Duration(1).sleep();
    geometry_msgs::Twist cmd;

    //cmd.linear.z = 1;

    double Fs[2];
    double r[2], u[2];
    double obs[2];
    double U = 0;

    const double A = 0;
    const double B = 13000;
    const double n = 1;
    const double m = 2.5;

    const double force = 0.025;

    Fs[0] = Fs[1] = 0;
    obs[0] = 0.5;
    obs[1] = 0;

    r[0] -= obs[0];
    r[1] -= obs[1];
    u = r;
    normalize(u);

    const double length = 4.0; //TODO: fix param
    const double d = sqrt(r[0]*r[0] + r[1]*r[1]) / length;
    U = -A/pow(d, n) + B/pow(d, m);

    u[0] *= U;
    u[1] *= U;
    //Fs += VRotate2D(-Units[i].fOrientation, U * u);

    cmd_pub.publish(cmd);

    return 0;
}

<commit_msg>Implement take off<commit_after>#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
#include <math>

static const float tol = 0.000000000000001f;

void normalize(double &vec[2]){
    float m = sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
    if(tol <= m) m = 1;
    vec[0] /= m;
    vec[1] /= m;

    if(fabs(vec[0]) < tol) vec[0] = 0.0f;
    if(fabs(vec[1]) < tol) vec[1] = 0.0f;
}

int main(int argc, char *argv[]){
    ros::NodeHandle node;
    ros::Publisher cmd_pub = node.advertise<geometry_msgs::Twist>("cmd_vel", 10);

    ros::Duration(1).sleep();
    geometry_msgs::Twist cmd;
    cmd.linear.z = 0.3;
    cmd_pub.publish(cmd);
    ros::Duration(1).sleep();

    cmd.linear.z = 0;
    cmd_pub.publish(cmd);
    ros::Duration(1).sleep();

    /*
    double Fs[2];
    double r[2], u[2];
    double obs[2];
    double U = 0;

    const double A = 0;
    const double B = 13000;
    const double n = 1;
    const double m = 2.5;

    const double force = 0.025;

    Fs[0] = Fs[1] = 0;
    obs[0] = 0.5;
    obs[1] = 0;

    r[0] -= obs[0];
    r[1] -= obs[1];
    u = r;
    normalize(u);

    //const double length = 4.0; //TODO: fix param
    //const double d = sqrt(r[0]*r[0] + r[1]*r[1]) / length;
    const double d = sqrt(r[0]*r[0] + r[1]*r[1]);
    U = -A/pow(d, n) + B/pow(d, m);

    Fs[0] += U * u[0];
    Fs[1] += U * u[1];
    */

    return 0;
}

<|endoftext|>


<commit_before>#ifndef SRC_ONLINE_ADAGRAD_RDA_HPP_
#define SRC_ONLINE_ADAGRAD_RDA_HPP_

#include <Eigen/Dense>
#include <cmath>
#include "utility.hpp"

class ADAGRAD_RDA {
private :
  const std::size_t kDim;
  const double kEta;
  const double kLambda;

private :
  std::size_t _timestep;
  Eigen::VectorXd _w;
  Eigen::VectorXd _h;
  Eigen::VectorXd _g;

public :
  ADAGRAD_RDA(const std::size_t dim, const double eta, const double lambda)
    : kDim(dim),
      kEta(eta),
      kLambda(lambda),
      _timestep(0),
      _w(Eigen::VectorXd::Zero(kDim)),
      _h(Eigen::VectorXd::Zero(kDim)),
      _g(Eigen::VectorXd::Zero(kDim)) {
    static_assert(std::numeric_limits<decltype(dim)>::max() > 0, "Dimension Error. (Dimension > 0)");
    static_assert(std::numeric_limits<decltype(eta)>::max() > 0, "Hyper Parameter Error. (eta > 0)");
    static_assert(std::numeric_limits<decltype(lambda)>::max() > 0, "Hyper Parameter Error. (lambda > 0)");
    assert(dim > 0);
    assert(eta > 0);
    assert(lambda > 0);
  }

  virtual ~ADAGRAD_RDA() { }

  double calculate_margin(const Eigen::VectorXd& x) const {
    return _w.dot(x);
  }

  double suffer_loss(const Eigen::VectorXd& x, const int y) const {
    return std::max(0.0, 1.0 - y * _w.dot(x));
  }

  void update(const Eigen::VectorXd& feature, const int label) {
    if (suffer_loss(feature, label) <= 0.0) { return ; }

    _timestep++;
    utility::enumerate(feature.data(), feature.data() + feature.size(), 0,
                       [&](const int index, const double value) {
                         const auto gradiant = -label * value;
                         _g[index] += gradiant;
                         _h[index] += gradiant * gradiant;

                         const auto sign = _g[index] >= 0 ? 1 : -1;
                         const auto eta = kEta / std::sqrt(_h[index]);
                         const auto u = std::abs(_g[index]) / _timestep;

                         _w[index] = (u <= kLambda) ? 0.0 : -sign * eta * _timestep * (u - kLambda);
                       });

  }

  int predict(const Eigen::VectorXd& x) const {
    return calculate_margin(x) > 0.0 ? 1 : -1;
  }

};

#endif //SRC_ONLINE_ADAGRAD_RDA_HPP_
<commit_msg>bool値を返すようにした<commit_after>#ifndef SRC_ONLINE_ADAGRAD_RDA_HPP_
#define SRC_ONLINE_ADAGRAD_RDA_HPP_

#include <Eigen/Dense>
#include <cmath>
#include "utility.hpp"

class ADAGRAD_RDA {
private :
  const std::size_t kDim;
  const double kEta;
  const double kLambda;

private :
  std::size_t _timestep;
  Eigen::VectorXd _w;
  Eigen::VectorXd _h;
  Eigen::VectorXd _g;

public :
  ADAGRAD_RDA(const std::size_t dim, const double eta, const double lambda)
    : kDim(dim),
      kEta(eta),
      kLambda(lambda),
      _timestep(0),
      _w(Eigen::VectorXd::Zero(kDim)),
      _h(Eigen::VectorXd::Zero(kDim)),
      _g(Eigen::VectorXd::Zero(kDim)) {
    static_assert(std::numeric_limits<decltype(dim)>::max() > 0, "Dimension Error. (Dimension > 0)");
    static_assert(std::numeric_limits<decltype(eta)>::max() > 0, "Hyper Parameter Error. (eta > 0)");
    static_assert(std::numeric_limits<decltype(lambda)>::max() > 0, "Hyper Parameter Error. (lambda > 0)");
    assert(dim > 0);
    assert(eta > 0);
    assert(lambda > 0);
  }

  virtual ~ADAGRAD_RDA() { }

  double calculate_margin(const Eigen::VectorXd& x) const {
    return _w.dot(x);
  }

  double suffer_loss(const Eigen::VectorXd& x, const int y) const {
    return std::max(0.0, 1.0 - y * _w.dot(x));
  }

  bool update(const Eigen::VectorXd& feature, const int label) {
    if (suffer_loss(feature, label) <= 0.0) { return false; }

    _timestep++;
    utility::enumerate(feature.data(), feature.data() + feature.size(), 0,
                       [&](const int index, const double value) {
                         const auto gradiant = -label * value;
                         _g[index] += gradiant;
                         _h[index] += gradiant * gradiant;

                         const auto sign = _g[index] >= 0 ? 1 : -1;
                         const auto eta = kEta / std::sqrt(_h[index]);
                         const auto u = std::abs(_g[index]) / _timestep;

                         _w[index] = (u <= kLambda) ? 0.0 : -sign * eta * _timestep * (u - kLambda);
                       });
    return true;

  }

  int predict(const Eigen::VectorXd& x) const {
    return calculate_margin(x) > 0.0 ? 1 : -1;
  }

};

#endif //SRC_ONLINE_ADAGRAD_RDA_HPP_
<|endoftext|>


<commit_before>#include <tansa/action.h>
#include <tansa/control.h>
#include <tansa/core.h>
#include <tansa/jocsParser.h>
#include <tansa/config.h>
#include <tansa/jocsPlayer.h>
#include <tansa/mocap.h>
#include <tansa/gazebo.h>
#include <tansa/osc.h>

#ifdef  __linux__
#include <sys/signal.h>
#endif
//TODO check if these work on OSX
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>

using namespace std;
using namespace tansa;

static bool running;
static bool killmode = false;
static bool pauseMode = false;
static bool stopMode = false;
static bool playMode = false;
static bool prepareMode = false;
static float scale = 1.0;
static JocsPlayer* player = nullptr;
static vector<Vehicle *> vehicles;
static std::vector<vehicle_config> vconfigs;
static vector<unsigned> jocsActiveIds;

void signal_sigint(int s) {
	// TODO: Prevent
	running = false;
}

/* For sending a system state update to the gui */
void send_status_message() {

	json j;

	j["type"] = "status";
	j["time"] = player->currentTime();

	json vehs = json::array();
	for(int i = 0; i < vehicles.size(); i++) {
		json v;
		v["id"] = vconfigs[i].net_id;
		v["role"] = i <= jocsActiveIds.size() - 1? jocsActiveIds[i] : -1;
		v["connected"] = vehicles[i]->connected;
		v["armed"] = vehicles[i]->armed;
		v["tracking"] = vehicles[i]->tracking;

		json pos = json::array();
		pos.push_back(vehicles[i]->state.position.x());
		pos.push_back(vehicles[i]->state.position.y());
		pos.push_back(vehicles[i]->state.position.z());
		v["position"] = pos;

		json bat = {
			{"voltage", vehicles[i]->battery.voltage},
			{"percent", vehicles[i]->battery.percent}
		};
		v["battery"] = bat;

		vehs.push_back(v);
	}
	j["vehicles"] = vehs;

	json global;
	global["playing"] = player->isPlaying();
	global["ready"] = player->isReady();
	j["global"] = global;

	tansa::send_message(j);
}

void send_file_list() {
	json j;

	j["type"] = "list_reply";
	json files = json::array();
	DIR *dir;
	struct dirent *ent;
	if ((dir = opendir ("data")) != NULL) {
		while ((ent = readdir (dir)) != NULL) {
			if(ent->d_type == DT_REG && std::string(ent->d_name).find(".jocs") != std::string::npos)
				files.push_back(std::string(ent->d_name));
		}
		closedir (dir);
	} else {
		/* could not open directory */
		//TODO Do something intelligent here
	}
	j["files"] = files;
	tansa::send_message(j);
}

void load_jocs_file(const json &data){
	if(player == nullptr){
		//TODO: Send some kinda error code here
		return;
	}
	std::string file = data["filename"];
	int cue = data["cue"];
	//TODO: Prepare the jocs file to start at this cue.
	//TODO: Need to make sure this gets deleted. Will have to delete inside the JocsPlayer class when we load a new file.
	// In other words, we transfer ownership of the jocs object to the player here.
	auto jocs = Jocs::Parse("data/"+ file, scale);
	player->loadJocs(jocs);
	auto breakpoints = jocs->GetBreakpoints();
	json j;
	j["type"] = "load_reply";
	json nums = json::array();
	for(auto& b : breakpoints){
		nums.push_back(b.GetNumber());
	}
	json positions = json::array();
	for(auto& b : breakpoints){
		//TODO: need to fill in the starting positions. Not contained in breakpoints currently
	}
	j["cues"] = nums;
	j["target_positions"] = positions;
	tansa::send_message(j);

}

void socket_on_message(const json &data) {

	string type = data["type"];

	if(type == "prepare") {
		printf("Preparing...\n");
		prepareMode = true;
	} else if(type == "play") {
		printf("Playing...\n");
		playMode = true;
	} else if(type == "land") {
		player->land();
	} else if (type == "pause") {
		printf("Pausing...\n");
		pauseMode = true;
	} else if (type == "stop") {
		printf("Stopping...\n");
		stopMode = true;
	} else if (type == "reset") {
		printf("Resetting...\n");
	} else if (type == "list"){
		printf("Sending file list...\n");
		send_file_list();
	} else if (type == "load"){
		printf("Loading jocs file...\n");
		load_jocs_file(data);
	} else if(type == "kill") {
		bool enabled = data["enabled"];
		printf("Killing...\n");
		killmode = enabled;
	} else {
		// TODO: Send an error message back to the browser
		printf("Unexpected message type recieved!\n");
	}
}

void osc_on_message(OSCMessage &msg) {

	// Address will look something like: '/cue/0101/start'
	if(msg.address[0] == "cue") {

		int num = std::stoi(msg.address[1]);

		if(msg.address[2] == "load") {
			player->prepare();
		}
		else if(msg.address[2] == "start") {
			printf("Starting at cue #: %d\n", num);

			// Assert that it is already prepared at the given cue

			player->play();
		}

	}

	/*
	printf("Address:\n");
	for(auto str : msg.address)
		printf("- %s\n", str.c_str());

	printf("\nArgs:\n");
	for(auto str : msg.args)
		printf("- %s\n", str.c_str());
	*/

}

pthread_t console_handle;

void *console_thread(void *arg) {

	while(running) {

		// Read a command
		cout << "> ";
		string line;
		getline(cin, line);

		// Split into arguments
		vector<string> args;
		istringstream iss(line);
		while(!iss.eof()) {
			string a;
			iss >> a;

			if(iss.fail())
				break;

			args.push_back(a);
		}

		if(args.size() == 0)
			continue;

		if (args[0] == "prepare") {
			cout << "Preparing..." << endl;
			prepareMode = true;
		} else if (args[0] == "play") {
			cout << "Playing..." << endl;
			playMode = true;
		} else if (args[0] == "pause") {
			cout << "Pausing..." << endl;
			pauseMode = true;
		} else if (args[0] == "stop") {
			cout << "Stopping..." << endl;
			stopMode = true;
		} else if (args[0] == "land") {
			cout << "Landing..." << endl;
			player->land();
		} else if (args[0] == "kill") {
			killmode = args.size() <= 1 || !(args[1] == "off");
		}

	}

}

void console_start() {
	pthread_create(&console_handle, NULL, console_thread, NULL);
}

int main(int argc, char *argv[]) {

	assert(argc == 2);
	string configPath = argv[1];

	ifstream configStream(configPath);
	if (!configStream) throw "Unable to read config file!";

	/// Parse the config file
	std::string configData((std::istreambuf_iterator<char>(configStream)), std::istreambuf_iterator<char>());
	nlohmann::json rawJson = nlohmann::json::parse(configData);
	hardware_config config;
	string jocsPath = rawJson["jocsPath"];
	vector<unsigned> activeids = rawJson["jocsActiveIds"];
	jocsActiveIds = activeids;
	bool useMocap = rawJson["useMocap"];
	scale = rawJson["theaterScale"];
	bool enableMessaging = rawJson["enableMessaging"];
	bool enableOSC = rawJson["enableOSC"];

	if (useMocap) {
		nlohmann::json hardwareConfig = rawJson["hardwareConfig"];
		config.clientAddress = hardwareConfig["clientAddress"];
		config.serverAddress = hardwareConfig["serverAddress"];
	}

	vconfigs.resize(rawJson["vehicles"].size());
	for(unsigned i = 0; i < rawJson["vehicles"].size(); i++) {
		vconfigs[i].net_id = rawJson["vehicles"][i]["net_id"];
		if(useMocap) {
			vconfigs[i].lport = 14550 + 10*vconfigs[i].net_id;
			vconfigs[i].rport = 14555;
		}
		else { // The simulated ones are zero-indexed and
			vconfigs[i].lport = 14550 + 10*(vconfigs[i].net_id - 1);
			vconfigs[i].rport = 14555 + 10*(vconfigs[i].net_id - 1);
		}
	}

	Jocs *jocs = Jocs::Parse(jocsPath, scale);

	auto homes = jocs->GetHomes();

	// Only pay attention to homes of active drones
	std::vector<Point> spawns;
	for (int i = 0; i < jocsActiveIds.size(); i++) {
		int chosenId = jocsActiveIds[i];
		// We assume the user only configured for valid IDs..
		spawns.push_back(homes[chosenId]);
		spawns[i].z() = 0;
	}

	tansa::init(enableMessaging);

	if(enableMessaging) {
		tansa::on_message(socket_on_message);
	}

	Mocap *mocap = nullptr;
	GazeboConnector *gazebo = nullptr;

	// Only pay attention to homes of active drones
	// TODO: Have a better check for mocap initialization/health
	if (useMocap) {
		mocap = new Mocap();
		mocap->connect(config.clientAddress, config.serverAddress);
	} else {
		gazebo = new GazeboConnector();
		gazebo->connect();
		gazebo->spawn(spawns);
	}

	int n = spawns.size();

	if (n > vconfigs.size()) {
		printf("Not enough drones on the network\n");
		return 1;
	}

	vehicles.resize(n);

	for(int i = 0; i < n; i++) {
		const vehicle_config &v = vconfigs[i];

		vehicles[i] = new Vehicle();

		// Load default parameters
		vehicles[i]->readParams("./config/params/default.json");
		// TODO: Also read in per-drone ones

		vehicles[i]->connect(v.lport, v.rport);
		if (useMocap) {
			mocap->track(vehicles[i], i+1);
		} else {
			gazebo->track(vehicles[i], i);
		}
	}

	if(enableOSC) {
		OSC *osc = new OSC();
		osc->start(53100);
		osc->set_listener(osc_on_message);
	}

	player = new JocsPlayer(vehicles, jocsActiveIds);
	player->loadJocs(jocs);

	running = true;
	signal(SIGINT, signal_sigint);

	int i = 0;

	/*
	// For sample lighting demo
	float level = 0;
	float dl = 0.005;
	*/

	signal(SIGINT, signal_sigint);
	printf("running...\n");
	running = true;

	console_start();

	Rate r(100);
	while(running) {

		// Regular status messages
		if(enableMessaging && i % 20 == 0) {
			send_status_message();
		}

		if (killmode) {
			for(Vehicle *v : vehicles)
				v->terminate();
		} else if (prepareMode) {
			prepareMode = false;
			player->prepare();
		} else if (playMode) {
			playMode = false;
			player->play();
		} else if (pauseMode) {
			pauseMode = false;
			player->pause();
		} else if (stopMode) {
			stopMode = false;
			player->stop();
		} else {
			player->step();
		}

		r.sleep();
		i++;
	}

	/// Cleanup
	if (useMocap) {
		mocap->disconnect();
		delete mocap;
	} else {
		gazebo->disconnect();
		delete gazebo;
	}

	// Stop all vehicles
	for(int vi = 0; vi < n; vi++) {
		Vehicle *v = vehicles[vi];
		v->disconnect();
		delete v;
	}

	player->cleanup();

	printf("Done!\n");
}
<commit_msg>Changes to gcs to be able to not segault on file upload<commit_after>#include <tansa/action.h>
#include <tansa/control.h>
#include <tansa/core.h>
#include <tansa/jocsParser.h>
#include <tansa/config.h>
#include <tansa/jocsPlayer.h>
#include <tansa/mocap.h>
#include <tansa/gazebo.h>
#include <tansa/osc.h>

#ifdef  __linux__
#include <sys/signal.h>
#endif
//TODO check if these work on OSX
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>

using namespace std;
using namespace tansa;

static bool running;
static bool killmode = false;
static bool pauseMode = false;
static bool stopMode = false;
static bool playMode = false;
static bool prepareMode = false;
static float scale = 1.0;
static JocsPlayer* player = nullptr;
static vector<Vehicle *> vehicles;
static std::vector<vehicle_config> vconfigs;
static vector<unsigned> jocsActiveIds;

void signal_sigint(int s) {
	// TODO: Prevent
	running = false;
}

/* For sending a system state update to the gui */
void send_status_message() {

	json j;

	j["type"] = "status";
	j["time"] = player->currentTime();

	json vehs = json::array();
	for(int i = 0; i < vehicles.size(); i++) {
		json v;
		v["id"] = vconfigs[i].net_id;
		v["role"] = i <= jocsActiveIds.size() - 1? jocsActiveIds[i] : -1;
		v["connected"] = vehicles[i]->connected;
		v["armed"] = vehicles[i]->armed;
		v["tracking"] = vehicles[i]->tracking;

		json pos = json::array();
		pos.push_back(vehicles[i]->state.position.x());
		pos.push_back(vehicles[i]->state.position.y());
		pos.push_back(vehicles[i]->state.position.z());
		v["position"] = pos;

		json bat = {
			{"voltage", vehicles[i]->battery.voltage},
			{"percent", vehicles[i]->battery.percent}
		};
		v["battery"] = bat;

		vehs.push_back(v);
	}
	j["vehicles"] = vehs;

	json global;
	global["playing"] = player->isPlaying();
	global["ready"] = player->isReady();
	j["global"] = global;

	tansa::send_message(j);
}

void send_file_list() {
	json j;

	j["type"] = "list_reply";
	json files = json::array();
	DIR *dir;
	struct dirent *ent;
	if ((dir = opendir ("data")) != NULL) {
		while ((ent = readdir (dir)) != NULL) {
			if(ent->d_type == DT_REG && std::string(ent->d_name).find(".jocs") != std::string::npos)
				files.push_back(std::string(ent->d_name));
		}
		closedir (dir);
	} else {
		/* could not open directory */
		//TODO Do something intelligent here
	}
	j["files"] = files;
	tansa::send_message(j);
}

void load_jocs_file(const json &data){
	if(player == nullptr){
		//TODO: Send some kinda error code here
		return;
	}
	std::string file = data["filename"];
	//int cue = data["cue"];
	//TODO: Prepare the jocs file to start at this cue.
	//TODO: Need to make sure this gets deleted. Will have to delete inside the JocsPlayer class when we load a new file.
	// In other words, we transfer ownership of the jocs object to the player here.
	auto jocs = Jocs::Parse("data/"+ file, scale);
	player->loadJocs(jocs);
	auto breakpoints = jocs->GetBreakpoints();
	json j;
	j["type"] = "load_reply";
	json nums = json::array();
	for(auto& b : breakpoints){
		nums.push_back(b.GetNumber());
	}
	json positions = json::array();
	for(auto& b : breakpoints){
		//TODO: need to fill in the starting positions. Not contained in breakpoints currently
	}
	j["cues"] = nums;
	j["target_positions"] = positions;
	tansa::send_message(j);

}

void socket_on_message(const json &data) {

	string type = data["type"];

	if(type == "prepare") {
		printf("Preparing...\n");
		prepareMode = true;
	} else if(type == "play") {
		printf("Playing...\n");
		playMode = true;
	} else if(type == "land") {
		player->land();
	} else if (type == "pause") {
		printf("Pausing...\n");
		pauseMode = true;
	} else if (type == "stop") {
		printf("Stopping...\n");
		stopMode = true;
	} else if (type == "reset") {
		printf("Resetting...\n");
	} else if (type == "list"){
		printf("Sending file list...\n");
		send_file_list();
	} else if (type == "load"){
		printf("Loading jocs file...\n");
		load_jocs_file(data);
	} else if(type == "kill") {
		bool enabled = data["enabled"];
		printf("Killing...\n");
		killmode = enabled;
	} else {
		// TODO: Send an error message back to the browser
		printf("Unexpected message type recieved!\n");
	}
}

void osc_on_message(OSCMessage &msg) {

	// Address will look something like: '/cue/0101/start'
	if(msg.address[0] == "cue") {

		int num = std::stoi(msg.address[1]);

		if(msg.address[2] == "load") {
			player->prepare();
		}
		else if(msg.address[2] == "start") {
			printf("Starting at cue #: %d\n", num);

			// Assert that it is already prepared at the given cue

			player->play();
		}

	}

	/*
	printf("Address:\n");
	for(auto str : msg.address)
		printf("- %s\n", str.c_str());

	printf("\nArgs:\n");
	for(auto str : msg.args)
		printf("- %s\n", str.c_str());
	*/

}

pthread_t console_handle;

void *console_thread(void *arg) {

	while(running) {

		// Read a command
		cout << "> ";
		string line;
		getline(cin, line);

		// Split into arguments
		vector<string> args;
		istringstream iss(line);
		while(!iss.eof()) {
			string a;
			iss >> a;

			if(iss.fail())
				break;

			args.push_back(a);
		}

		if(args.size() == 0)
			continue;

		if (args[0] == "prepare") {
			cout << "Preparing..." << endl;
			prepareMode = true;
		} else if (args[0] == "play") {
			cout << "Playing..." << endl;
			playMode = true;
		} else if (args[0] == "pause") {
			cout << "Pausing..." << endl;
			pauseMode = true;
		} else if (args[0] == "stop") {
			cout << "Stopping..." << endl;
			stopMode = true;
		} else if (args[0] == "land") {
			cout << "Landing..." << endl;
			player->land();
		} else if (args[0] == "kill") {
			killmode = args.size() <= 1 || !(args[1] == "off");
		}

	}

}

void console_start() {
	pthread_create(&console_handle, NULL, console_thread, NULL);
}

int main(int argc, char *argv[]) {

	assert(argc == 2);
	string configPath = argv[1];

	ifstream configStream(configPath);
	if (!configStream) throw "Unable to read config file!";

	/// Parse the config file
	std::string configData((std::istreambuf_iterator<char>(configStream)), std::istreambuf_iterator<char>());
	nlohmann::json rawJson = nlohmann::json::parse(configData);
	hardware_config config;
	string jocsPath = rawJson["jocsPath"];
	vector<unsigned> activeids = rawJson["jocsActiveIds"];
	jocsActiveIds = activeids;
	bool useMocap = rawJson["useMocap"];
	scale = rawJson["theaterScale"];
	bool enableMessaging = rawJson["enableMessaging"];
	bool enableOSC = rawJson["enableOSC"];

	if (useMocap) {
		nlohmann::json hardwareConfig = rawJson["hardwareConfig"];
		config.clientAddress = hardwareConfig["clientAddress"];
		config.serverAddress = hardwareConfig["serverAddress"];
	}

	vconfigs.resize(rawJson["vehicles"].size());
	for(unsigned i = 0; i < rawJson["vehicles"].size(); i++) {
		vconfigs[i].net_id = rawJson["vehicles"][i]["net_id"];
		if(useMocap) {
			vconfigs[i].lport = 14550 + 10*vconfigs[i].net_id;
			vconfigs[i].rport = 14555;
		}
		else { // The simulated ones are zero-indexed and
			vconfigs[i].lport = 14550 + 10*(vconfigs[i].net_id - 1);
			vconfigs[i].rport = 14555 + 10*(vconfigs[i].net_id - 1);
		}
	}

	Jocs *jocs = Jocs::Parse(jocsPath, scale);

	auto homes = jocs->GetHomes();

	// Only pay attention to homes of active drones
	std::vector<Point> spawns;
	for (int i = 0; i < jocsActiveIds.size(); i++) {
		int chosenId = jocsActiveIds[i];
		// We assume the user only configured for valid IDs..
		spawns.push_back(homes[chosenId]);
		spawns[i].z() = 0;
	}

	tansa::init(enableMessaging);

	if(enableMessaging) {
		tansa::on_message(socket_on_message);
	}

	Mocap *mocap = nullptr;
	GazeboConnector *gazebo = nullptr;

	// Only pay attention to homes of active drones
	// TODO: Have a better check for mocap initialization/health
	if (useMocap) {
		mocap = new Mocap();
		mocap->connect(config.clientAddress, config.serverAddress);
	} else {
		gazebo = new GazeboConnector();
		gazebo->connect();
		gazebo->spawn(spawns);
	}

	int n = spawns.size();

	if (n > vconfigs.size()) {
		printf("Not enough drones on the network\n");
		return 1;
	}

	vehicles.resize(n);

	for(int i = 0; i < n; i++) {
		const vehicle_config &v = vconfigs[i];

		vehicles[i] = new Vehicle();

		// Load default parameters
		vehicles[i]->readParams("./config/params/default.json");
		// TODO: Also read in per-drone ones

		vehicles[i]->connect(v.lport, v.rport);
		if (useMocap) {
			mocap->track(vehicles[i], i+1);
		} else {
			gazebo->track(vehicles[i], i);
		}
	}

	if(enableOSC) {
		OSC *osc = new OSC();
		osc->start(53100);
		osc->set_listener(osc_on_message);
	}

	player = new JocsPlayer(vehicles, jocsActiveIds);
	player->loadJocs(jocs);

	running = true;
	signal(SIGINT, signal_sigint);

	int i = 0;

	/*
	// For sample lighting demo
	float level = 0;
	float dl = 0.005;
	*/

	signal(SIGINT, signal_sigint);
	printf("running...\n");
	running = true;

	console_start();

	Rate r(100);
	while(running) {

		// Regular status messages
		if(enableMessaging && i % 20 == 0) {
			send_status_message();
		}

		if (killmode) {
			for(Vehicle *v : vehicles)
				v->terminate();
		} else if (prepareMode) {
			prepareMode = false;
			player->prepare();
		} else if (playMode) {
			playMode = false;
			player->play();
		} else if (pauseMode) {
			pauseMode = false;
			player->pause();
		} else if (stopMode) {
			stopMode = false;
			player->stop();
		} else {
			player->step();
		}

		r.sleep();
		i++;
	}

	/// Cleanup
	if (useMocap) {
		mocap->disconnect();
		delete mocap;
	} else {
		gazebo->disconnect();
		delete gazebo;
	}

	// Stop all vehicles
	for(int vi = 0; vi < n; vi++) {
		Vehicle *v = vehicles[vi];
		v->disconnect();
		delete v;
	}

	player->cleanup();

	printf("Done!\n");
}
<|endoftext|>


<commit_before>#include "gui.h"

extern Adafruit_ILI9341 tft;
extern Pipeline *pipe;
TouchScreen screen = TouchScreen(XP, YP, XM, YM);

int freeRam ()
{
    extern int __heap_start, *__brkval;
    int v;
    return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

void Pipeline::segueTo(ViewController *nextController) {
    flush();
    if (_previousController != NULL) {
        delete _previousController;
        _previousController = NULL;
    }
    if (_viewController != NULL) {
        delete _viewController;
    }
    _viewController = nextController;
    _viewController->init();
}

void Pipeline::seguePopover(ViewController *popoverController) {
    flush();
    if (_previousController == NULL) {
        _previousController = _viewController;
        _previousController->deinit();
    }
    _viewController = popoverController;
    _viewController->init();
}

void Pipeline::segueBack() {
    if (_previousController == NULL) {
        // TODO: Inconsistent api
#ifdef DEBUGV
        Serial.println(F("segue back called, no previous controller"));
#endif
        return;
    }
    flush();
    if (_viewController != NULL) {
        delete _viewController;
    }
    _viewController = _previousController;
    _previousController = NULL;
    _viewController->init();
}

void Pipeline::push(tickCallback fn, void *context) {
    if (_draining) {
#ifdef DEBUGV
        Serial.println(F("ignoring push, draining"));
#endif
        return;
    }
    _callbacks[_push] = fn;
    _contexts[_push] = context;
    _push++;
    if (_push == SIZE) {
        _push = 0; // wrap
    }
#ifdef DEBUG
    if (_push == _pop) {
        Serial.println(F("overflowed Pipeline"));
    }
#endif
}

void Pipeline::tick() {
    if (_viewController) {
        _viewController->tick();
    }
    if (!_callbacks[_pop]) {
        return;
    }
    int last = _pop;
    _pop++;
    if (_pop == SIZE) {
        _pop = 0; // wrap
    }

    void *context = _contexts[last];
    _callbacks[last](context);
    _callbacks[last] = NULL;
    _contexts[last] = NULL;
}

void Pipeline::flush() {
    _draining = true;
    while (_pop != _push) {
        tick();
    }
    _draining = false;
}

ViewController::~ViewController() {}

Control::~Control() {}

void Clickable::tick() {
    if (screen.isTouching(
        _pos.x,
        _pos.y,
        _pos.x+_size.w,
        _pos.y+_size.h
    )) {
        _touching++;
        if (_touching == 1) {
            pipe->push(drawControlForwarder, this);
        }
    } else if (_touching > 0) {
        _touching = 0;
        if (_onClick != NULL) {
            pipe->push(_onClick, _onClickContext);
        }
        pipe->push(drawControlForwarder, this);
    }
}

void Clickable::then(tickCallback cb, void *context) {
    _onClick = cb;
    _onClickContext = context;
}

void Label::tick() {
    Clickable::tick();
}

void Label::draw() {
    int ox = _pos.x;
    int oy = _pos.y;
    if (_touching) {
        ox++;
        oy++;
    }
    tft.setTextColor(fontColor);
    int offset = CURSOR_Y_SMALL;
    if (fontSize == 1) {
        tft.setFont(&Inconsolata_g5pt7b);
    } else {
        tft.setFont(&Inconsolata_g8pt7b);
        offset = CURSOR_Y_LARGE;
    }
    if (centerLabel) {
        uint16_t w, h;
        int16_t x1, y1;
        tft.getTextBounds(_label, 10, 10, &x1, &y1, &w, &h);
        int x = (int) ox + (_size.w / 2) - ((int) w / 2);
        int y = (int) oy + (_size.h / 2) - ((int) h / 2);
        tft.setCursor(x, y + offset);
        tft.print(_label);
    } else {
        tft.setCursor(ox, oy+_size.h-2);
        tft.print(_label);
    }
}

void Label::setLabel(const char *label) {
    strcpy(_label, label);
}

void Button::tick() {
    Label::tick();
}

void Button::draw() {
    if (_touching) {
        tft.fillRect(_pos.x, _pos.y, _size.w, _size.h, touchColor);
    } else {
        tft.fillRect(_pos.x-1, _pos.y-1, _size.w, _size.h, bgColor);
        // TODO: Better drop shadow
        tft.drawFastVLine(_pos.x-1+_size.w, _pos.y-1, _size.h, ILI9341_BLACK);
        tft.drawFastHLine(_pos.x-1, _pos.y-1+_size.h, _size.w, ILI9341_BLACK);
    }

    Label::draw();
}

void Textbox::setValueSuffix(const char *suffix) {
    strcpy(_valueSuffixText, suffix);
}

void Textbox::setValue(const char *val) {
    strcpy(_value, val);
}

void Textbox::setValue(long val) {
    ltoa(val, _value, 10);
}

void Textbox::setValue(double val) {
    String str = String(val);
    strcpy(_value, str.c_str());
}

const char *Textbox::getValue() {
    return _value;
}

void Textbox::tick() {
    Clickable::tick();
}

void Textbox::draw() {
    if (_fillBackground) {
        tft.fillRect(_pos.x, _pos.y, _size.w, _size.h, bgColor);
        _fillBackground = false;
    }
    if (fontSize == 1) {
        tft.setFont(&Inconsolata_g5pt7b);
    } else {
        tft.setFont(&Inconsolata_g8pt7b);
    }

    const char *valStr = _value;
    int valLength = strlen(valStr);
    if (_lastValLength > 0) {
        if (strcmp(_lastVal, valStr) == 0) {
            return;
        }

        tft.setCursor(_pos.x, _pos.y +_size.h-2);
        tft.setTextColor(bgColor);
        tft.print(_lastVal);

//        if (valLength != _lastValLength && _valueSuffixText[0] != 0) {
//            tft.setFont(&Inconsolata_g5pt7b);
//            tft.print(_valueSuffixText);
//        }
    }

    tft.setCursor(_pos.x, _pos.y +_size.h-2);
    tft.setTextColor(ILI9341_WHITE);
    tft.print(valStr);
    tft.setFont(&Inconsolata_g5pt7b);
//    if (valLength != _lastValLength) {
//        tft.setFont(&Inconsolata_g5pt7b);
//        tft.print(_valueSuffixText);
//    }
    strcpy(_lastVal, valStr);
    _lastValLength = valLength;
}

void drawControlForwarder(void *context) {
    static_cast<Control*>(context)->draw();
}

void drawViewControllerForwarder(void *context) {
    static_cast<ViewController*>(context)->draw();
}<commit_msg>Fixed failing handset build.<commit_after>#include "gui.h"

extern Adafruit_ILI9341 tft;
extern Pipeline *pipe;
TouchScreen screen = TouchScreen(XP, YP, XM, YM);

void Pipeline::segueTo(ViewController *nextController) {
    flush();
    if (_previousController != NULL) {
        delete _previousController;
        _previousController = NULL;
    }
    if (_viewController != NULL) {
        delete _viewController;
    }
    _viewController = nextController;
    _viewController->init();
}

void Pipeline::seguePopover(ViewController *popoverController) {
    flush();
    if (_previousController == NULL) {
        _previousController = _viewController;
        _previousController->deinit();
    }
    _viewController = popoverController;
    _viewController->init();
}

void Pipeline::segueBack() {
    if (_previousController == NULL) {
        // TODO: Inconsistent api
#ifdef DEBUGV
        Serial.println(F("segue back called, no previous controller"));
#endif
        return;
    }
    flush();
    if (_viewController != NULL) {
        delete _viewController;
    }
    _viewController = _previousController;
    _previousController = NULL;
    _viewController->init();
}

void Pipeline::push(tickCallback fn, void *context) {
    if (_draining) {
#ifdef DEBUGV
        Serial.println(F("ignoring push, draining"));
#endif
        return;
    }
    _callbacks[_push] = fn;
    _contexts[_push] = context;
    _push++;
    if (_push == SIZE) {
        _push = 0; // wrap
    }
#ifdef DEBUG
    if (_push == _pop) {
        Serial.println(F("overflowed Pipeline"));
    }
#endif
}

void Pipeline::tick() {
    if (_viewController) {
        _viewController->tick();
    }
    if (!_callbacks[_pop]) {
        return;
    }
    int last = _pop;
    _pop++;
    if (_pop == SIZE) {
        _pop = 0; // wrap
    }

    void *context = _contexts[last];
    _callbacks[last](context);
    _callbacks[last] = NULL;
    _contexts[last] = NULL;
}

void Pipeline::flush() {
    _draining = true;
    while (_pop != _push) {
        tick();
    }
    _draining = false;
}

ViewController::~ViewController() {}

Control::~Control() {}

void Clickable::tick() {
    if (screen.isTouching(
        _pos.x,
        _pos.y,
        _pos.x+_size.w,
        _pos.y+_size.h
    )) {
        _touching++;
        if (_touching == 1) {
            pipe->push(drawControlForwarder, this);
        }
    } else if (_touching > 0) {
        _touching = 0;
        if (_onClick != NULL) {
            pipe->push(_onClick, _onClickContext);
        }
        pipe->push(drawControlForwarder, this);
    }
}

void Clickable::then(tickCallback cb, void *context) {
    _onClick = cb;
    _onClickContext = context;
}

void Label::tick() {
    Clickable::tick();
}

void Label::draw() {
    int ox = _pos.x;
    int oy = _pos.y;
    if (_touching) {
        ox++;
        oy++;
    }
    tft.setTextColor(fontColor);
    int offset = CURSOR_Y_SMALL;
    if (fontSize == 1) {
        tft.setFont(&Inconsolata_g5pt7b);
    } else {
        tft.setFont(&Inconsolata_g8pt7b);
        offset = CURSOR_Y_LARGE;
    }
    if (centerLabel) {
        uint16_t w, h;
        int16_t x1, y1;
        tft.getTextBounds(_label, 10, 10, &x1, &y1, &w, &h);
        int x = (int) ox + (_size.w / 2) - ((int) w / 2);
        int y = (int) oy + (_size.h / 2) - ((int) h / 2);
        tft.setCursor(x, y + offset);
        tft.print(_label);
    } else {
        tft.setCursor(ox, oy+_size.h-2);
        tft.print(_label);
    }
}

void Label::setLabel(const char *label) {
    strcpy(_label, label);
}

void Button::tick() {
    Label::tick();
}

void Button::draw() {
    if (_touching) {
        tft.fillRect(_pos.x, _pos.y, _size.w, _size.h, touchColor);
    } else {
        tft.fillRect(_pos.x-1, _pos.y-1, _size.w, _size.h, bgColor);
        // TODO: Better drop shadow
        tft.drawFastVLine(_pos.x-1+_size.w, _pos.y-1, _size.h, ILI9341_BLACK);
        tft.drawFastHLine(_pos.x-1, _pos.y-1+_size.h, _size.w, ILI9341_BLACK);
    }

    Label::draw();
}

void Textbox::setValueSuffix(const char *suffix) {
    strcpy(_valueSuffixText, suffix);
}

void Textbox::setValue(const char *val) {
    strcpy(_value, val);
}

void Textbox::setValue(long val) {
    ltoa(val, _value, 10);
}

void Textbox::setValue(double val) {
    String str = String(val);
    strcpy(_value, str.c_str());
}

const char *Textbox::getValue() {
    return _value;
}

void Textbox::tick() {
    Clickable::tick();
}

void Textbox::draw() {
    if (_fillBackground) {
        tft.fillRect(_pos.x, _pos.y, _size.w, _size.h, bgColor);
        _fillBackground = false;
    }
    if (fontSize == 1) {
        tft.setFont(&Inconsolata_g5pt7b);
    } else {
        tft.setFont(&Inconsolata_g8pt7b);
    }

    const char *valStr = _value;
    int valLength = strlen(valStr);
    if (_lastValLength > 0) {
        if (strcmp(_lastVal, valStr) == 0) {
            return;
        }

        tft.setCursor(_pos.x, _pos.y +_size.h-2);
        tft.setTextColor(bgColor);
        tft.print(_lastVal);

//        if (valLength != _lastValLength && _valueSuffixText[0] != 0) {
//            tft.setFont(&Inconsolata_g5pt7b);
//            tft.print(_valueSuffixText);
//        }
    }

    tft.setCursor(_pos.x, _pos.y +_size.h-2);
    tft.setTextColor(ILI9341_WHITE);
    tft.print(valStr);
    tft.setFont(&Inconsolata_g5pt7b);
//    if (valLength != _lastValLength) {
//        tft.setFont(&Inconsolata_g5pt7b);
//        tft.print(_valueSuffixText);
//    }
    strcpy(_lastVal, valStr);
    _lastValLength = valLength;
}

void drawControlForwarder(void *context) {
    static_cast<Control*>(context)->draw();
}

void drawViewControllerForwarder(void *context) {
    static_cast<ViewController*>(context)->draw();
}<|endoftext|>