<commit_before>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "http_server/http_server.hxx"
#include "http_server/Request.hxx"
#include "http_server/Handler.hxx"
#include "http_headers.hxx"
#include "duplex.hxx"
#include "direct.hxx"
#include "istream/sink_null.hxx"
#include "istream/ByteIstream.hxx"
#include "istream/DelayedIstream.hxx"
#include "istream/HeadIstream.hxx"
#include "istream/istream_hold.hxx"
#include "istream/istream_memory.hxx"
#include "istream/ZeroIstream.hxx"
#include "istream/istream.hxx"
#include "istream/UnusedHoldPtr.hxx"
#include "PInstance.hxx"
#include "pool/pool.hxx"
#include "event/TimerEvent.hxx"
#include "event/ShutdownListener.hxx"
#include "fb_pool.hxx"
#include "net/SocketDescriptor.hxx"
#include "util/Cancellable.hxx"
#include "util/PrintException.hxx"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Instance final : PInstance, HttpServerConnectionHandler, Cancellable {
    ShutdownListener shutdown_listener;

    enum class Mode {
        MODE_NULL,
        MIRROR,

        /**
         * Response body of unknown length with keep-alive disabled.
         * Response body ends when socket is closed.
         */
        CLOSE,

        DUMMY,
        FIXED,
        HUGE_,
        HOLD,
    } mode;

    HttpServerConnection *connection;

    UnusedHoldIstreamPtr request_body;

    TimerEvent timer;

    Instance()
        :shutdown_listener(event_loop, BIND_THIS_METHOD(ShutdownCallback)),
         timer(event_loop, BIND_THIS_METHOD(OnTimer)) {}

    void ShutdownCallback();

    void OnTimer();

    /* virtual methods from class Cancellable */
    void Cancel() noexcept override {
        request_body.Clear();
        timer.Cancel();
    }

    /* virtual methods from class HttpServerConnectionHandler */
    void HandleHttpRequest(HttpServerRequest &request,
                           CancellablePointer &cancel_ptr) override;

    void LogHttpRequest(HttpServerRequest &,
                        http_status_t, int64_t,
                        uint64_t, uint64_t) override {}

    void HttpConnectionError(std::exception_ptr e) override;
    void HttpConnectionClosed() override;
};

void
Instance::ShutdownCallback()
{
    http_server_connection_close(connection);
}

void
Instance::OnTimer()
{
    http_server_connection_close(connection);
    shutdown_listener.Disable();
}

/*
 * http_server handler
 *
 */

void
Instance::HandleHttpRequest(HttpServerRequest &request,
                            gcc_unused CancellablePointer &cancel_ptr)
{
    switch (mode) {
        http_status_t status;
        Istream *body;
        static char data[0x100];

    case Instance::Mode::MODE_NULL:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        http_server_response(&request, HTTP_STATUS_NO_CONTENT,
                             HttpHeaders(request.pool), nullptr);
        break;

    case Instance::Mode::MIRROR:
        status = request.body
            ? HTTP_STATUS_OK
            : HTTP_STATUS_NO_CONTENT;
        http_server_response(&request, status,
                             HttpHeaders(request.pool),
                             std::move(request.body));
        break;

    case Instance::Mode::CLOSE:
        /* disable keep-alive */
        http_server_connection_graceful(&request.connection);

        /* fall through */

    case Instance::Mode::DUMMY:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        body = istream_head_new(request.pool,
                                istream_zero_new(request.pool),
                                256, false).Steal();
        body = istream_byte_new(request.pool, UnusedIstreamPtr(body)).Steal();

        http_server_response(&request, HTTP_STATUS_OK,
                             HttpHeaders(request.pool),
                             UnusedIstreamPtr(body));
        break;

    case Instance::Mode::FIXED:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        http_server_response(&request, HTTP_STATUS_OK, HttpHeaders(request.pool),
                             istream_memory_new(request.pool,
                                                data, sizeof(data)));
        break;

    case Instance::Mode::HUGE_:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        http_server_response(&request, HTTP_STATUS_OK,
                             HttpHeaders(request.pool),
                             istream_head_new(request.pool,
                                              istream_zero_new(request.pool),
                                              512 * 1024, true));
        break;

    case Instance::Mode::HOLD:
        request_body = UnusedHoldIstreamPtr(request.pool,
                                            std::move(request.body));

        {
            auto delayed = istream_delayed_new(request.pool, event_loop);
            delayed.second.cancel_ptr = *this;

            http_server_response(&request, HTTP_STATUS_OK,
                                 HttpHeaders(request.pool),
                                 std::move(delayed.first));
        }

        static constexpr struct timeval t{0,0};
        timer.Add(t);
        break;
    }
}

void
Instance::HttpConnectionError(std::exception_ptr e)
{
    timer.Cancel();
    shutdown_listener.Disable();

    PrintException(e);
}

void
Instance::HttpConnectionClosed()
{
    timer.Cancel();
    shutdown_listener.Disable();
}

/*
 * main
 *
 */

int main(int argc, char **argv) {
    if (argc != 4) {
        fprintf(stderr, "Usage: %s INFD OUTFD {null|mirror|close|dummy|fixed|huge|hold}\n", argv[0]);
        return EXIT_FAILURE;
    }

    int in_fd, out_fd;

    if (strcmp(argv[1], "accept") == 0) {
        const int listen_fd = atoi(argv[2]);
        in_fd = out_fd = accept(listen_fd, nullptr, 0);
        if (in_fd < 0) {
            perror("accept() failed");
            return EXIT_FAILURE;
        }
    } else {
        in_fd = atoi(argv[1]);
        out_fd = atoi(argv[2]);
    }

    direct_global_init();
    const ScopeFbPoolInit fb_pool_init;

    Instance instance;
    instance.shutdown_listener.Enable();

    int sockfd;
    if (in_fd != out_fd) {
        sockfd = duplex_new(instance.event_loop, instance.root_pool,
                            in_fd, out_fd);
        if (sockfd < 0) {
            perror("duplex_new() failed");
            exit(2);
        }
    } else
        sockfd = in_fd;

    const char *mode = argv[3];
    if (strcmp(mode, "null") == 0)
        instance.mode = Instance::Mode::MODE_NULL;
    else if (strcmp(mode, "mirror") == 0)
        instance.mode = Instance::Mode::MIRROR;
    else if (strcmp(mode, "close") == 0)
        instance.mode = Instance::Mode::CLOSE;
    else if (strcmp(mode, "dummy") == 0)
        instance.mode = Instance::Mode::DUMMY;
    else if (strcmp(mode, "fixed") == 0)
        instance.mode = Instance::Mode::FIXED;
    else if (strcmp(mode, "huge") == 0)
        instance.mode = Instance::Mode::HUGE_;
    else if (strcmp(mode, "hold") == 0)
        instance.mode = Instance::Mode::HOLD;
    else {
        fprintf(stderr, "Unknown mode: %s\n", mode);
        return EXIT_FAILURE;
    }

    instance.connection = http_server_connection_new(instance.root_pool,
                                                     instance.event_loop,
                                                     SocketDescriptor(sockfd),
                                                     FdType::FD_SOCKET,
                                                     nullptr, nullptr,
                                                     nullptr, nullptr,
                                                     true, instance);

    instance.event_loop.Dispatch();

    return EXIT_SUCCESS;
}
<commit_msg>test/run_http_server: use class UnusedIstreamPtr<commit_after>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "http_server/http_server.hxx"
#include "http_server/Request.hxx"
#include "http_server/Handler.hxx"
#include "http_headers.hxx"
#include "duplex.hxx"
#include "direct.hxx"
#include "istream/sink_null.hxx"
#include "istream/ByteIstream.hxx"
#include "istream/DelayedIstream.hxx"
#include "istream/HeadIstream.hxx"
#include "istream/istream_hold.hxx"
#include "istream/istream_memory.hxx"
#include "istream/ZeroIstream.hxx"
#include "istream/istream.hxx"
#include "istream/UnusedHoldPtr.hxx"
#include "PInstance.hxx"
#include "pool/pool.hxx"
#include "event/TimerEvent.hxx"
#include "event/ShutdownListener.hxx"
#include "fb_pool.hxx"
#include "net/SocketDescriptor.hxx"
#include "util/Cancellable.hxx"
#include "util/PrintException.hxx"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Instance final : PInstance, HttpServerConnectionHandler, Cancellable {
    ShutdownListener shutdown_listener;

    enum class Mode {
        MODE_NULL,
        MIRROR,

        /**
         * Response body of unknown length with keep-alive disabled.
         * Response body ends when socket is closed.
         */
        CLOSE,

        DUMMY,
        FIXED,
        HUGE_,
        HOLD,
    } mode;

    HttpServerConnection *connection;

    UnusedHoldIstreamPtr request_body;

    TimerEvent timer;

    Instance()
        :shutdown_listener(event_loop, BIND_THIS_METHOD(ShutdownCallback)),
         timer(event_loop, BIND_THIS_METHOD(OnTimer)) {}

    void ShutdownCallback();

    void OnTimer();

    /* virtual methods from class Cancellable */
    void Cancel() noexcept override {
        request_body.Clear();
        timer.Cancel();
    }

    /* virtual methods from class HttpServerConnectionHandler */
    void HandleHttpRequest(HttpServerRequest &request,
                           CancellablePointer &cancel_ptr) override;

    void LogHttpRequest(HttpServerRequest &,
                        http_status_t, int64_t,
                        uint64_t, uint64_t) override {}

    void HttpConnectionError(std::exception_ptr e) override;
    void HttpConnectionClosed() override;
};

void
Instance::ShutdownCallback()
{
    http_server_connection_close(connection);
}

void
Instance::OnTimer()
{
    http_server_connection_close(connection);
    shutdown_listener.Disable();
}

/*
 * http_server handler
 *
 */

void
Instance::HandleHttpRequest(HttpServerRequest &request,
                            gcc_unused CancellablePointer &cancel_ptr)
{
    switch (mode) {
        http_status_t status;
        static char data[0x100];

    case Instance::Mode::MODE_NULL:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        http_server_response(&request, HTTP_STATUS_NO_CONTENT,
                             HttpHeaders(request.pool), nullptr);
        break;

    case Instance::Mode::MIRROR:
        status = request.body
            ? HTTP_STATUS_OK
            : HTTP_STATUS_NO_CONTENT;
        http_server_response(&request, status,
                             HttpHeaders(request.pool),
                             std::move(request.body));
        break;

    case Instance::Mode::CLOSE:
        /* disable keep-alive */
        http_server_connection_graceful(&request.connection);

        /* fall through */

    case Instance::Mode::DUMMY:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        {
            auto body = istream_head_new(request.pool,
                                         istream_zero_new(request.pool),
                                         256, false);
            body = istream_byte_new(request.pool, std::move(body));

            http_server_response(&request, HTTP_STATUS_OK,
                                 HttpHeaders(request.pool),
                                 std::move(body));
        }

        break;

    case Instance::Mode::FIXED:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        http_server_response(&request, HTTP_STATUS_OK, HttpHeaders(request.pool),
                             istream_memory_new(request.pool,
                                                data, sizeof(data)));
        break;

    case Instance::Mode::HUGE_:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        http_server_response(&request, HTTP_STATUS_OK,
                             HttpHeaders(request.pool),
                             istream_head_new(request.pool,
                                              istream_zero_new(request.pool),
                                              512 * 1024, true));
        break;

    case Instance::Mode::HOLD:
        request_body = UnusedHoldIstreamPtr(request.pool,
                                            std::move(request.body));

        {
            auto delayed = istream_delayed_new(request.pool, event_loop);
            delayed.second.cancel_ptr = *this;

            http_server_response(&request, HTTP_STATUS_OK,
                                 HttpHeaders(request.pool),
                                 std::move(delayed.first));
        }

        static constexpr struct timeval t{0,0};
        timer.Add(t);
        break;
    }
}

void
Instance::HttpConnectionError(std::exception_ptr e)
{
    timer.Cancel();
    shutdown_listener.Disable();

    PrintException(e);
}

void
Instance::HttpConnectionClosed()
{
    timer.Cancel();
    shutdown_listener.Disable();
}

/*
 * main
 *
 */

int main(int argc, char **argv) {
    if (argc != 4) {
        fprintf(stderr, "Usage: %s INFD OUTFD {null|mirror|close|dummy|fixed|huge|hold}\n", argv[0]);
        return EXIT_FAILURE;
    }

    int in_fd, out_fd;

    if (strcmp(argv[1], "accept") == 0) {
        const int listen_fd = atoi(argv[2]);
        in_fd = out_fd = accept(listen_fd, nullptr, 0);
        if (in_fd < 0) {
            perror("accept() failed");
            return EXIT_FAILURE;
        }
    } else {
        in_fd = atoi(argv[1]);
        out_fd = atoi(argv[2]);
    }

    direct_global_init();
    const ScopeFbPoolInit fb_pool_init;

    Instance instance;
    instance.shutdown_listener.Enable();

    int sockfd;
    if (in_fd != out_fd) {
        sockfd = duplex_new(instance.event_loop, instance.root_pool,
                            in_fd, out_fd);
        if (sockfd < 0) {
            perror("duplex_new() failed");
            exit(2);
        }
    } else
        sockfd = in_fd;

    const char *mode = argv[3];
    if (strcmp(mode, "null") == 0)
        instance.mode = Instance::Mode::MODE_NULL;
    else if (strcmp(mode, "mirror") == 0)
        instance.mode = Instance::Mode::MIRROR;
    else if (strcmp(mode, "close") == 0)
        instance.mode = Instance::Mode::CLOSE;
    else if (strcmp(mode, "dummy") == 0)
        instance.mode = Instance::Mode::DUMMY;
    else if (strcmp(mode, "fixed") == 0)
        instance.mode = Instance::Mode::FIXED;
    else if (strcmp(mode, "huge") == 0)
        instance.mode = Instance::Mode::HUGE_;
    else if (strcmp(mode, "hold") == 0)
        instance.mode = Instance::Mode::HOLD;
    else {
        fprintf(stderr, "Unknown mode: %s\n", mode);
        return EXIT_FAILURE;
    }

    instance.connection = http_server_connection_new(instance.root_pool,
                                                     instance.event_loop,
                                                     SocketDescriptor(sockfd),
                                                     FdType::FD_SOCKET,
                                                     nullptr, nullptr,
                                                     nullptr, nullptr,
                                                     true, instance);

    instance.event_loop.Dispatch();

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>#include <catch.hpp>
#include <rapidcheck-catch.h>

#include "rapidcheck/seq/Create.h"
#include "rapidcheck/seq/Transform.h"

#include "util/CopyGuard.h"
#include "util/Meta.h"
#include "util/Util.h"
#include "util/TypeListMacros.h"
#include "util/SeqUtils.h"

using namespace rc;
using namespace rc::test;

TEST_CASE("seq::repeat") {
    prop("repeatedly returns the given value",
         [](const std::string &value) {
             auto seq = seq::repeat(value);
             for (int i = 0; i < 2000; i++) {
                 auto x = seq.next();
                 RC_ASSERT(x);
                 RC_ASSERT(*x == value);
             }
         });

    prop("copies are equal",
         [](const std::string &value) {
             assertEqualCopies(seq::take(200, seq::repeat(value)));
         });

    prop("does not copy value on construction",
         [](CopyGuard guard) {
             auto seq = seq::repeat(std::move(guard));
         });
}

TEST_CASE("seq::just") {
    SECTION("does not copy values") {
        auto seq = seq::just(CopyGuard(), CopyGuard(), CopyGuard());
        seq.next();
        seq.next();
        seq.next();
    }

    prop("returns the passed in objects",
         [](const std::string &a,
             const std::string &b,
             const std::string &c)
         {
             auto seq = seq::just(a, b, c);
             RC_ASSERT(*seq.next() == a);
             RC_ASSERT(*seq.next() == b);
             RC_ASSERT(*seq.next() == c);
             RC_ASSERT(!seq.next());
         });

    prop("copies are equal",
         [](const std::string &a,
             const std::string &b,
             const std::string &c)
         {
             assertEqualCopies(seq::just(a, b, c));
         });
}

struct FromContainerTests
{
    template<typename T>
    static void exec()
    {
        templatedProp<T>(
            "the returned elements are equal to the elements"
            " returned by iterating",
            [](const T &elements) {
                auto seq = seq::fromContainer(elements);
                for (const auto &x : elements) {
                    RC_ASSERT(*seq.next() == x);
                }
                RC_ASSERT(!seq.next());
            });

        templatedProp<T>(
            "copies are equal",
            [](const T &elements) {
                assertEqualCopies(seq::fromContainer(elements));
            });
    }
};

struct FromContainerCopyTests
{
    template<typename T>
    static void exec()
    {
        templatedProp<T>(
            "does not copy elements",
            [](T elements) {
                auto seq = seq::fromContainer(std::move(elements));
                while (seq.next());
            });
    }
};

TEST_CASE("seq::fromContainer") {
    meta::forEachType<FromContainerTests,
                      RC_ORDERED_CONTAINERS(std::string),
                      RC_STRING_TYPES,
                      std::array<std::string, 100>>();

    // TODO Weirdly arbitrary to run this for a category called
    // RC_SEQUENCE_CONTAINERS
    meta::forEachType<FromContainerCopyTests,
                      RC_SEQUENCE_CONTAINERS(CopyGuard),
                      std::array<CopyGuard, 100>>();
}

TEST_CASE("seq::fromIteratorRange") {
    prop("creates a sequence that returns the values in the range",
         [] (const std::vector<int> &elements) {
             const int r1 = *gen::ranged<int>(0, elements.size() + 1);
             const int r2 = *gen::ranged<int>(0, elements.size() + 1);
             const int start = std::min(r1, r2);
             const int end = std::max(r1, r2);
             const auto startIt = elements.begin() + start;
             const auto endIt = elements.begin() + end;
             std::vector<int> subElements(startIt, endIt);
             RC_ASSERT(seq::fromIteratorRange(startIt, endIt) ==
                       seq::fromContainer(subElements));
         });
}

TEST_CASE("seq::iterate") {
    prop("returns an infinite sequence of applying the given function to the"
         " value",
         [](int start, int inc) {
             const auto func = [=](int x) { return x + inc; };
             auto seq = seq::iterate(start, func);
             int x = start;
             // Arbitrary number of iterations, sequence is infinite
             for (int i = 0; i < 1000; i++) {
                 RC_ASSERT(*seq.next() == x);
                 x = func(x);
             }
         });

    prop("copies are equal",
         [](int start, int inc) {
             const auto func = [=](int x) { return x + inc; };
             assertEqualCopies(seq::take(1000, seq::iterate(start, func)));
         });
}

TEST_CASE("seq::range") {
    auto smallInt = gen::scale(0.25, gen::arbitrary<int>());

    prop("returns a sequence from start to end",
         [&]{
             int start = *smallInt;
             int end = *smallInt;

             auto seq = seq::range(start, end);
             int inc = (start < end) ? 1 : -1;
             int i = start;
             while (i != end) {
                 auto x = seq.next();
                 RC_ASSERT(x);
                 RC_ASSERT(*x == i);
                 i += inc;
             }
             RC_ASSERT(!seq.next());
         });

    prop("copies are equal",
         [&]{ assertEqualCopies(seq::range(*smallInt, *smallInt)); });
}

TEST_CASE("seq::index") {
    prop("returns an infinite sequence of increasing indexes from 0",
         []{
             auto seq = seq::index();
             for (std::size_t i = 0; i < 2000; i++) {
                 auto x = seq.next();
                 RC_ASSERT(x);
                 RC_ASSERT(*x == i);
             }
         });

    prop("copies are equal",
         []{ assertEqualCopies(seq::take(2000, seq::index())); });
}
<commit_msg>Move seq/CreateTests to new framework<commit_after>#include <catch.hpp>
#include <rapidcheck-catch.h>

#include "rapidcheck/seq/Create.h"
#include "rapidcheck/seq/Transform.h"

#include "util/CopyGuard.h"
#include "util/Meta.h"
#include "util/Util.h"
#include "util/TypeListMacros.h"
#include "util/SeqUtils.h"

using namespace rc;
using namespace rc::test;

TEST_CASE("seq::repeat") {
    newprop(
        "repeatedly returns the given value",
        [](const std::string &value) {
            auto seq = seq::repeat(value);
            for (int i = 0; i < 2000; i++) {
                auto x = seq.next();
                RC_ASSERT(x);
                RC_ASSERT(*x == value);
            }
        });

    newprop(
        "copies are equal",
        [](const std::string &value) {
            assertEqualCopies(seq::take(200, seq::repeat(value)));
        });

    newprop(
        "does not copy value on construction",
        [](CopyGuard guard) {
            auto seq = seq::repeat(std::move(guard));
        });
}

TEST_CASE("seq::just") {
    SECTION("does not copy values") {
        auto seq = seq::just(CopyGuard(), CopyGuard(), CopyGuard());
        seq.next();
        seq.next();
        seq.next();
    }

    newprop(
        "returns the passed in objects",
        [](const std::string &a,
           const std::string &b,
           const std::string &c)
        {
            auto seq = seq::just(a, b, c);
            RC_ASSERT(*seq.next() == a);
            RC_ASSERT(*seq.next() == b);
            RC_ASSERT(*seq.next() == c);
            RC_ASSERT(!seq.next());
        });

    newprop(
        "copies are equal",
        [](const std::string &a,
           const std::string &b,
           const std::string &c)
        {
            assertEqualCopies(seq::just(a, b, c));
        });
}

struct FromContainerTests
{
    template<typename T>
    static void exec()
    {
        newtemplatedProp<T>(
            "the returned elements are equal to the elements"
            " returned by iterating",
            [](const T &elements) {
                auto seq = seq::fromContainer(elements);
                for (const auto &x : elements) {
                    RC_ASSERT(*seq.next() == x);
                }
                RC_ASSERT(!seq.next());
            });

        newtemplatedProp<T>(
            "copies are equal",
            [](const T &elements) {
                assertEqualCopies(seq::fromContainer(elements));
            });
    }
};

struct FromContainerCopyTests
{
    template<typename T>
    static void exec()
    {
        newtemplatedProp<T>(
            "does not copy elements",
            [](T elements) {
                auto seq = seq::fromContainer(std::move(elements));
                while (seq.next());
            });
    }
};

TEST_CASE("seq::fromContainer") {
    meta::forEachType<FromContainerTests,
                      RC_ORDERED_CONTAINERS(std::string),
                      RC_STRING_TYPES,
                      std::array<std::string, 100>>();

    // TODO Weirdly arbitrary to run this for a category called
    // RC_SEQUENCE_CONTAINERS
    meta::forEachType<FromContainerCopyTests,
                      RC_SEQUENCE_CONTAINERS(CopyGuard),
                      std::array<CopyGuard, 100>>();
}

TEST_CASE("seq::fromIteratorRange") {
    newprop(
        "creates a sequence that returns the values in the range",
        [] (const std::vector<int> &elements) {
            const int r1 = *newgen::inRange<int>(0, elements.size() + 1);
            const int r2 = *newgen::inRange<int>(0, elements.size() + 1);
            const int start = std::min(r1, r2);
            const int end = std::max(r1, r2);
            const auto startIt = elements.begin() + start;
            const auto endIt = elements.begin() + end;
            std::vector<int> subElements(startIt, endIt);
            RC_ASSERT(seq::fromIteratorRange(startIt, endIt) ==
                      seq::fromContainer(subElements));
        });
}

TEST_CASE("seq::iterate") {
    newprop(
        "returns an infinite sequence of applying the given function to the"
        " value",
        [](int start, int inc) {
            const auto func = [=](int x) { return x + inc; };
            auto seq = seq::iterate(start, func);
            int x = start;
            // Arbitrary number of iterations, sequence is infinite
            for (int i = 0; i < 1000; i++) {
                RC_ASSERT(*seq.next() == x);
                x = func(x);
            }
        });

    newprop(
        "copies are equal",
        [](int start, int inc) {
            const auto func = [=](int x) { return x + inc; };
            assertEqualCopies(seq::take(1000, seq::iterate(start, func)));
        });
}

TEST_CASE("seq::range") {
    static const auto smallInt = newgen::scale(0.25, newgen::arbitrary<int>());

    newprop(
        "returns a sequence from start to end",
        []{
            int start = *smallInt;
            int end = *smallInt;

            auto seq = seq::range(start, end);
            int inc = (start < end) ? 1 : -1;
            int i = start;
            while (i != end) {
                auto x = seq.next();
                RC_ASSERT(x);
                RC_ASSERT(*x == i);
                i += inc;
            }
            RC_ASSERT(!seq.next());
        });

    newprop(
        "copies are equal",
        [&]{ assertEqualCopies(seq::range(*smallInt, *smallInt)); });
}

TEST_CASE("seq::index") {
    newprop(
        "returns an infinite sequence of increasing indexes from 0",
        []{
            auto seq = seq::index();
            for (std::size_t i = 0; i < 2000; i++) {
                auto x = seq.next();
                RC_ASSERT(x);
                RC_ASSERT(*x == i);
            }
        });

    newprop(
        "copies are equal",
        []{ assertEqualCopies(seq::take(2000, seq::index())); });
}
<|endoftext|>


<commit_before>// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef rr_Routine_hpp
#define rr_Routine_hpp

#include <memory>

namespace rr {

class Routine
{
public:
	Routine() = default;
	virtual ~Routine() = default;

	virtual const void *getEntry(int index = 0) const = 0;
};

// RoutineT is a type-safe wrapper around a Routine and its function entry, returned by FunctionT
template<typename FunctionType>
class RoutineT;

template<typename Return, typename... Arguments>
class RoutineT<Return(Arguments...)>
{
public:
	using FunctionType = Return (*)(Arguments...);

	RoutineT() = default;

	explicit RoutineT(const std::shared_ptr<Routine> &routine)
	    : routine(routine)
	{
		if(routine)
		{
			function = reinterpret_cast<FunctionType>(const_cast<void *>(routine->getEntry(0)));
		}
	}

	operator bool() const
	{
		return function != nullptr;
	}

	template<typename... Args>
	Return operator()(Args... args) const
	{
		return function(args...);
	}

	const FunctionType getEntry() const
	{
		return function;
	}

private:
	std::shared_ptr<Routine> routine;
	FunctionType function = nullptr;
};

}  // namespace rr

#endif  // rr_Routine_hpp
<commit_msg>Work around MSan false positives for Reactor routine parameters<commit_after>// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef rr_Routine_hpp
#define rr_Routine_hpp

// A Clang extension to determine compiler features.
// We use it to detect Sanitizer builds (e.g. -fsanitize=memory).
#ifndef __has_feature
#	define __has_feature(x) 0
#endif

#if __has_feature(memory_sanitizer)
#	include "sanitizer/msan_interface.h"
#endif

#include <memory>

namespace rr {

class Routine
{
public:
	Routine() = default;
	virtual ~Routine() = default;

	virtual const void *getEntry(int index = 0) const = 0;
};

// RoutineT is a type-safe wrapper around a Routine and its function entry, returned by FunctionT
template<typename FunctionType>
class RoutineT;

template<typename Return, typename... Arguments>
class RoutineT<Return(Arguments...)>
{
public:
	using FunctionType = Return (*)(Arguments...);

	RoutineT() = default;

	explicit RoutineT(const std::shared_ptr<Routine> &routine)
	    : routine(routine)
	{
		if(routine)
		{
			function = reinterpret_cast<FunctionType>(const_cast<void *>(routine->getEntry(0)));
		}
	}

	operator bool() const
	{
		return function != nullptr;
	}

	template<typename... Args>
	Return operator()(Args... args) const
	{
#if __has_feature(memory_sanitizer)
		// TODO(b/228253151): Fix support for detecting uninitialized parameters.
		__msan_unpoison_param(sizeof...(args));
#endif

		return function(args...);
	}

	const FunctionType getEntry() const
	{
		return function;
	}

private:
	std::shared_ptr<Routine> routine;
	FunctionType function = nullptr;
};

}  // namespace rr

#endif  // rr_Routine_hpp
<|endoftext|>


<commit_before>/*
Copyright (c) 2015-2017 Alternative Games Ltd / Turo Lamminen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "RendererInternal.h"
#include "Utils.h"

#include <algorithm>

#include <shaderc/shaderc.hpp>

void RendererImpl::SDLPrefDirDel::operator()(char *ptr) const {
	SDL_free(ptr);
}

class Includer final : public shaderc::CompileOptions::IncluderInterface {
	std::unordered_map<std::string, std::vector<char> > cache;

	virtual shaderc_include_result* GetInclude(const char* requested_source, shaderc_include_type /* type */, const char* /* requesting_source */, size_t /* include_depth */) {
		std::string filename(requested_source);

		// std::unordered_map<std::string, std::vector<char> >::iterator it = cache.find(filename);
		auto it = cache.find(filename);
		if (it == cache.end()) {
			auto contents = readFile(requested_source);
			bool inserted = false;
			std::tie(it, inserted) = cache.emplace(std::move(filename), std::move(contents));
			// since we just checked it's not there this must succeed
			assert(inserted);
		}

		auto data = new shaderc_include_result;
		data->source_name         = it->first.c_str();
		data->source_name_length  = it->first.size();
		data->content             = it->second.data();
		data->content_length      = it->second.size();
		data->user_data           = nullptr;

		return data;
	}

	virtual void ReleaseInclude(shaderc_include_result* data) {
		// no need to delete any of data's contents, they're owned by this class
		delete data;
	}
};

std::vector<char> RendererImpl::loadSource(const std::string &name) {
	auto it = shaderSources.find(name);
	if (it != shaderSources.end()) {
		return it->second;
	} else {
		auto source = readFile(name);
		shaderSources.emplace(name, source);
		return source;
	}
}

std::vector<uint32_t> RendererImpl::compileSpirv(const std::string &name, const ShaderMacros &macros, shaderc_shader_kind kind) {
	// check spir-v cache first

	std::string spvName = std::string(spirvCacheDir.get()) + name;
	{
		std::vector<std::string> sorted;
		sorted.reserve(macros.size());
		for (const auto &macro : macros) {
			std::string s = macro.first;
			if (!macro.second.empty()) {
				s += "=";
				s += macro.second;
			}
			sorted.emplace_back(std::move(s));
		}

		std::sort(sorted.begin(), sorted.end());
		for (const auto &s : sorted) {
			spvName += "_" + s;
		}
	}
	spvName = spvName + ".spv";

	// TODO: check timestamp against source file
	// TODO: should also check headers included during original compilation
	if (fileExists(spvName)) {
		auto temp = readFile(spvName);
		if (temp.size() % 4 == 0) {
			std::vector<uint32_t> spirv;
			spirv.resize(temp.size() / 4);
			memcpy(&spirv[0], &temp[0], temp.size());
			printf("Loaded shader \"%s\" from cache\n", spvName.c_str());

			return spirv;
		}

		// incorrect size...
	}

	auto src = loadSource(name);

	shaderc::CompileOptions options;
	// TODO: optimization level?
	// TODO: cache includes globally
	options.SetIncluder(std::make_unique<Includer>());

	for (const auto &p : macros) {
		options.AddMacroDefinition(p.first, p.second);
	}

	shaderc::Compiler compiler;
	auto result = compiler.CompileGlslToSpv(&src[0], src.size(), kind, name.c_str(), options);
	if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
		printf("Shader %s compile failed: %s\n", name.c_str(), result.GetErrorMessage().c_str());
		throw std::runtime_error("Shader compile failed");
	}

	std::vector<uint32_t> spirv(result.cbegin(), result.cend());

	writeFile(spvName, &spirv[0], spirv.size() * 4);

	return spirv;
}

Renderer Renderer::createRenderer(const RendererDesc &desc) {
	return Renderer(new RendererImpl(desc));
}

Renderer &Renderer::operator=(Renderer &&other) {
	assert(impl == nullptr);
	impl = other.impl;
	other.impl = nullptr;

	return *this;
}

Renderer::Renderer(Renderer &&other)
: impl(other.impl)
{
	other.impl = nullptr;
}

Renderer::Renderer()
: impl(nullptr)
{
}

Renderer::Renderer(RendererImpl *impl_)
: impl(impl_)
{
	assert(impl != nullptr);
}

Renderer::~Renderer() {
	if (impl) {
		delete impl;
		impl = nullptr;
	}
}

BufferHandle Renderer::createBuffer(uint32_t size, const void *contents) {
	return impl->createBuffer(size, contents);
}

BufferHandle Renderer::createEphemeralBuffer(uint32_t size, const void *contents) {
	return impl->createEphemeralBuffer(size, contents);
}

FramebufferHandle Renderer::createFramebuffer(const FramebufferDesc &desc) {
	return impl->createFramebuffer(desc);
}

RenderPassHandle Renderer::createRenderPass(const RenderPassDesc &desc) {
	return impl->createRenderPass(desc);
}

PipelineHandle Renderer::createPipeline(const PipelineDesc &desc) {
	return impl->createPipeline(desc);
}

RenderTargetHandle Renderer::createRenderTarget(const RenderTargetDesc &desc) {
	return impl->createRenderTarget(desc);
}

SamplerHandle Renderer::createSampler(const SamplerDesc &desc) {
	return impl->createSampler(desc);
}

VertexShaderHandle Renderer::createVertexShader(const std::string &name, const ShaderMacros &macros) {
	return impl->createVertexShader(name, macros);
}

FragmentShaderHandle Renderer::createFragmentShader(const std::string &name, const ShaderMacros &macros) {
	return impl->createFragmentShader(name, macros);
}

TextureHandle Renderer::createTexture(const TextureDesc &desc) {
	return impl->createTexture(desc);
}

DSLayoutHandle Renderer::createDescriptorSetLayout(const DescriptorLayout *layout) {
	return impl->createDescriptorSetLayout(layout);
}

TextureHandle Renderer::getRenderTargetTexture(RenderTargetHandle handle) {
	return impl->getRenderTargetTexture(handle);
}

void Renderer::deleteBuffer(BufferHandle handle) {
	impl->deleteBuffer(handle);
}

void Renderer::deleteFramebuffer(FramebufferHandle handle) {
	impl->deleteFramebuffer(handle);
}

void Renderer::deleteRenderTarget(RenderTargetHandle &rt) {
	impl->deleteRenderTarget(rt);
}

void Renderer::deleteRenderPass(RenderPassHandle handle) {
	impl->deleteRenderPass(handle);
}

void Renderer::deleteSampler(SamplerHandle handle) {
	impl->deleteSampler(handle);
}

void Renderer::deleteTexture(TextureHandle handle) {
	impl->deleteTexture(handle);
}

void Renderer::recreateSwapchain(const SwapchainDesc &desc) {
	impl->recreateSwapchain(desc);
}

void Renderer::beginFrame() {
	impl->beginFrame();
}

void Renderer::presentFrame(RenderTargetHandle image) {
	impl->presentFrame(image);
}

void Renderer::beginRenderPass(RenderPassHandle rpHandle, FramebufferHandle fbHandle) {
	impl->beginRenderPass(rpHandle, fbHandle);
}

void Renderer::endRenderPass() {
	impl->endRenderPass();
}

void Renderer::bindPipeline(PipelineHandle pipeline) {
	impl->bindPipeline(pipeline);
}

void Renderer::bindIndexBuffer(BufferHandle buffer, bool bit16) {
	impl->bindIndexBuffer(buffer, bit16);
}

void Renderer::bindVertexBuffer(unsigned int binding, BufferHandle buffer) {
	impl->bindVertexBuffer(binding, buffer);
}

void Renderer::bindDescriptorSet(unsigned int index, DSLayoutHandle layout, const void *data) {
	impl->bindDescriptorSet(index, layout, data);
}

void Renderer::setViewport(unsigned int x, unsigned int y, unsigned int width, unsigned int height) {
	impl->setViewport(x, y, width, height);
}

void Renderer::setScissorRect(unsigned int x, unsigned int y, unsigned int width, unsigned int height) {
	impl->setScissorRect(x, y, width, height);
}

void Renderer::draw(unsigned int firstVertex, unsigned int vertexCount) {
	impl->draw(firstVertex, vertexCount);
}

void Renderer::drawIndexedInstanced(unsigned int vertexCount, unsigned int instanceCount) {
	impl->drawIndexedInstanced(vertexCount, instanceCount);
}

void Renderer::drawIndexedOffset(unsigned int vertexCount, unsigned int firstIndex) {
	impl->drawIndexedOffset(vertexCount, firstIndex);
}

unsigned int RendererImpl::ringBufferAllocate(unsigned int size, unsigned int alignment) {
	// sub-allocate from persistent coherent buffer
	// round current pointer up to necessary alignment
	const unsigned int add   = alignment - 1;
	const unsigned int mask  = ~add;
	unsigned int alignedPtr  = (ringBufPtr + add) & mask;
	assert(ringBufPtr <= alignedPtr);
	// TODO: ring buffer size should be pow2, se should use add & mask here too
	unsigned int beginPtr    =  alignedPtr % ringBufSize;

	if (beginPtr + size >= ringBufSize) {
		// we went past the end and have to go back to beginning
		// TODO: add and mask here too
		ringBufPtr = (ringBufPtr / ringBufSize + 1) * ringBufSize;
		assert((ringBufPtr & ~mask) == 0);
		alignedPtr  = (ringBufPtr + add) & mask;
		beginPtr    =  alignedPtr % ringBufSize;
		assert(beginPtr + size < ringBufSize);
		assert(beginPtr == 0);
	}
	ringBufPtr = alignedPtr + size;

	return beginPtr;
}

glm::uvec2 Renderer::getDrawableSize() const {
	return glm::uvec2(impl->swapchainDesc.width, impl->swapchainDesc.height);
}
<commit_msg>Last printf replace.<commit_after>/*
Copyright (c) 2015-2017 Alternative Games Ltd / Turo Lamminen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "RendererInternal.h"
#include "Utils.h"

#include <algorithm>

#include <shaderc/shaderc.hpp>

void RendererImpl::SDLPrefDirDel::operator()(char *ptr) const {
	SDL_free(ptr);
}

class Includer final : public shaderc::CompileOptions::IncluderInterface {
	std::unordered_map<std::string, std::vector<char> > cache;

	virtual shaderc_include_result* GetInclude(const char* requested_source, shaderc_include_type /* type */, const char* /* requesting_source */, size_t /* include_depth */) {
		std::string filename(requested_source);

		// std::unordered_map<std::string, std::vector<char> >::iterator it = cache.find(filename);
		auto it = cache.find(filename);
		if (it == cache.end()) {
			auto contents = readFile(requested_source);
			bool inserted = false;
			std::tie(it, inserted) = cache.emplace(std::move(filename), std::move(contents));
			// since we just checked it's not there this must succeed
			assert(inserted);
		}

		auto data = new shaderc_include_result;
		data->source_name         = it->first.c_str();
		data->source_name_length  = it->first.size();
		data->content             = it->second.data();
		data->content_length      = it->second.size();
		data->user_data           = nullptr;

		return data;
	}

	virtual void ReleaseInclude(shaderc_include_result* data) {
		// no need to delete any of data's contents, they're owned by this class
		delete data;
	}
};

std::vector<char> RendererImpl::loadSource(const std::string &name) {
	auto it = shaderSources.find(name);
	if (it != shaderSources.end()) {
		return it->second;
	} else {
		auto source = readFile(name);
		shaderSources.emplace(name, source);
		return source;
	}
}

std::vector<uint32_t> RendererImpl::compileSpirv(const std::string &name, const ShaderMacros &macros, shaderc_shader_kind kind) {
	// check spir-v cache first

	std::string spvName = std::string(spirvCacheDir.get()) + name;
	{
		std::vector<std::string> sorted;
		sorted.reserve(macros.size());
		for (const auto &macro : macros) {
			std::string s = macro.first;
			if (!macro.second.empty()) {
				s += "=";
				s += macro.second;
			}
			sorted.emplace_back(std::move(s));
		}

		std::sort(sorted.begin(), sorted.end());
		for (const auto &s : sorted) {
			spvName += "_" + s;
		}
	}
	spvName = spvName + ".spv";

	// TODO: check timestamp against source file
	// TODO: should also check headers included during original compilation
	if (fileExists(spvName)) {
		auto temp = readFile(spvName);
		if (temp.size() % 4 == 0) {
			std::vector<uint32_t> spirv;
			spirv.resize(temp.size() / 4);
			memcpy(&spirv[0], &temp[0], temp.size());
			LOG("Loaded shader \"%s\" from cache\n", spvName.c_str());

			return spirv;
		}

		// incorrect size...
	}

	auto src = loadSource(name);

	shaderc::CompileOptions options;
	// TODO: optimization level?
	// TODO: cache includes globally
	options.SetIncluder(std::make_unique<Includer>());

	for (const auto &p : macros) {
		options.AddMacroDefinition(p.first, p.second);
	}

	shaderc::Compiler compiler;
	auto result = compiler.CompileGlslToSpv(&src[0], src.size(), kind, name.c_str(), options);
	if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
		LOG("Shader %s compile failed: %s\n", name.c_str(), result.GetErrorMessage().c_str());
		throw std::runtime_error("Shader compile failed");
	}

	std::vector<uint32_t> spirv(result.cbegin(), result.cend());

	writeFile(spvName, &spirv[0], spirv.size() * 4);

	return spirv;
}

Renderer Renderer::createRenderer(const RendererDesc &desc) {
	return Renderer(new RendererImpl(desc));
}

Renderer &Renderer::operator=(Renderer &&other) {
	assert(impl == nullptr);
	impl = other.impl;
	other.impl = nullptr;

	return *this;
}

Renderer::Renderer(Renderer &&other)
: impl(other.impl)
{
	other.impl = nullptr;
}

Renderer::Renderer()
: impl(nullptr)
{
}

Renderer::Renderer(RendererImpl *impl_)
: impl(impl_)
{
	assert(impl != nullptr);
}

Renderer::~Renderer() {
	if (impl) {
		delete impl;
		impl = nullptr;
	}
}

BufferHandle Renderer::createBuffer(uint32_t size, const void *contents) {
	return impl->createBuffer(size, contents);
}

BufferHandle Renderer::createEphemeralBuffer(uint32_t size, const void *contents) {
	return impl->createEphemeralBuffer(size, contents);
}

FramebufferHandle Renderer::createFramebuffer(const FramebufferDesc &desc) {
	return impl->createFramebuffer(desc);
}

RenderPassHandle Renderer::createRenderPass(const RenderPassDesc &desc) {
	return impl->createRenderPass(desc);
}

PipelineHandle Renderer::createPipeline(const PipelineDesc &desc) {
	return impl->createPipeline(desc);
}

RenderTargetHandle Renderer::createRenderTarget(const RenderTargetDesc &desc) {
	return impl->createRenderTarget(desc);
}

SamplerHandle Renderer::createSampler(const SamplerDesc &desc) {
	return impl->createSampler(desc);
}

VertexShaderHandle Renderer::createVertexShader(const std::string &name, const ShaderMacros &macros) {
	return impl->createVertexShader(name, macros);
}

FragmentShaderHandle Renderer::createFragmentShader(const std::string &name, const ShaderMacros &macros) {
	return impl->createFragmentShader(name, macros);
}

TextureHandle Renderer::createTexture(const TextureDesc &desc) {
	return impl->createTexture(desc);
}

DSLayoutHandle Renderer::createDescriptorSetLayout(const DescriptorLayout *layout) {
	return impl->createDescriptorSetLayout(layout);
}

TextureHandle Renderer::getRenderTargetTexture(RenderTargetHandle handle) {
	return impl->getRenderTargetTexture(handle);
}

void Renderer::deleteBuffer(BufferHandle handle) {
	impl->deleteBuffer(handle);
}

void Renderer::deleteFramebuffer(FramebufferHandle handle) {
	impl->deleteFramebuffer(handle);
}

void Renderer::deleteRenderTarget(RenderTargetHandle &rt) {
	impl->deleteRenderTarget(rt);
}

void Renderer::deleteRenderPass(RenderPassHandle handle) {
	impl->deleteRenderPass(handle);
}

void Renderer::deleteSampler(SamplerHandle handle) {
	impl->deleteSampler(handle);
}

void Renderer::deleteTexture(TextureHandle handle) {
	impl->deleteTexture(handle);
}

void Renderer::recreateSwapchain(const SwapchainDesc &desc) {
	impl->recreateSwapchain(desc);
}

void Renderer::beginFrame() {
	impl->beginFrame();
}

void Renderer::presentFrame(RenderTargetHandle image) {
	impl->presentFrame(image);
}

void Renderer::beginRenderPass(RenderPassHandle rpHandle, FramebufferHandle fbHandle) {
	impl->beginRenderPass(rpHandle, fbHandle);
}

void Renderer::endRenderPass() {
	impl->endRenderPass();
}

void Renderer::bindPipeline(PipelineHandle pipeline) {
	impl->bindPipeline(pipeline);
}

void Renderer::bindIndexBuffer(BufferHandle buffer, bool bit16) {
	impl->bindIndexBuffer(buffer, bit16);
}

void Renderer::bindVertexBuffer(unsigned int binding, BufferHandle buffer) {
	impl->bindVertexBuffer(binding, buffer);
}

void Renderer::bindDescriptorSet(unsigned int index, DSLayoutHandle layout, const void *data) {
	impl->bindDescriptorSet(index, layout, data);
}

void Renderer::setViewport(unsigned int x, unsigned int y, unsigned int width, unsigned int height) {
	impl->setViewport(x, y, width, height);
}

void Renderer::setScissorRect(unsigned int x, unsigned int y, unsigned int width, unsigned int height) {
	impl->setScissorRect(x, y, width, height);
}

void Renderer::draw(unsigned int firstVertex, unsigned int vertexCount) {
	impl->draw(firstVertex, vertexCount);
}

void Renderer::drawIndexedInstanced(unsigned int vertexCount, unsigned int instanceCount) {
	impl->drawIndexedInstanced(vertexCount, instanceCount);
}

void Renderer::drawIndexedOffset(unsigned int vertexCount, unsigned int firstIndex) {
	impl->drawIndexedOffset(vertexCount, firstIndex);
}

unsigned int RendererImpl::ringBufferAllocate(unsigned int size, unsigned int alignment) {
	// sub-allocate from persistent coherent buffer
	// round current pointer up to necessary alignment
	const unsigned int add   = alignment - 1;
	const unsigned int mask  = ~add;
	unsigned int alignedPtr  = (ringBufPtr + add) & mask;
	assert(ringBufPtr <= alignedPtr);
	// TODO: ring buffer size should be pow2, se should use add & mask here too
	unsigned int beginPtr    =  alignedPtr % ringBufSize;

	if (beginPtr + size >= ringBufSize) {
		// we went past the end and have to go back to beginning
		// TODO: add and mask here too
		ringBufPtr = (ringBufPtr / ringBufSize + 1) * ringBufSize;
		assert((ringBufPtr & ~mask) == 0);
		alignedPtr  = (ringBufPtr + add) & mask;
		beginPtr    =  alignedPtr % ringBufSize;
		assert(beginPtr + size < ringBufSize);
		assert(beginPtr == 0);
	}
	ringBufPtr = alignedPtr + size;

	return beginPtr;
}

glm::uvec2 Renderer::getDrawableSize() const {
	return glm::uvec2(impl->swapchainDesc.width, impl->swapchainDesc.height);
}
<|endoftext|>


<commit_before>// This file is part of the dune-gdt project:
//   http://users.dune-project.org/projects/dune-gdt
// Copyright holders: Felix Schindler
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

#ifndef DUNE_GDT_TEST_SPACES_CG_PDELAB_HH
#define DUNE_GDT_TEST_SPACES_CG_PDELAB_HH

#include <dune/gdt/spaces/cg/pdelab.hh>

#include "grids.hh"

#if HAVE_DUNE_PDELAB

#define SPACE_CG_PDELAB_SGRID(dd, rr, pp) Spaces::CG::PdelabBased<S##dd##dLeafGridViewType, pp, double, rr>

#define SPACE_CG_PDELAB_YASPGRID(dd, rr, pp) Spaces::CG::PdelabBased<Yasp##dd##dLeafGridViewType, pp, double, rr>

#define SPACES_CG_PDELAB(pp)                                                                                \
  SPACE_CG_PDELAB_SGRID(1, pp, 1)                                                                                \
  , SPACE_CG_PDELAB_SGRID(2, 1, pp), SPACE_CG_PDELAB_SGRID(3, 1, pp), SPACE_CG_PDELAB_YASPGRID(1, pp, 1),              \
      SPACE_CG_PDELAB_YASPGRID(2, 1, pp), SPACE_CG_PDELAB_YASPGRID(3, 1, pp)

#if HAVE_ALUGRID

#define SPACE_CG_PDELAB_ALUCONFORMGRID(dd, rr, pp)                                                                     \
  Spaces::CG::PdelabBased<AluConform##dd##dLeafGridViewType, pp, double, rr>

#define SPACE_CG_PDELAB_ALUCUBEGRID(dd, rr, pp) Spaces::CG::PdelabBased<AluCube##dd##dLeafGridViewType, pp, double, rr>

#define SPACES_CG_PDELAB_ALUGRID(pp)                                                                                \
  SPACE_CG_PDELAB_ALUCONFORMGRID(2, 1, pp)                                                                             \
  , SPACE_CG_PDELAB_ALUCONFORMGRID(3, 1, pp), SPACE_CG_PDELAB_ALUCUBEGRID(2, 1, pp),                                   \
      SPACE_CG_PDELAB_ALUCUBEGRID(3, 1, pp)

#endif // HAVE_ALUGRID
#endif // HAVE_DUNE_PDELAB

#endif // DUNE_GDT_TEST_SPACES_CG_PDELAB_HH
<commit_msg>[test.spaces_cg_pdelab] add missing namespaces<commit_after>// This file is part of the dune-gdt project:
//   http://users.dune-project.org/projects/dune-gdt
// Copyright holders: Felix Schindler
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

#ifndef DUNE_GDT_TEST_SPACES_CG_PDELAB_HH
#define DUNE_GDT_TEST_SPACES_CG_PDELAB_HH

#include <dune/gdt/spaces/cg/pdelab.hh>

#include "grids.hh"

#if HAVE_DUNE_PDELAB

#define SPACE_CG_PDELAB_SGRID(dd, rr, pp) Dune::GDT::Spaces::CG::PdelabBased<S##dd##dLeafGridViewType, pp, double, rr>

#define SPACE_CG_PDELAB_YASPGRID(dd, rr, pp)                                                                           \
  Dune::GDT::Spaces::CG::PdelabBased<Yasp##dd##dLeafGridViewType, pp, double, rr>

#define SPACES_CG_PDELAB(pp)                                                                                \
  SPACE_CG_PDELAB_SGRID(1, pp, 1)                                                                                \
  , SPACE_CG_PDELAB_SGRID(2, 1, pp), SPACE_CG_PDELAB_SGRID(3, 1, pp), SPACE_CG_PDELAB_YASPGRID(1, pp, 1),              \
      SPACE_CG_PDELAB_YASPGRID(2, 1, pp), SPACE_CG_PDELAB_YASPGRID(3, 1, pp)

#if HAVE_ALUGRID

#define SPACE_CG_PDELAB_ALUCONFORMGRID(dd, rr, pp)                                                                     \
  Dune::GDT::Spaces::CG::PdelabBased<AluConform##dd##dLeafGridViewType, pp, double, rr>

#define SPACE_CG_PDELAB_ALUCUBEGRID(dd, rr, pp)                                                                        \
  Dune::GDT::Spaces::CG::PdelabBased<AluCube##dd##dLeafGridViewType, pp, double, rr>

#define SPACES_CG_PDELAB_ALUGRID(pp)                                                                                \
  SPACE_CG_PDELAB_ALUCONFORMGRID(2, 1, pp)                                                                             \
  , SPACE_CG_PDELAB_ALUCONFORMGRID(3, 1, pp), SPACE_CG_PDELAB_ALUCUBEGRID(2, 1, pp),                                   \
      SPACE_CG_PDELAB_ALUCUBEGRID(3, 1, pp)

#endif // HAVE_ALUGRID
#endif // HAVE_DUNE_PDELAB

#endif // DUNE_GDT_TEST_SPACES_CG_PDELAB_HH
<|endoftext|>


<commit_before>/*
    Copyright 2005-2007 Adobe Systems Incorporated
    Distributed under the MIT License (see accompanying file LICENSE_1_0_0.txt
    or a copy at http://stlab.adobe.com/licenses.html)
*/

/****************************************************************************************************/

#define ADOBE_DLL_SAFE 0

// toggle.hpp needs to come before widget_factory to hook the overrides
#include <GG/adobe/future/widgets/headers/platform_toggle.hpp>

#include <GG/adobe/functional.hpp>
#include <GG/adobe/future/widgets/headers/toggle_factory.hpp>
#include <GG/adobe/future/widgets/headers/virtual_machine_extension.hpp>
#include <GG/adobe/future/widgets/headers/widget_factory_registry.hpp>
#include <GG/adobe/future/widgets/headers/widget_factory.hpp>

/****************************************************************************************************/

namespace adobe {

/****************************************************************************************************/

void create_widget(const dictionary_t&  parameters,
                   size_enum_t          size,
                   toggle_t*&           widget)
{
    std::string          alt_text;
    any_regular_t        value_on(true);
    toggle_t::image_type image_on;
    toggle_t::image_type image_off;
    toggle_t::image_type image_disabled;
    theme_t              theme(implementation::size_to_theme(size));

    get_value(parameters, key_alt_text).cast(alt_text);
    get_value(parameters, key_value_on).cast(value_on);
    get_value(parameters, key_image_on).cast(image_on);
    get_value(parameters, key_image_off).cast(image_off);
    get_value(parameters, key_image_disabled).cast(image_disabled);

    widget = new toggle_t(alt_text, value_on, image_on, image_off, image_disabled, theme);
}

/*************************************************************************************************/

void subscribe_view_to_model(toggle_t&               control,
                             name_t                  cell,
                             basic_sheet_t*          layout_sheet,
                             sheet_t*                model_sheet,
                             assemblage_t&           assemblage,
                             visible_change_queue_t& visible_queue)
{
    typedef force_relayout_view_adaptor<toggle_t> adaptor_type;

    adaptor_type* view_adaptor(new adaptor_type(control, visible_queue));

    assemblage.cleanup(boost::bind(delete_ptr<adaptor_type*>(), view_adaptor));

    if (layout_sheet)
    {
        attach_view(assemblage, cell, *view_adaptor, *layout_sheet);
        return;
    }

    attach_view(assemblage, cell, *view_adaptor, *model_sheet);
}

/****************************************************************************************************/

widget_node_t make_toggle(const dictionary_t&     parameters,
                          const widget_node_t&    parent,
                          const factory_token_t&  token,
                          const widget_factory_t& factory)
{
    return create_and_hookup_widget<toggle_t, poly_placeable_t>(parameters, parent, token,
        factory.is_container(static_name_t("toggle")),
        factory.layout_attributes(static_name_t("toggle")));
}

/****************************************************************************************************/

} // namespace adobe

/****************************************************************************************************/
<commit_msg>Fixed the toggle_t overload of create_widget().<commit_after>/*
    Copyright 2005-2007 Adobe Systems Incorporated
    Distributed under the MIT License (see accompanying file LICENSE_1_0_0.txt
    or a copy at http://stlab.adobe.com/licenses.html)
*/

/****************************************************************************************************/

#define ADOBE_DLL_SAFE 0

// toggle.hpp needs to come before widget_factory to hook the overrides
#include <GG/adobe/future/widgets/headers/platform_toggle.hpp>

#include <GG/adobe/functional.hpp>
#include <GG/adobe/future/widgets/headers/toggle_factory.hpp>
#include <GG/adobe/future/widgets/headers/virtual_machine_extension.hpp>
#include <GG/adobe/future/widgets/headers/widget_factory_registry.hpp>
#include <GG/adobe/future/widgets/headers/widget_factory.hpp>

/****************************************************************************************************/

namespace adobe {

/****************************************************************************************************/

void create_widget(const dictionary_t&  parameters,
                   size_enum_t          size,
                   toggle_t*&           widget)
{
    std::string          alt_text;
    any_regular_t        value_on(true);
    toggle_t::image_type image_on;
    toggle_t::image_type image_off;
    toggle_t::image_type image_disabled;
    theme_t              theme(implementation::size_to_theme(size));

    get_value(parameters, key_alt_text, alt_text);
    get_value(parameters, key_value_on, value_on);
    get_value(parameters, key_image_on, image_on);
    get_value(parameters, key_image_off, image_off);
    get_value(parameters, key_image_disabled, image_disabled);

    widget = new toggle_t(alt_text, value_on, image_on, image_off, image_disabled, theme);
}

/*************************************************************************************************/

void subscribe_view_to_model(toggle_t&               control,
                             name_t                  cell,
                             basic_sheet_t*          layout_sheet,
                             sheet_t*                model_sheet,
                             assemblage_t&           assemblage,
                             visible_change_queue_t& visible_queue)
{
    typedef force_relayout_view_adaptor<toggle_t> adaptor_type;

    adaptor_type* view_adaptor(new adaptor_type(control, visible_queue));

    assemblage.cleanup(boost::bind(delete_ptr<adaptor_type*>(), view_adaptor));

    if (layout_sheet)
    {
        attach_view(assemblage, cell, *view_adaptor, *layout_sheet);
        return;
    }

    attach_view(assemblage, cell, *view_adaptor, *model_sheet);
}

/****************************************************************************************************/

widget_node_t make_toggle(const dictionary_t&     parameters,
                          const widget_node_t&    parent,
                          const factory_token_t&  token,
                          const widget_factory_t& factory)
{
    return create_and_hookup_widget<toggle_t, poly_placeable_t>(parameters, parent, token,
        factory.is_container(static_name_t("toggle")),
        factory.layout_attributes(static_name_t("toggle")));
}

/****************************************************************************************************/

} // namespace adobe

/****************************************************************************************************/
<|endoftext|>


<commit_before>#include <FWPlatform.h>
#include <EventLoop.h>
#include <CurlClient.h>
#include <Logger.h>
#include <ContextCairo.h>
#include <SDLSoundCanvas.h>
#include <Message.h>
#include <FWApplication.h>

#include <InitEvent.h>
#include <TouchEvent.h>
#include <DrawEvent.h>
#include <SysEvent.h>
#include <UpdateEvent.h>
#include <ResizeEvent.h>

#include <SDL/SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include <cstdio>
#include <cstdlib>
#include <sys/time.h>

using namespace std;

class PlatformSDL : public FWPlatform {
public:
  PlatformSDL() : FWPlatform(1.0f,
			     // "#version 300 es",
			     "#version 100",
			     true) { }

  double getTime() const override {
    struct timeval tv;
    struct timezone tz;
    int r = gettimeofday(&tv, &tz);
    double t = 0;
    if (r == 0) {
      t = (double)tv.tv_sec + tv.tv_usec / 1000000.0;
    }
    return t;
  }

  string getLocalFilename(const char * fn, FileType type) {
    string s = "assets/";
    return s + fn;
  }

  std::shared_ptr<HTTPClientFactory> createHTTPClientFactory() const {
    return std::make_shared<CurlClientFactory>();
  }

  std::shared_ptr<canvas::ContextFactory> createContextFactory() const {
    return std::shared_ptr<canvas::ContextFactory>(new canvas::CairoContextFactory);
  }

  void sendMessage(const Message & message) override {
    cerr << "sendMessage(" << int(message.getType()) << ")\n";
    FWPlatform::sendMessage(message);
    switch (message.getType()) {
    case Message::SET_CAPTION:
      SDL_WM_SetCaption(message.getTextValue().c_str(),
			message.getTextValue().c_str());
      break;
    case Message::SHOW_MESSAGE_DIALOG:
#if 0
      SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_INFORMATION,
				title.c_str(),
				message.c_str(),
				NULL);
#endif
      break;
    case Message::REQUEST_REDRAW:
      redraw_needed = true;
      break;
    default:
      break;
    }
  }

  std::shared_ptr<SoundCanvas> createSoundCanvas() const override {
    return std::make_shared<SDLSoundCanvas>();
  }

  std::shared_ptr<Logger> createLogger() const override {
    return std::make_shared<BasicLogger>();
  }

  // std::shared_ptr<EventLoop> createEventLoop() override;

  std::string showTextEntryDialog(const std::string & message) {
    return "";
  }

  std::string getBundleFilename(const char * filename) {
    string s = "assets/";
    return s + filename;
  }

  void storeValue(const std::string & key, const std::string & value) {

  }

  std::string loadValue(const std::string & key) {
    return "";
  }

  void swapBuffers() override {
    SDL_GL_SwapBuffers( );
  }

  void run() {
    SDL_Event event;

    cerr << "starting runloop\n";

    while ( true ) {
      /* Grab all the events off the queue. */
      while( SDL_PollEvent( &event ) ) {
	switch( event.type ) {
	case SDL_KEYDOWN:
	  /* Handle key presses. */
	  // handle_key_down( &event.key.keysym );
	  break;
	case SDL_MOUSEBUTTONDOWN:
	  {
	    TouchEvent ev(getTime(), TouchEvent::ACTION_DOWN, mouse_x, mouse_y, 0);
	    postEvent(getActiveViewId(), ev);
	    button_pressed = true;
	  }
	  break;
	case SDL_MOUSEBUTTONUP:
	  {
	    TouchEvent ev(getTime(), TouchEvent::ACTION_UP, mouse_x, mouse_y, 0);
	    postEvent(getActiveViewId(), ev);
	    button_pressed = false;
	  }
	  break;
	case SDL_MOUSEMOTION:
	  if (button_pressed) {
	    TouchEvent ev(getTime(), TouchEvent::ACTION_MOVE, event.motion.x, event.motion.y, 0);
	    postEvent(getActiveViewId(), ev);
	  }
	  mouse_x = event.motion.x;
	  mouse_y = event.motion.y;
	  break;
	case SDL_VIDEORESIZE:
	  {
	    int w = event.resize.w, h = event.resize.h;
	    cerr << "resized (" << w << " " << h << ")\n";
	    setDisplayWidth(w);
	    setDisplayHeight(h);
	    ResizeEvent ev(getTime(), w, h, w, h);
	    postEvent(getActiveViewId(), ev);
	  }
	  break;
	case SDL_QUIT:
	  return;
	}
      }

      getApplication().loadEvents();

      if (!is_initialized) {
	InitEvent ev(getTime());
	postEvent(getActiveViewId(), ev);
	is_initialized = true;
      }

      UpdateEvent ev0(getTime());
      postEvent(getActiveViewId(), ev0);

      if (isRedrawNeeded()) {
	DrawEvent ev(getTime());
	postEvent(getActiveViewId(), ev);
	clearRedrawNeeded();
      }
    }
  }

  bool isRedrawNeeded() { return redraw_needed; }
  void clearRedrawNeeded() { redraw_needed = false; }

private:
  bool button_pressed = false;
  int mouse_x = 0, mouse_y = 0;
  bool redraw_needed = false;
  bool is_initialized = false;
};

#if 0
static void handle_key_down(SDL_keysym* keysym) {

    /*
     * We're only interested if 'Esc' has
     * been presssed.
     *
     * EXERCISE:
     * Handle the arrow keys and have that change the
     * viewing position/angle.
     */
    switch( keysym->sym ) {
    case SDLK_ESCAPE:
        quit_tutorial( 0 );
        break;
    case SDLK_SPACE:
        should_rotate = !should_rotate;
        break;
    default:
        break;
    }

}
#endif

extern FWApplication * applicationMain();

int main(int argc, char *argv[]) {
  if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO ) < 0 ) {
    /* Failed, exit. */
    fprintf( stderr, "Video initialization failed: %s\n",
             SDL_GetError( ) );
    SDL_Quit( );
    exit(1);
  }

  const SDL_VideoInfo * info = SDL_GetVideoInfo( );

  if (!info) {
    /* This should probably never happen. */
    fprintf( stderr, "Video query failed: %s\n", SDL_GetError() );
    SDL_Quit();
    exit(1);
  }

  int width = 800, height = 600;
  int bpp = info->vfmt->BitsPerPixel;

  SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
  SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
  SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
  SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
  SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

  int flags = SDL_OPENGL; // | SDL_FULLSCREEN;

  if (SDL_SetVideoMode( width, height, bpp, flags ) == 0) {
    fprintf( stderr, "Video mode set failed: %s\n",
             SDL_GetError( ) );
    SDL_Quit();
    exit(1);
  }

  FWApplication * application = applicationMain();

  cerr << "starting, app = " << application << "\n";

  PlatformSDL platform;
  platform.setApplication(application);
  platform.getApplication().initialize(&platform);
  platform.getApplication().onCmdLine(argc, argv);

  platform.setDisplayWidth(width);
  platform.setDisplayHeight(height);
  platform.getApplication().initializeContent();

  platform.run();

#if 0
  auto eventloop = platform.createEventLoop();
  eventloop->run();
#endif

  SysEvent ev(platform.getTime(), SysEvent::SHUTDOWN);
  ev.dispatch(platform.getApplication());
  SDL_Quit();
  return 0;
}
<commit_msg>remove debug print<commit_after>#include <FWPlatform.h>
#include <EventLoop.h>
#include <CurlClient.h>
#include <Logger.h>
#include <ContextCairo.h>
#include <SDLSoundCanvas.h>
#include <Message.h>
#include <FWApplication.h>

#include <InitEvent.h>
#include <TouchEvent.h>
#include <DrawEvent.h>
#include <SysEvent.h>
#include <UpdateEvent.h>
#include <ResizeEvent.h>

#include <SDL/SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include <cstdio>
#include <cstdlib>
#include <sys/time.h>

using namespace std;

class PlatformSDL : public FWPlatform {
public:
  PlatformSDL() : FWPlatform(1.0f,
			     // "#version 300 es",
			     "#version 100",
			     true) { }

  double getTime() const override {
    struct timeval tv;
    struct timezone tz;
    int r = gettimeofday(&tv, &tz);
    double t = 0;
    if (r == 0) {
      t = (double)tv.tv_sec + tv.tv_usec / 1000000.0;
    }
    return t;
  }

  string getLocalFilename(const char * fn, FileType type) {
    string s = "assets/";
    return s + fn;
  }

  std::shared_ptr<HTTPClientFactory> createHTTPClientFactory() const {
    return std::make_shared<CurlClientFactory>();
  }

  std::shared_ptr<canvas::ContextFactory> createContextFactory() const {
    return std::shared_ptr<canvas::ContextFactory>(new canvas::CairoContextFactory);
  }

  void sendMessage(const Message & message) override {
    FWPlatform::sendMessage(message);
    switch (message.getType()) {
    case Message::SET_CAPTION:
      SDL_WM_SetCaption(message.getTextValue().c_str(),
			message.getTextValue().c_str());
      break;
    case Message::SHOW_MESSAGE_DIALOG:
#if 0
      SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_INFORMATION,
				title.c_str(),
				message.c_str(),
				NULL);
#endif
      break;
    case Message::REQUEST_REDRAW:
      redraw_needed = true;
      break;
    default:
      break;
    }
  }

  std::shared_ptr<SoundCanvas> createSoundCanvas() const override {
    return std::make_shared<SDLSoundCanvas>();
  }

  std::shared_ptr<Logger> createLogger() const override {
    return std::make_shared<BasicLogger>();
  }

  // std::shared_ptr<EventLoop> createEventLoop() override;

  std::string showTextEntryDialog(const std::string & message) {
    return "";
  }

  std::string getBundleFilename(const char * filename) {
    string s = "assets/";
    return s + filename;
  }

  void storeValue(const std::string & key, const std::string & value) {

  }

  std::string loadValue(const std::string & key) {
    return "";
  }

  void swapBuffers() override {
    SDL_GL_SwapBuffers( );
  }

  void run() {
    SDL_Event event;

    cerr << "starting runloop\n";

    while ( true ) {
      /* Grab all the events off the queue. */
      while( SDL_PollEvent( &event ) ) {
	switch( event.type ) {
	case SDL_KEYDOWN:
	  /* Handle key presses. */
	  // handle_key_down( &event.key.keysym );
	  break;
	case SDL_MOUSEBUTTONDOWN:
	  {
	    TouchEvent ev(getTime(), TouchEvent::ACTION_DOWN, mouse_x, mouse_y, 0);
	    postEvent(getActiveViewId(), ev);
	    button_pressed = true;
	  }
	  break;
	case SDL_MOUSEBUTTONUP:
	  {
	    TouchEvent ev(getTime(), TouchEvent::ACTION_UP, mouse_x, mouse_y, 0);
	    postEvent(getActiveViewId(), ev);
	    button_pressed = false;
	  }
	  break;
	case SDL_MOUSEMOTION:
	  if (button_pressed) {
	    TouchEvent ev(getTime(), TouchEvent::ACTION_MOVE, event.motion.x, event.motion.y, 0);
	    postEvent(getActiveViewId(), ev);
	  }
	  mouse_x = event.motion.x;
	  mouse_y = event.motion.y;
	  break;
	case SDL_VIDEORESIZE:
	  {
	    int w = event.resize.w, h = event.resize.h;
	    cerr << "resized (" << w << " " << h << ")\n";
	    setDisplayWidth(w);
	    setDisplayHeight(h);
	    ResizeEvent ev(getTime(), w, h, w, h);
	    postEvent(getActiveViewId(), ev);
	  }
	  break;
	case SDL_QUIT:
	  return;
	}
      }

      getApplication().loadEvents();

      if (!is_initialized) {
	InitEvent ev(getTime());
	postEvent(getActiveViewId(), ev);
	is_initialized = true;
      }

      UpdateEvent ev0(getTime());
      postEvent(getActiveViewId(), ev0);

      if (isRedrawNeeded()) {
	DrawEvent ev(getTime());
	postEvent(getActiveViewId(), ev);
	clearRedrawNeeded();
      }
    }
  }

  bool isRedrawNeeded() { return redraw_needed; }
  void clearRedrawNeeded() { redraw_needed = false; }

private:
  bool button_pressed = false;
  int mouse_x = 0, mouse_y = 0;
  bool redraw_needed = false;
  bool is_initialized = false;
};

#if 0
static void handle_key_down(SDL_keysym* keysym) {

    /*
     * We're only interested if 'Esc' has
     * been presssed.
     *
     * EXERCISE:
     * Handle the arrow keys and have that change the
     * viewing position/angle.
     */
    switch( keysym->sym ) {
    case SDLK_ESCAPE:
        quit_tutorial( 0 );
        break;
    case SDLK_SPACE:
        should_rotate = !should_rotate;
        break;
    default:
        break;
    }

}
#endif

extern FWApplication * applicationMain();

int main(int argc, char *argv[]) {
  if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO ) < 0 ) {
    /* Failed, exit. */
    fprintf( stderr, "Video initialization failed: %s\n",
             SDL_GetError( ) );
    SDL_Quit( );
    exit(1);
  }

  const SDL_VideoInfo * info = SDL_GetVideoInfo( );

  if (!info) {
    /* This should probably never happen. */
    fprintf( stderr, "Video query failed: %s\n", SDL_GetError() );
    SDL_Quit();
    exit(1);
  }

  int width = 800, height = 600;
  int bpp = info->vfmt->BitsPerPixel;

  SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
  SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
  SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
  SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
  SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

  int flags = SDL_OPENGL; // | SDL_FULLSCREEN;

  if (SDL_SetVideoMode( width, height, bpp, flags ) == 0) {
    fprintf( stderr, "Video mode set failed: %s\n",
             SDL_GetError( ) );
    SDL_Quit();
    exit(1);
  }

  FWApplication * application = applicationMain();

  cerr << "starting, app = " << application << "\n";

  PlatformSDL platform;
  platform.setApplication(application);
  platform.getApplication().initialize(&platform);
  platform.getApplication().onCmdLine(argc, argv);

  platform.setDisplayWidth(width);
  platform.setDisplayHeight(height);
  platform.getApplication().initializeContent();

  platform.run();

#if 0
  auto eventloop = platform.createEventLoop();
  eventloop->run();
#endif

  SysEvent ev(platform.getTime(), SysEvent::SHUTDOWN);
  ev.dispatch(platform.getApplication());
  SDL_Quit();
  return 0;
}
<|endoftext|>


<commit_before>/* Copyright (c) 2016, 2017, 2018, 2019 Dennis Wölfing
 *
 * 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.
 */

/* kernel/src/terminal.cpp
 * Terminal class.
 */

#include <errno.h>
#include <limits.h>
#include <sched.h>
#include <signal.h>
#include <dennix/devctls.h>
#include <dennix/kernel/kernel.h>
#include <dennix/kernel/process.h>
#include <dennix/kernel/signal.h>
#include <dennix/kernel/terminal.h>
#include <dennix/kernel/terminaldisplay.h>

#define CTRL(c) ((c) & 0x1F)

static Terminal _terminal;
Reference<Terminal> terminal(&_terminal);

Terminal::Terminal() : Vnode(S_IFCHR, 0) {
    termio.c_iflag = 0;
    termio.c_oflag = 0;
    termio.c_cflag = 0;
    termio.c_lflag = ECHO | ICANON | ISIG;

    termio.c_cc[VEOF] = CTRL('D');
    termio.c_cc[VEOL] = 0;
    termio.c_cc[VERASE] = '\b';
    termio.c_cc[VINTR] = CTRL('C');
    termio.c_cc[VKILL] = CTRL('U');
    termio.c_cc[VMIN] = 1;
    termio.c_cc[VQUIT] = CTRL('\\');
    termio.c_cc[VSTART] = CTRL('Q');
    termio.c_cc[VSTOP] = CTRL('S');
    termio.c_cc[VSUSP] = CTRL('Z');
    termio.c_cc[VTIME] = 0;

    foregroundGroup = -1;
    numEof = 0;
}

void Terminal::handleCharacter(char c) {
    if (termio.c_lflag & ICANON && c == termio.c_cc[VEOF]) {
        if (terminalBuffer.hasIncompleteLine()) {
            terminalBuffer.endLine();
        } else {
            numEof++;
        }
    } else if (termio.c_lflag & ICANON && c == termio.c_cc[VERASE]) {
        if (terminalBuffer.backspace() && (termio.c_lflag & ECHO)) {
            TerminalDisplay::backspace();
        }
    } else if (termio.c_lflag & ISIG && c == termio.c_cc[VINTR]) {
        raiseSignal(SIGINT);
    } else if (termio.c_lflag & ICANON && c == termio.c_cc[VKILL]) {

    } else if (termio.c_lflag & ISIG && c == termio.c_cc[VQUIT]) {
        raiseSignal(SIGQUIT);
    } else if (/* IXON */ false && c == termio.c_cc[VSTART]) {

    } else if (/* IXON */ false && c == termio.c_cc[VSTOP]) {

    } else if (termio.c_lflag & ISIG && c == termio.c_cc[VSUSP]) {

    } else {
        if (termio.c_lflag & ECHO) {
            TerminalDisplay::printCharacterRaw(c);
        }
        terminalBuffer.write(c);
        if (!(termio.c_lflag & ICANON) || c == '\n' || c == termio.c_cc[VEOL]) {
            terminalBuffer.endLine();
        }
    }
}

void Terminal::handleSequence(const char* sequence) {
    if (!(termio.c_lflag & ICANON)) {
        while (*sequence) {
            if (termio.c_lflag & ECHO) {
                TerminalDisplay::printCharacterRaw(*sequence);
            }
            terminalBuffer.write(*sequence++);
        }
        terminalBuffer.endLine();
    }
}

void Terminal::onKeyboardEvent(int key) {
    char buffer[MB_CUR_MAX];
    size_t bytes = Keyboard::getUtf8FromKey(key, buffer);
    if (bytes != (size_t) -1) {
        for (size_t i = 0; i < bytes; i++) {
            handleCharacter(buffer[i]);
        }
    } else {
        const char* sequence = Keyboard::getSequenceFromKey(key);
        if (sequence) {
            handleSequence(sequence);
        }
    }
    TerminalDisplay::updateCursorPosition();
}

int Terminal::devctl(int command, void* restrict data, size_t size,
        int* restrict info) {
    switch (command) {
    case TIOCGPGRP: {
        if (size != 0 && size != sizeof(pid_t)) {
            *info = -1;
            return EINVAL;
        }

        if (Process::current()->controllingTerminal != this) {
            *info = -1;
            return ENOTTY;
        }

        pid_t* pgid = (pid_t*) data;

        if (foregroundGroup >= 0) {
            *pgid = foregroundGroup;
        } else {
            *pgid = INT_MAX;
        }
        *info = 0;
        return 0;
    } break;
    case TIOCGWINSZ: {
        if (size != 0 && size != sizeof(struct winsize)) {
            *info = -1;
            return EINVAL;
        }

        struct winsize* ws = (struct winsize*) data;
        ws->ws_col = TerminalDisplay::display->width();
        ws->ws_row = TerminalDisplay::display->height();
        *info = 0;
        return 0;
    } break;
    case TIOCSPGRP: {
        if (size != 0 && size != sizeof(pid_t)) {
            *info = -1;
            return EINVAL;
        }

        if (Process::current()->controllingTerminal != this) {
            *info = -1;
            return ENOTTY;
        }

        const pid_t* pgid = (const pid_t*) data;

        if (*pgid < 0) {
            *info = -1;
            return EINVAL;
        }

        if (!Process::getGroup(*pgid)) {
            *info = -1;
            return EPERM;
        }

        // TODO: The terminal should lose its foreground ground when the group
        // dies.
        foregroundGroup = *pgid;

        *info = 0;
        return 0;
    } break;
    default:
        *info = -1;
        return EINVAL;
    }
}

int Terminal::isatty() {
    return 1;
}

void Terminal::raiseSignal(int signal) {
    siginfo_t siginfo = {};
    siginfo.si_signo = signal;
    siginfo.si_code = SI_KERNEL;

    if (foregroundGroup > 0) {
        Process* group = Process::getGroup(foregroundGroup);
        if (group) {
            group->raiseSignalForGroup(siginfo);
        }
    }
}

ssize_t Terminal::read(void* buffer, size_t size) {
    if (size == 0) return 0;
    char* buf = (char*) buffer;
    size_t readSize = 0;

    while (readSize < size) {
        while (!terminalBuffer.available() && !numEof) {
            if (termio.c_lflag & ICANON) {
                if (readSize) {
                    updateTimestamps(true, false, false);
                    return readSize;
                }
            } else {
                if (readSize >= termio.c_cc[VMIN]) {
                    updateTimestamps(true, false, false);
                    return readSize;
                }
            }
            sched_yield();

            if (Signal::isPending()) {
                if (readSize) {
                    updateTimestamps(true, false, false);
                    return readSize;
                }
                errno = EINTR;
                return -1;
            }
        }

        if (numEof) {
            if (readSize) {
                updateTimestamps(true, false, false);
                return readSize;
            }
            numEof--;
            return 0;
        }
        char c = terminalBuffer.read();
        buf[readSize] = c;
        readSize++;
        if ((termio.c_lflag & ICANON) && c == '\n') break;
    }

    updateTimestamps(true, false, false);
    return (ssize_t) readSize;
}

int Terminal::tcgetattr(struct termios* result) {
    *result = termio;
    return 0;
}

int Terminal::tcsetattr(int flags, const struct termios* termio) {
    this->termio = *termio;

    if (flags == TCSAFLUSH) {
        terminalBuffer.reset();
        numEof = 0;
    }
    return 0;
}

ssize_t Terminal::write(const void* buffer, size_t size) {
    if (size == 0) return 0;
    AutoLock lock(&mutex);
    const char* buf = (const char*) buffer;

    for (size_t i = 0; i < size; i++) {
        TerminalDisplay::printCharacter(buf[i]);
    }
    TerminalDisplay::updateCursorPosition();

    updateTimestamps(false, true, true);
    return (ssize_t) size;
}

// TODO: TerminalBuffer reads and writes should be atomic.

TerminalBuffer::TerminalBuffer() {
    reset();
}

size_t TerminalBuffer::available() {
    return lineIndex >= readIndex ?
            lineIndex - readIndex : readIndex - lineIndex;
}

bool TerminalBuffer::backspace() {
    if (lineIndex == writeIndex) return false;

    if (likely(writeIndex != 0)) {
        writeIndex = (writeIndex - 1) % TERMINAL_BUFFER_SIZE;
    } else {
        writeIndex = TERMINAL_BUFFER_SIZE - 1;
    }
    return true;
}

void TerminalBuffer::endLine() {
    lineIndex = writeIndex;
}

bool TerminalBuffer::hasIncompleteLine() {
    return lineIndex != writeIndex;
}

char TerminalBuffer::read() {
    char result = circularBuffer[readIndex];
    readIndex = (readIndex + 1) % TERMINAL_BUFFER_SIZE;
    return result;
}

void TerminalBuffer::reset() {
    readIndex = 0;
    lineIndex = 0;
    writeIndex = 0;
}

void TerminalBuffer::write(char c) {
    while ((writeIndex + 1) % TERMINAL_BUFFER_SIZE == readIndex);
    circularBuffer[writeIndex] = c;
    writeIndex = (writeIndex + 1) % TERMINAL_BUFFER_SIZE;
}
<commit_msg>Handle multibyte characters for backspace.<commit_after>/* Copyright (c) 2016, 2017, 2018, 2019 Dennis Wölfing
 *
 * 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.
 */

/* kernel/src/terminal.cpp
 * Terminal class.
 */

#include <errno.h>
#include <limits.h>
#include <sched.h>
#include <signal.h>
#include <dennix/devctls.h>
#include <dennix/kernel/kernel.h>
#include <dennix/kernel/process.h>
#include <dennix/kernel/signal.h>
#include <dennix/kernel/terminal.h>
#include <dennix/kernel/terminaldisplay.h>

#define CTRL(c) ((c) & 0x1F)

static Terminal _terminal;
Reference<Terminal> terminal(&_terminal);

Terminal::Terminal() : Vnode(S_IFCHR, 0) {
    termio.c_iflag = 0;
    termio.c_oflag = 0;
    termio.c_cflag = 0;
    termio.c_lflag = ECHO | ICANON | ISIG;

    termio.c_cc[VEOF] = CTRL('D');
    termio.c_cc[VEOL] = 0;
    termio.c_cc[VERASE] = '\b';
    termio.c_cc[VINTR] = CTRL('C');
    termio.c_cc[VKILL] = CTRL('U');
    termio.c_cc[VMIN] = 1;
    termio.c_cc[VQUIT] = CTRL('\\');
    termio.c_cc[VSTART] = CTRL('Q');
    termio.c_cc[VSTOP] = CTRL('S');
    termio.c_cc[VSUSP] = CTRL('Z');
    termio.c_cc[VTIME] = 0;

    foregroundGroup = -1;
    numEof = 0;
}

void Terminal::handleCharacter(char c) {
    if (termio.c_lflag & ICANON && c == termio.c_cc[VEOF]) {
        if (terminalBuffer.hasIncompleteLine()) {
            terminalBuffer.endLine();
        } else {
            numEof++;
        }
    } else if (termio.c_lflag & ICANON && c == termio.c_cc[VERASE]) {
        if (terminalBuffer.backspace() && (termio.c_lflag & ECHO)) {
            TerminalDisplay::backspace();
        }
    } else if (termio.c_lflag & ISIG && c == termio.c_cc[VINTR]) {
        raiseSignal(SIGINT);
    } else if (termio.c_lflag & ICANON && c == termio.c_cc[VKILL]) {

    } else if (termio.c_lflag & ISIG && c == termio.c_cc[VQUIT]) {
        raiseSignal(SIGQUIT);
    } else if (/* IXON */ false && c == termio.c_cc[VSTART]) {

    } else if (/* IXON */ false && c == termio.c_cc[VSTOP]) {

    } else if (termio.c_lflag & ISIG && c == termio.c_cc[VSUSP]) {

    } else {
        if (termio.c_lflag & ECHO) {
            TerminalDisplay::printCharacterRaw(c);
        }
        terminalBuffer.write(c);
        if (!(termio.c_lflag & ICANON) || c == '\n' || c == termio.c_cc[VEOL]) {
            terminalBuffer.endLine();
        }
    }
}

void Terminal::handleSequence(const char* sequence) {
    if (!(termio.c_lflag & ICANON)) {
        while (*sequence) {
            if (termio.c_lflag & ECHO) {
                TerminalDisplay::printCharacterRaw(*sequence);
            }
            terminalBuffer.write(*sequence++);
        }
        terminalBuffer.endLine();
    }
}

void Terminal::onKeyboardEvent(int key) {
    char buffer[MB_CUR_MAX];
    size_t bytes = Keyboard::getUtf8FromKey(key, buffer);
    if (bytes != (size_t) -1) {
        for (size_t i = 0; i < bytes; i++) {
            handleCharacter(buffer[i]);
        }
    } else {
        const char* sequence = Keyboard::getSequenceFromKey(key);
        if (sequence) {
            handleSequence(sequence);
        }
    }
    TerminalDisplay::updateCursorPosition();
}

int Terminal::devctl(int command, void* restrict data, size_t size,
        int* restrict info) {
    switch (command) {
    case TIOCGPGRP: {
        if (size != 0 && size != sizeof(pid_t)) {
            *info = -1;
            return EINVAL;
        }

        if (Process::current()->controllingTerminal != this) {
            *info = -1;
            return ENOTTY;
        }

        pid_t* pgid = (pid_t*) data;

        if (foregroundGroup >= 0) {
            *pgid = foregroundGroup;
        } else {
            *pgid = INT_MAX;
        }
        *info = 0;
        return 0;
    } break;
    case TIOCGWINSZ: {
        if (size != 0 && size != sizeof(struct winsize)) {
            *info = -1;
            return EINVAL;
        }

        struct winsize* ws = (struct winsize*) data;
        ws->ws_col = TerminalDisplay::display->width();
        ws->ws_row = TerminalDisplay::display->height();
        *info = 0;
        return 0;
    } break;
    case TIOCSPGRP: {
        if (size != 0 && size != sizeof(pid_t)) {
            *info = -1;
            return EINVAL;
        }

        if (Process::current()->controllingTerminal != this) {
            *info = -1;
            return ENOTTY;
        }

        const pid_t* pgid = (const pid_t*) data;

        if (*pgid < 0) {
            *info = -1;
            return EINVAL;
        }

        if (!Process::getGroup(*pgid)) {
            *info = -1;
            return EPERM;
        }

        // TODO: The terminal should lose its foreground ground when the group
        // dies.
        foregroundGroup = *pgid;

        *info = 0;
        return 0;
    } break;
    default:
        *info = -1;
        return EINVAL;
    }
}

int Terminal::isatty() {
    return 1;
}

void Terminal::raiseSignal(int signal) {
    siginfo_t siginfo = {};
    siginfo.si_signo = signal;
    siginfo.si_code = SI_KERNEL;

    if (foregroundGroup > 0) {
        Process* group = Process::getGroup(foregroundGroup);
        if (group) {
            group->raiseSignalForGroup(siginfo);
        }
    }
}

ssize_t Terminal::read(void* buffer, size_t size) {
    if (size == 0) return 0;
    char* buf = (char*) buffer;
    size_t readSize = 0;

    while (readSize < size) {
        while (!terminalBuffer.available() && !numEof) {
            if (termio.c_lflag & ICANON) {
                if (readSize) {
                    updateTimestamps(true, false, false);
                    return readSize;
                }
            } else {
                if (readSize >= termio.c_cc[VMIN]) {
                    updateTimestamps(true, false, false);
                    return readSize;
                }
            }
            sched_yield();

            if (Signal::isPending()) {
                if (readSize) {
                    updateTimestamps(true, false, false);
                    return readSize;
                }
                errno = EINTR;
                return -1;
            }
        }

        if (numEof) {
            if (readSize) {
                updateTimestamps(true, false, false);
                return readSize;
            }
            numEof--;
            return 0;
        }
        char c = terminalBuffer.read();
        buf[readSize] = c;
        readSize++;
        if ((termio.c_lflag & ICANON) && c == '\n') break;
    }

    updateTimestamps(true, false, false);
    return (ssize_t) readSize;
}

int Terminal::tcgetattr(struct termios* result) {
    *result = termio;
    return 0;
}

int Terminal::tcsetattr(int flags, const struct termios* termio) {
    this->termio = *termio;

    if (flags == TCSAFLUSH) {
        terminalBuffer.reset();
        numEof = 0;
    }
    return 0;
}

ssize_t Terminal::write(const void* buffer, size_t size) {
    if (size == 0) return 0;
    AutoLock lock(&mutex);
    const char* buf = (const char*) buffer;

    for (size_t i = 0; i < size; i++) {
        TerminalDisplay::printCharacter(buf[i]);
    }
    TerminalDisplay::updateCursorPosition();

    updateTimestamps(false, true, true);
    return (ssize_t) size;
}

// TODO: TerminalBuffer reads and writes should be atomic.

TerminalBuffer::TerminalBuffer() {
    reset();
}

size_t TerminalBuffer::available() {
    return lineIndex >= readIndex ?
            lineIndex - readIndex : readIndex - lineIndex;
}

bool TerminalBuffer::backspace() {
    if (lineIndex == writeIndex) return false;
    bool continuationByte;
    do {
        continuationByte = (circularBuffer[writeIndex - 1] & 0xC0) == 0x80;
        if (likely(writeIndex != 0)) {
            writeIndex = (writeIndex - 1) % TERMINAL_BUFFER_SIZE;
        } else {
            writeIndex = TERMINAL_BUFFER_SIZE - 1;
        }
    } while (continuationByte && lineIndex != writeIndex);

    return true;
}

void TerminalBuffer::endLine() {
    lineIndex = writeIndex;
}

bool TerminalBuffer::hasIncompleteLine() {
    return lineIndex != writeIndex;
}

char TerminalBuffer::read() {
    char result = circularBuffer[readIndex];
    readIndex = (readIndex + 1) % TERMINAL_BUFFER_SIZE;
    return result;
}

void TerminalBuffer::reset() {
    readIndex = 0;
    lineIndex = 0;
    writeIndex = 0;
}

void TerminalBuffer::write(char c) {
    while ((writeIndex + 1) % TERMINAL_BUFFER_SIZE == readIndex);
    circularBuffer[writeIndex] = c;
    writeIndex = (writeIndex + 1) % TERMINAL_BUFFER_SIZE;
}
<|endoftext|>